Странная проблема с constexpr и __FUNCTION__

Тема в разделе "LANGS.C", создана пользователем Rel, 15 авг 2011.

  1. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.321
    сделал удобную систему логирования, которая при возникновении ошибок выдает стек вызовов функций... в стеке в режиме дебага лежат строки с названием функций (__FUNCTION__), в режиме релиза лежат хеш-значения имен функций (для уменьшения размера релиза)... написал реализацию хеш-функции времени компиляции в виде constexpr-функции, но столкнулся с неожиданной проблемой... вот код:
    Код (Text):
    1. // Константификация значения хеша, тк бывает, что для constexpr
    2. // компилятор все же генерирует код времени выполнения
    3. template <uint32_t Const> struct Constantify { enum { Value = Const }; };
    4.  
    5. // Хеш-функция времени компиляции
    6. constexpr uint32_t Strhash(const char* Str) { /* реализация хеш-функции */ };
    7.  
    8. // Где-то в коде
    9. uint32_t hash1 = Constantify<Strhash("Test!")>::Value; // Нормально вычисляет хеш во времени компиляции
    10. uint32_t hash2 = Constantify<Strhash(__FUNCTION__)>::Value; // Ошибка компиляции
    сообщения ошибки:
    Код (Text):
    1. /main.cpp:6:76:   in constexpr expansion of «Strhash(((const char*)(& __FUNCTION__)))»
    2. /main.cpp:6:77: ошибка: the value of «__FUNCTION__» is not usable in a constant expression
    3. /main.cpp:6:64: замечание: «__FUNCTION__» was not declared «constexpr»
    4. /main.cpp:6:77: замечание: in template argument for type «unsigned int»
    вообще мне это кажется странным, тк я не могу понять, чем с точки зрения компилятора __FUNCTION__ отличается от обычной константной строки... как пофиксить?

    ЗЫ gcc 4.6.1 если что... ;)
     
  2. shchetinin

    shchetinin Member

    Публикаций:
    0
    Регистрация:
    27 май 2011
    Сообщения:
    715
    Rel
    Где именно в коде? это хоть в контексте функции? :)
     
  3. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.321
    само собой в контексте функции...
     
  4. shchetinin

    shchetinin Member

    Публикаций:
    0
    Регистрация:
    27 май 2011
    Сообщения:
    715
    Может быть
    Код (Text):
    1. const char * lpStr  = __FUNCTION__;
    2. uint32_t hash2 = Constantify<Strhash(lpStr)>::Value;
    Компилятора нет ...
     
  5. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.321
    так не получится по все той же причине, очевидной компилятору, но не мне:
    Код (Text):
    1. ошибка: the value of «lpStr» is not usable in a constant expression
     
  6. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    shchetinin

    Ну уж точно не так, т.к. это неконстантный указатель на константу. Попробуйте так:

    Код (Text):
    1. char const* const lpStr = __FUNCTION__;
    2. uint32_t hash2 = Constantify<Strhash(lpStr)>::Value;
     
  7. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.321
    нет, та же самая ошибка компиляции:
    Код (Text):
    1. ошибка: the value of «__FUNCTION__» is not usable in a constant expression
    я не понимаю, почему __FUNCTION__ не может быть использовано, как константное выражение...
     
  8. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Rel

    Попробуй сделать чтобы сама constexpr принимала не const char*, а выводила бы массив:

    Код (Text):
    1. template <int size>
    2. constexpr uint32_t Strhash(const char (&Str)[size]) { ... }
     
  9. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.321
    ну вообще при такой постановке задачи работает, но дело в том, что вычисление хеша происходит рекурсивно посимвольно... и я не пойму, как при такой постановке мне вызвать Strhash к следующему символу, например подсчет суммы значений символов в моем варианте был бы такой:
    Код (Text):
    1.  constexpr uint32_t CountSum(const char* Str) { return (*Str) ? (*Str + CountSum(Str + 1)) : (0); }
    а как реализовать рекурсию с массивом в качестве параметра?
     
  10. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Rel

    Очевидно же:

    Код (Text):
    1. template <int size>
    2. constexpr uint32_t CountSum(const char (&Str)[size], int n = 0)
    3. {
    4.     return n == size ? 0 : (Str[n] + CountSum(Str, n + 1));
    5. }
    EDIT: фикс имен.
     
  11. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    _DEN_
    в constexpr функциях запрещена рекурсия. надо использовать разные шаблонные перегрузки
    http://forum.vingrad.ru/index.php?showtopic=322361&view=findpost&p=2299543
     
  12. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    GoldFinch

    Удалено. Думаю... :)

    EDIT: Не, без нормального знания стандарта не могу сообразить :) А что, в C++09 нет какого-нибудь constexpr-ного for_each?
     
  13. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.321
    чего-чего? вообще constexpr имеет хоть какую-то ценность только благодаря рекурсии))) такой код собирается нормально и выдает ожидаемый результат:
    Код (Text):
    1. template <uint32_t N> constexpr uint32_t CountSum(const char (&Str)[N], uint32_t t = 0) { return Str[t] ? (Str[t] + CountSum(Str, t + 1)) : (0); }
    но, извиняйте... я в первый раз плохо проверил))) с __FUNCTION__ в качестве параметра выдает ту же самую ошибку...
     
  14. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Rel
    C++ - это такой язык, в котором вышеуказаный факт ни о чем не говорит.

    Скорее всего это значит, что компилятор не рассматривает __FUNCTION__ как литерную строку - для него это рантаймовое значение. Если это так, то тут будет баттхерт - с этим ничего не поделать.
     
  15. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    у меня нет FDIS, однако в http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf написано
    если что-то поменялось - я только рад
     
  16. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Хреново то, что рекурсии через численные шаблонные параметры тут не катят - частичная специализация шаблонных функций запрещена, и как останавливать такие рекурсии - непонятно.
     
  17. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    _DEN_
    заворачивать функцию в класс, как еще
     
  18. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    можно использовать как параметр шаблона константную строку с внешним связыванием. фактически будет использоваться её адрес. __FUNCTION__ явно не подходит
     
  19. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    GoldFinch
    Макрос + decltype + auto + значение?
     
  20. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    Код (Text):
    1. #include <iostream>
    2.  
    3. template <int X, int Y>
    4. struct comb
    5. {
    6.   static const int value = Y * comb<X, Y - 1>::value;
    7. };
    8.  
    9. template <int Y>
    10. struct comb<Y, Y>
    11. {
    12.   static const int value = Y;
    13. };
    14.  
    15. template <int X, int Y>
    16. int get_comb()
    17. {
    18.   assert(Y >= X);
    19.   return comb<X, Y>::value;
    20. }
    21.  
    22. int main()
    23. {
    24.   std::cout << get_comb<1, 5>() << std::endl;
    25. }