I was looking up compile constant detection and discovered this gem:
struct chkconst {
struct Small { char a; };
struct Big : Small { char b; };
struct Temp { Temp(int x) {} };
static Small chk2(void*);// { return Small(); }
static Big chk2(Temp);// { return Big(); }
};
// There are three major variants I found shown here:
#if CHECK==0
# define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(chkconst::Big))
# define is_const_pos(X) is_const_0( int(X)^(int(X)&INT_MAX) )
# define is_const(X) (is_const_pos(X)|is_const_pos(-int(X))|is_const_pos(-(int(X)+1)))
#elif CHECK==1
# define is_const_0(X) (sizeof(chkconst::chk2(0+!!(X))) != sizeof(chkconst::chk2(0+!(X))))
# define is_const(X) is_const_0( int(X) )
#else
// this one works with const arrays and function parameters in g++
# define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(chkconst::Big))
# define is_const(X) is_const_0( int(X)-int(X) )
#endif
This allows the detection if a value is handled as a constant in the compiler upon testing by using the is_const()
macro and does this by detecting if a constant can be evaluated to zero or not at compile time. If it is zero, then the compiler will choose the function that takes a pointer over the function that takes an int
, and with that, determine the size of the return value is that of the one taking the pointer, which is very cool.
It was suggested by @David Rodríguez - dribeas here, that it could potentially be used in a SFINAE context. I have tried, but it doesn't seem to work.
template <typename X>
constexpr auto fn(const X x)
-> typename std::enable_if<
std::is_integral<X>::value && is_const(x)
, int>::type
{
return 1;
}
int main()
{
fn(1);
}
The error message is different for each compiler that I've tried. clang states:
test-is_const.cpp:148:3: error: no matching function for call to 'fn'
fn(3);
^~~~~~~
test-is_const.cpp:136:3: note: candidate template ignored: disabled by 'enable_if' [with X = int]
std::is_integral<X>::value && is_const(x)
^
1 error generated.
g++ states different results depending on what CHECK
is because it also spits out the type information:
Adrian@K9 ~/winhome/Documents/test-is_const
$ g++ -std=c++11 -O3 -DCHECK=0 test-is_const.cpp && ./a.exe
test-is_const.cpp: In function ‘int main()’:
test-is_const.cpp:148:12: error: no matching function for call to ‘fn(int)’
fn(3);
^
test-is_const.cpp:134:16: note: candidate: template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (((sizeof (chkconst::chk2(((int)(x) ^ ((int)(x) & 2147483647)))) < sizeof (chkconst::Big)) | (sizeof (chkconst::chk2(((int)((-(int)(x))) ^ ((int)((-(int)(x))) & 2147483647)))) < sizeof (chkconst::Big))) | (sizeof (chkconst::chk2(((int)((-((int)(x) + 1))) ^ ((int)((-((int)(x) + 1))) & 2147483647)))) < sizeof (chkconst::Big)))), int>::type fn(X)
constexpr auto fn(const X x)
^
test-is_const.cpp:134:16: note: template argument deduction/substitution failed:
test-is_const.cpp: In substitution of ‘template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (((sizeof (chkconst::chk2(((int)(x) ^ ((int)(x) & 2147483647)))) < sizeof (chkconst::Big)) | (sizeof (chkconst::chk2(((int)((-(int)(x))) ^ ((int)((-(int)(x))) & 2147483647)))) < sizeof (chkconst::Big))) | (sizeof (chkconst::chk2(((int)((-((int)(x) + 1))) ^ ((int)((-((int)(x) + 1))) & 2147483647)))) < sizeof (chkconst::Big)))), int>::type fn(X) [with X = int]’:
test-is_const.cpp:148:12: required from here
test-is_const.cpp:134:16: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
Adrian@K9 ~/winhome/Documents/test-is_const
$ g++ -std=c++11 -O3 -DCHECK=1 test-is_const.cpp && ./a.exe
test-is_const.cpp: In function ‘int main()’:
test-is_const.cpp:148:12: error: no matching function for call to ‘fn(int)’
fn(3);
^
test-is_const.cpp:134:16: note: candidate: template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (sizeof (chkconst::chk2((0 + (! !(int)(x))))) != sizeof (chkconst::chk2((0 + (!(int)(x))))))), int>::type fn(X)
constexpr auto fn(const X x)
^
test-is_const.cpp:134:16: note: template argument deduction/substitution failed:
test-is_const.cpp: In substitution of ‘template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (sizeof (chkconst::chk2((0 + (! !(int)(x))))) != sizeof (chkconst::chk2((0 + (!(int)(x))))))), int>::type fn(X) [with X = int]’:
test-is_const.cpp:148:12: required from here
test-is_const.cpp:134:16: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
Adrian@K9 ~/winhome/Documents/test-is_const
$ g++ -std=c++11 -O3 -DCHECK=2 test-is_const.cpp && ./a.exe
test-is_const.cpp: In function ‘int main()’:
test-is_const.cpp:148:12: error: no matching function for call to ‘fn(int)’
fn(3);
^
test-is_const.cpp:134:16: note: candidate: template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (sizeof (chkconst::chk2(((int)(x) - (int)(x)))) < sizeof (chkconst::Big))), int>::type fn(X)
constexpr auto fn(const X x)
^
test-is_const.cpp:134:16: note: template argument deduction/substitution failed:
test-is_const.cpp: In substitution of ‘template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (sizeof (chkconst::chk2(((int)(x) - (int)(x)))) < sizeof (chkconst::Big))), int>::type fn(X) [with X = int]’:
test-is_const.cpp:148:12: required from here
test-is_const.cpp:134:16: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
Does anyone know why this is the case? Is it because it is trying to evaluate the value of the expression where it cannot get such information, only the type?