Функция, которая принимает только целые литералы
Я ищу способ моделирования некоторых перегруженных встроенных модулей GCC на С++. Встроенные модули схожи с ними:
__builtin_foo(char *a, signed int b);
__builtin_foo(short *a, signed int b);
__builtin_foo(long *a, signed int b);
С особым ограничением, жестко закодированным в GCC: b
должно быть буквальным значением, то есть вы можете вызвать:
__builtin_foo((char *)0, 1);
но не:
extern int val;
__builtin_foo((char *)0, val);
который генерирует ошибку компилятора. Я искал с помощью std::enable_if
, чтобы имитировать это, но не могу найти способ обеспечить, чтобы принимались только литералы. Есть ли способ сделать это?
Ответы
Ответ 1
Вы можете сортировать его с помощью макроса и встроенного GCC __builtin_constant_p
constexpr int foo(int i) { return i; }
#define FOO(i) do { \
static_assert(__builtin_constant_p(i), "Not a constant"); \
foo(i); \
} while (false)
Это позволит FOO(1)
скомпилировать, но не int i = 1; FOO(i);
Однако результат __builtin_constant_p
зависит от уровня оптимизации, при более высоких уровнях оптимизации const
переменные рассматриваются как константы, поэтому он принимает не только литералы.
Конечно, если вы готовы разрешать постоянные выражения, а не только литералы, то все, что вам нужно сделать, это использовать переменную в контексте, который требует постоянного выражения, например статического утверждения или не-типа аргумент шаблона.
Ответ 2
Вот портативное решение на С++ 11, так что ваша функция (макрос фактически, извините) принимает только целые литералы и вместо этого запускает ошибку времени компиляции:
constexpr int operator "" _literal(unsigned long long i)
{
return i;
}
#define m_builtin_foo(integer) builtin_foo(integer ## _literal)
Пользовательские литералы принимают только литералы (отсюда и их название). Поэтому, если вы сделаете свой макрос вставляемым пользователем литералом для того, что ему передается, он должен принимать только литералы, принятые соответствующим пользовательским литералом.
Однако, это довольно уродливо, и я думаю, что это может быть подвержено ошибкам. Не знаю, как это сделать.
@Майкл Андерсон указал на комментарии, что m_builtin_foo(i+1)
будет работать. Это так. @Angew предложила обернуть возвращаемый тип _literal
в оболочку, несовместимую с целочисленной арифметикой и добавив явные преобразования. Он был прав, вот более полное решение:
struct wrapper
{
constexpr wrapper(int n):
value{n}
{}
explicit constexpr operator int() const
{
return value;
}
int value;
};
constexpr wrapper operator "" _literal(unsigned long long i)
{
return { int(i) };
}
#define m_builtin_foo(integer) builtin_foo(int(integer ## _literal))
Ответ 3
Вы можете превратить функцию в шаблон:
template <int b>
builtin_foo(char *a);
Тогда синтаксис вызова будет builtin_foo<1>(whatever)
.
Если вам это не нравится, вы можете обернуть шаблон в макрос:
#define m_builtin_foo(a, b) builtin_foo<(b)>((a))
Это, конечно, будет принимать любое интегральное постоянное выражение, а не только литерал.