0

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?

Community
  • 1
  • 1
Adrian
  • 10,246
  • 4
  • 44
  • 110
  • is that your whole code? there's only one overload for `fn`. – Karoly Horvath May 19 '16 at 12:07
  • @KarolyHorvath, It is simplified to reduce noise, but in this case it should work. – Adrian May 19 '16 at 12:09
  • Where's the error message? :/ – Karoly Horvath May 19 '16 at 12:10
  • `x` is not a constexpr, if you want to force "`constexpr`" argument, take `std::integral_constant`, or ask them as template argument. – Jarod42 May 19 '16 at 12:16
  • @KarolyHorvath, added them now. – Adrian May 19 '16 at 12:19
  • @Jarod42, please explain – Adrian May 19 '16 at 12:20
  • @Jarod42, oh, I see what you mean. No, I don't want to pass the value as a template argument, I want to keep it as a function parameter. – Adrian May 19 '16 at 12:22
  • 1
    Within the body of the function, a function parameter is never treated as a constant compile-time value, at least at that stage in parsing, unless it has `constexpr` operations on it that do not depend on its non-type state. – Yakk - Adam Nevraumont May 19 '16 at 15:30
  • @Yakk, within a body of a function, there is knowledge that can indicate if the value is a compile time value, that is how `is_const()` works. However, I'm thinking that it maybe that the return type is outside of the body and is why this isn't working in a SFINAE context. Just would like some confirmation from more advanced users that this is the case. Preferably with a reference to the standard, or with knowledge to the compilers' inners. – Adrian May 19 '16 at 16:26
  • 1
    @Adrian Parameters *to functions* never have compile time values. If you pass `constexpr int x=3` to a function, and run a `is_constexpr` check on it in the body, you'll still see "not a constexpr". – Yakk - Adam Nevraumont May 19 '16 at 17:25
  • Only zero-valued integer literals are null pointer constants in C++11 or later (the DR that made this change is de facto retroactive to C++11). This method of checking is doomed from the very beginning. – T.C. May 20 '16 at 04:16
  • @Yakk, I'm assuming that you meant `is_const`, right? Or is there something else got are referring to? If so then it did when I called a constexpr function, with CHECK set to 2. However, it might be that it is a false positive. – Adrian May 20 '16 at 05:18
  • @T.C., what do you mean by "DR"? It might very well be doomed. – Adrian May 20 '16 at 05:22

0 Answers0