Странная проблема с constexpr и __FUNCTION__
Тема в разделе «LANGS.C», создана пользователем Rel, 15 авг 2011.
Страница 1 из 2
-
Rel
Well-Known Member
- Публикаций:
-
2
- Регистрация:
- 11 дек 2008
- Сообщения:
- 4.896
сделал удобную систему логирования, которая при возникновении ошибок выдает стек вызовов функций… в стеке в режиме дебага лежат строки с названием функций (__FUNCTION__), в режиме релиза лежат хеш-значения имен функций (для уменьшения размера релиза)… написал реализацию хеш-функции времени компиляции в виде constexpr-функции, но столкнулся с неожиданной проблемой… вот код:
-
// Константификация значения хеша, тк бывает, что для constexpr
-
// компилятор все же генерирует код времени выполнения
-
template <uint32_t Const> struct Constantify { enum { Value = Const }; };
-
// Хеш-функция времени компиляции
-
constexpr uint32_t Strhash(const char* Str) { /* реализация хеш-функции */ };
-
uint32_t hash1 = Constantify<Strhash(«Test!»)>::Value; // Нормально вычисляет хеш во времени компиляции
-
uint32_t hash2 = Constantify<Strhash(__FUNCTION__)>::Value; // Ошибка компиляции
сообщения ошибки:
-
/main.cpp:6:76: in constexpr expansion of «Strhash(((const char*)(& __FUNCTION__)))»
-
/main.cpp:6:77: ошибка: the value of «__FUNCTION__» is not usable in a constant expression
-
/main.cpp:6:64: замечание: «__FUNCTION__» was not declared «constexpr»
-
/main.cpp:6:77: замечание: in template argument for type «unsigned int»
вообще мне это кажется странным, тк я не могу понять, чем с точки зрения компилятора __FUNCTION__ отличается от обычной константной строки… как пофиксить?
ЗЫ gcc 4.6.1 если что…
-
shchetinin
Member
- Публикаций:
-
0
- Регистрация:
- 27 май 2011
- Сообщения:
- 715
Rel
Где именно в коде? это хоть в контексте функции?
-
Rel
Well-Known Member
- Публикаций:
-
2
- Регистрация:
- 11 дек 2008
- Сообщения:
- 4.896
само собой в контексте функции…
-
shchetinin
Member
- Публикаций:
-
0
- Регистрация:
- 27 май 2011
- Сообщения:
- 715
Может быть
-
const char * lpStr = __FUNCTION__;
-
uint32_t hash2 = Constantify<Strhash(lpStr)>::Value;
Компилятора нет …
-
Rel
Well-Known Member
- Публикаций:
-
2
- Регистрация:
- 11 дек 2008
- Сообщения:
- 4.896
так не получится по все той же причине, очевидной компилятору, но не мне:
-
ошибка: the value of «lpStr» is not usable in a constant expression
-
shchetininНу уж точно не так, т.к. это неконстантный указатель на константу. Попробуйте так:
-
char const* const lpStr = __FUNCTION__;
-
uint32_t hash2 = Constantify<Strhash(lpStr)>::Value;
-
-
Rel
Well-Known Member
- Публикаций:
-
2
- Регистрация:
- 11 дек 2008
- Сообщения:
- 4.896
нет, та же самая ошибка компиляции:
-
ошибка: the value of «__FUNCTION__» is not usable in a constant expression
я не понимаю, почему __FUNCTION__ не может быть использовано, как константное выражение…
-
Rel
Попробуй сделать чтобы сама constexpr принимала не const char*, а выводила бы массив:
-
constexpr uint32_t Strhash(const char (&Str)[size]) { … }
-
-
Rel
Well-Known Member
- Публикаций:
-
2
- Регистрация:
- 11 дек 2008
- Сообщения:
- 4.896
ну вообще при такой постановке задачи работает, но дело в том, что вычисление хеша происходит рекурсивно посимвольно… и я не пойму, как при такой постановке мне вызвать Strhash к следующему символу, например подсчет суммы значений символов в моем варианте был бы такой:
-
constexpr uint32_t CountSum(const char* Str) { return (*Str) ? (*Str + CountSum(Str + 1)) : (0); }
а как реализовать рекурсию с массивом в качестве параметра?
-
Rel
Очевидно же:
-
constexpr uint32_t CountSum(const char (&Str)[size], int n = 0)
-
return n == size ? 0 : (Str[n] + CountSum(Str, n + 1));
EDIT: фикс имен.
-
-
GoldFinch
New Member
- Публикаций:
-
0
- Регистрация:
- 29 мар 2008
- Сообщения:
- 1.775
_DEN_
в constexpr функциях запрещена рекурсия. надо использовать разные шаблонные перегрузки
http://forum.vingrad.ru/index.php?showtopic=322361&view=findpost&p=2299543 -
GoldFinch
Удалено. Думаю…
EDIT: Не, без нормального знания стандарта не могу сообразить
А что, в C++09 нет какого-нибудь constexpr-ного for_each?
-
Rel
Well-Known Member
- Публикаций:
-
2
- Регистрация:
- 11 дек 2008
- Сообщения:
- 4.896
чего-чего? вообще constexpr имеет хоть какую-то ценность только благодаря рекурсии))) такой код собирается нормально и выдает ожидаемый результат:
-
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__ в качестве параметра выдает ту же самую ошибку…
-
Rel
C++ — это такой язык, в котором вышеуказаный факт ни о чем не говорит.Скорее всего это значит, что компилятор не рассматривает __FUNCTION__ как литерную строку — для него это рантаймовое значение. Если это так, то тут будет баттхерт — с этим ничего не поделать.
-
GoldFinch
New Member
- Публикаций:
-
0
- Регистрация:
- 29 мар 2008
- Сообщения:
- 1.775
у меня нет FDIS, однако в http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf написано
если что-то поменялось — я только рад
-
Хреново то, что рекурсии через численные шаблонные параметры тут не катят — частичная специализация шаблонных функций запрещена, и как останавливать такие рекурсии — непонятно.
-
GoldFinch
New Member
- Публикаций:
-
0
- Регистрация:
- 29 мар 2008
- Сообщения:
- 1.775
_DEN_
заворачивать функцию в класс, как еще -
sergegers
New Member
- Публикаций:
-
0
- Регистрация:
- 8 июн 2008
- Сообщения:
- 172
можно использовать как параметр шаблона константную строку с внешним связыванием. фактически будет использоваться её адрес. __FUNCTION__ явно не подходит
-
GoldFinch
Макрос + decltype + auto + значение?
-
sergegers
New Member
- Публикаций:
-
0
- Регистрация:
- 8 июн 2008
- Сообщения:
- 172
-
static const int value = Y * comb<X, Y — 1>::value;
-
static const int value = Y;
-
return comb<X, Y>::value;
-
std::cout << get_comb<1, 5>() << std::endl;
Страница 1 из 2
Anton9900 0 / 0 / 0 Регистрация: 18.10.2021 Сообщений: 56 |
||||||||
1 |
||||||||
20.01.2023, 17:26. Показов 176. Ответов 4 Метки нет (Все метки)
Как я понимаю как аргументы шаблонов надо использовать значения, которые будут известны уже во время компиляции(поправьте если я не прав). А если мне надо чтобы пользователь сам определял размер — как мне этого добиться?
Заранее спасибо
__________________
0 |
3986 / 3255 / 910 Регистрация: 25.03.2012 Сообщений: 12,102 Записей в блоге: 1 |
|
20.01.2023, 17:28 |
2 |
Как я понимаю как аргументы шаблонов надо использовать значения, которые будут известны уже во время компиляции(поправьте если я не прав). А если мне надо чтобы пользователь сам определял размер — как мне этого добиться? написать класс без шаблонов. Добавлено через 1 минуту судя по всему, это не библиотека. Этот код ты пишешь? Ну вот и пиши без шаблонов и Arrayев
0 |
0 / 0 / 0 Регистрация: 18.10.2021 Сообщений: 56 |
|
20.01.2023, 17:29 [ТС] |
3 |
написать класс без шаблонов. Я думал есть какое-то иное решение… Мир жесток
0 |
фрилансер 4478 / 3988 / 870 Регистрация: 11.10.2019 Сообщений: 10,503 |
|
20.01.2023, 17:53 |
4 |
Anton9900,
0 |
Вездепух 10435 / 5704 / 1553 Регистрация: 18.10.2014 Сообщений: 14,101 |
|
20.01.2023, 19:19 |
5 |
А если мне надо чтобы пользователь сам определял размер — как мне этого добиться? Есть. Включаете запись пения птиц и журчания воды, садитесь в позу лотоса, расслабляетесь и спокойным тихим голосом повторяете себе под нос «Мне не надо, чтобы пользователь сам определял размер. Пусть этого добивается кто-то другой»
0 |
Google ref: b/11479502 /// --- cut --- template <const char name[]> class BaseObject { virtual const char* GetName() const { return name; } }; const char kName[] = "name"; class Object : public BaseObject<kName> { }; int main() { return 0; } /// --- cut --- Using g++ (GCC) 4.9.0 20131028 (experimental) g++ -c t.cc t.cc:9:34: error: 'kName' cannot appear in a constant-expression class Object : public BaseObject<kName> { ^ t.cc:9:39: error: template argument 1 is invalid class Object : public BaseObject<kName> { ^ g++ -c t.cc -std=c++11 t.cc:9:34: error: the value of 'kName' is not usable in a constant expression class Object : public BaseObject<kName> { ^ t.cc:8:12: note: 'kName' was not declared 'constexpr' const char kName[] = "name"; ^ Compiles with Clang. Analysis by Richard Smith: This is a GCC bug. It appears to be some sort of confusion in the way GCC handles internal linkage non-type template parameters of pointer type. You can also get GCC to accept the code by marking kName as 'extern'. Obligatory standards references (relative to N3797): [temp.param](14.1)/8: "A non-type template-parameter of type "array of T" [...] is adjusted to be of type "pointer to T". [temp.arg.nontype](14.3.2)/1: "A template-argument for a non-type, non-template template-parameter shall be [...] a constant expression that designates the address of a complete object with static storage duration and external or internal linkage [...] expressed [...] as & id-expression, where the id-expression is the name of an object [...], except that the & may be omitted if the name refers to a[n] [...] array" Note that "kName" is a constant expression, because it is a glvalue core constant expression whose value refers to an object with static storage duration (see [expr.const](5.19)/4). [temp.arg.nontype](14.3.2)/5: "The following conversions are performed on each expression used as a non-type template-argument. [...] For a non-type template-parameter of type pointer to object, [...] the array-to-pointer conversion [is] applied." So BaseObject has a template parameter of type 'const char*', and that parameter can bind to the template argument kName (after array-to-pointer decay). There's no requirement that kName be declared 'constexpr'; its value is not used here, only its address is used.
Hmm, issue seems to be in too restrictive decl_maybe_constant_var_p function. We could allow here additional ARRAY_TYPEs with constant, non-vla, and trivial destructors. Maybe even non-trivial destructors could be ok. Not sure Suggested patch, which allows provided testcase to run is: Index: decl2.c =================================================================== --- decl2.c (Revision 218570) +++ decl2.c (Arbeitskopie) @@ -4157,8 +4157,12 @@ decl_maybe_constant_var_p (tree decl) return false; if (DECL_DECLARED_CONSTEXPR_P (decl)) return true; - return (CP_TYPE_CONST_NON_VOLATILE_P (type) - && INTEGRAL_OR_ENUMERATION_TYPE_P (type)); + if (!CP_TYPE_CONST_NON_VOLATILE_P (type)) + return false; + return ((TREE_CODE (type) == ARRAY_TYPE + && !TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (type)) + && !array_of_runtime_bound_p (type)) + || INTEGRAL_OR_ENUMERATION_TYPE_P (type)); } /* Complain that DECL uses a type with no linkage. In C++98 mode this is
(In reply to Kai Tietz from comment #1)
> Hmm, issue seems to be in too restrictive decl_maybe_constant_var_p function.
I don't know how the GCC code is structured, but I don't think that's right; that function appears to be checking whether the value of the variable can be used in a constant expression. The relevant condition here is whether the address of the variable can be used.
(In reply to Richard Smith from comment #2)
> (In reply to Kai Tietz from comment #1)
> > Hmm, issue seems to be in too restrictive decl_maybe_constant_var_p function.
>
> I don't know how the GCC code is structured, but I don't think that's right;
> that function appears to be checking whether the value of the variable can
> be used in a constant expression. The relevant condition here is whether the
> address of the variable can be used.
hmm, this function has nothing to do with its value. AFAIU the comment (and its use) it just checks that a VAR-decl might be a constant.
In general it checks for const and volatile attributes, and assumes for integral/enumeral typed variables that variable is constant.
So a 'const char *' isn't constant - as just the destination the variable is pointing to is constant, but not the variable itself. For a constant array with trivial destructor, and non-vla size this is different. The array's name is indeed a constant address, and its content is constant too. Of course the a variable pointing to into an array isn't constant, but the array itself is.
Anyway I might be wrong here
This was fixed by r249079 on trunk and r249325 for 7.2 Fix array decay handling in constant expressions. * parser.c (cp_parser_constant_expression): Check potential_rvalue_constant_expression after decay_conversion. * pt.c (convert_nontype_argument): Don't require linkage in C++17. |