3

I want to write a function evaluated at compile time, it takes a pointer to 4 bytes array, and outputs an int that has the same bit pattern as that array. So I came up with:

constexpr int f(const char* p) {
     return *reinterpret_cast<int*>(p);
}

Then, I want to use f() like this:

switch(x) {
case f("GOOG"):
   // do something
case f("MSFT"):
   // do something
case f("NIKE"):
  // do something
}

However, I got a compiler error:

error: accessing value of ‘"GOOG"’ through a ‘int’ glvalue in a constant expression case f("GOOG")
  1. How to fix f() so it compiles?
  2. Is there a better way to accomplish the same goal?
7
  • 3
    Even if it compiled, it would be a strict aliasing violation and UB. Use bit-shifts to make the integer from individual chars. Commented Sep 24, 2020 at 6:53
  • @HolyBlackCat Thanks. btw what is UB? Commented Sep 24, 2020 at 6:55
  • UB is undefined behavior. Commented Sep 24, 2020 at 6:56
  • Thanks. I feel that doing bit shifting 4 times is not as elegant as just treat it as an int. Is there no way of doing that safely? Commented Sep 24, 2020 at 6:59
  • You can also create an int and memcpy the array into it, but memcpy is not constexpr. Commented Sep 24, 2020 at 7:01

1 Answer 1

3

Congratulations, you have activated the strict aliasing trap card and your code has undefined behaviour (if it would compile).

There are few errors in your code, the "correct" version is:

 constexpr int f(const char* p) {
         return *reinterpret_cast<const int*>(p);
    }
  • reinterpret_cast cannot cast away const.
  • cursor->p typo?

But since const char* does not point to an int, casting to it breaks the strict aliasing rule. int is not one of the types that can alias others - only std::byte, (unsigned) char can.

The cleanest would be this:

#include <cstring>

constexpr int f(const char* p) {
         int val = 0;
         static_assert(sizeof(val)==4); // If the array is 4-byte long.
         std::memcpy(&val,p,sizeof val);
         return val;
    }

But std::memcpy is not constexpr, even at run-time this will probably not have any overhead, compiler can recognize this and reinterpret the bytes on its own.

So go with bit-shifting:

constexpr int f(const char* p) {
       int value=0;
       using T = decltype (value);
       for(std::size_t i =0; i< sizeof(T);++i)
        value|= (T)p[i] << (8*i);

    return value;
    }

int main(){

    // @ == 64
    // 1077952576 = 01000000 01000000 01000000 01000000
    static_assert(f("@@@@") ==1077952576);
}

Just to be pedantic "@@@@" has length 5, not 4.

Sign up to request clarification or add additional context in comments.

8 Comments

cursor was typo. fixed. TY.
Can you explain what this value|= (T)*p << (8*i); is doing? It looks like it only use the first char of p, shift left by 1 byte, and doing an bitwise or with value. ie. is it using the rest of the chars? p[1], p[2] etc.
@ijklr Oops, sorry, of course it is missing the index, silly me for making a too easy test case. Fixed.
thanks. and the reason you used (T) instead of reinterpret_cast<T> is because inside constexpr functions, you can't reinterpret_cast, right?
I wish I can just cast it to int and don't need to loop :(
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.