Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

function that takes only literal integers

Tags:

I'm looking for a way to simulate certain overloaded GCC built-ins in C++. The built-ins are similar to these:

__builtin_foo(char *a, signed int b); __builtin_foo(short *a, signed int b); __builtin_foo(long *a, signed int b); 

With a special limitation hard coded in GCC: b must be a literal value, i.e. you can call:

__builtin_foo((char *)0, 1); 

but not:

extern int val; __builtin_foo((char *)0, val); 

which generates a compiler error. I've fiddled with std::enable_if to simulate this but cannot find a way to enforce that only literal arguments are accepted. Is there a way to do this?

like image 822
user3510167 Avatar asked Mar 12 '15 11:03

user3510167


People also ask

Which is an integer literal?

Integer literals are numbers that do not have a decimal point or an exponential part. They can be represented as: Decimal integer literals. Hexadecimal integer literals.

What is integer literal in Python?

In computer science, an integer literal is a kind of literal for an integer whose value is directly represented in source code.


2 Answers

Here is a portable C++11 solution so that your function (macro actually, sorry) accepts only integer literals and triggers a compile-time error instead:

constexpr int operator "" _literal(unsigned long long i) {     return i; }  #define m_builtin_foo(integer) builtin_foo(integer ## _literal) 

User-defined literals accept only literals (hence their name). Therefore, if you make your macro paste a user-defined literal to what is passed to it, it should only accept the literals accepted by the corresponding user-defined literal.

However, that's rather ugly and I guess that could be error-prone. Not sure how though.


@MichaelAnderson pointed in the comments that m_builtin_foo(i+1) would still work. That's right. @Angew suggested wrapping the return type of _literal in a wrapper incompatible with integer arithmetic and adding explicit conversions. He was also right, here is the more complete solution:

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)) 
like image 67
Morwenn Avatar answered Jan 12 '23 02:01

Morwenn


You can sort of simulate it using a macro and the GCC intrinsic __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) 

This will allow FOO(1) to compile, but not int i = 1; FOO(i);

However, the result of __builtin_constant_p depends on the optimisation level, at higher levels of optimisation const variables are treated as constants, so it doesn't only accept literals.

Of course if you're willing to allow constant-expressions, not just literals, then all you have to do is use the variable in a context that requires a constant-expression, such as a static assertion or a non-type template argument.

like image 25
Jonathan Wakely Avatar answered Jan 12 '23 03:01

Jonathan Wakely