Question

I'm using C++11 on a project of mine and was wondering how best to represent the ELF magic number. I'm not a fan of hex literals, so I was looking for something better than:

const uint32 ELF_MAGIC_NUMBER = 0x7F454c46; // 0x7F, E, L, F

So, I tried to write:

const uint32 ELF_MAGIC_NUMBER = { 0x7F, 'E', 'L', 'F' };

but the compiler complains that there are too many items in the initializer list, which is understandable, although annoying.

Is there any way to write an integer literal in terms of its bytes? I feel like the first option, while it works, is not as readable at the second.

Was it helpful?

Solution

Since you can afford C++11, you could just define a little constexpr helper, which would enable compile-time evaluation:

#include <cstdint>

constexpr std::uint32_t from_bytes(char b1, char b2, char b3, char b4)
{
    return b4 + 
           (static_cast<std::uint32_t>(b3) << 8) + 
           (static_cast<std::uint32_t>(b2) << 16) + 
           (static_cast<std::uint32_t>(b1) << 24);
}

This way, your code won't look much different from the original version:

const std::uint32_t ELF_MAGIC_NUMBER = from_bytes(0x7F, 'E', 'L', 'F');

int main()
{
    static_assert(ELF_MAGIC_NUMBER == 0x7F454c46, "!");
}

Here is a live example.

OTHER TIPS

Many compilers have this little known feature: multi-char character literals.

uint32 ELF_MAGIC_NUMBER = '\177ELF';

You'll have to use octal numbers for the non char, I'm afraid.

Ah, almost forgot! The meaning of that is compiler dependent, so I wouldn't do it.

But if you can use C++11 you can use constexpr and user-defined literals:

constexpr uint32_t operator "" _mc (const char *str, size_t len)
{
    return len==4? 
         (str[0] << 24) | (str[1] << 16) | (str[2] << 8) | str[3] : 
         throw "_mc literal must be of length 4";
}

constexpr uint32_t ELF_MAGIC_NUMBER = "\177ELF"_mc;

This has the nice feature that you can use string concatenation to use hexadecimal characters:

constexpr uint32_t ELF_MAGIC_NUMBER = "\x7F""ELF"_mc;

How about:

const uint32 i = 0x1000000 * 0x7F
               + 0x10000 * 'E'
               + 0x100 * 'L'
               + 0x1 * 'F';

Potentially more readable (a matter of opinion):

const uint32 i = 0x01000000 * 0x7F
               + 0x00010000 * 'E'
               + 0x00000100 * 'L'
               + 0x00000001 * 'F';

Edit: I'd amend my own answer by saying that even if you take an approach like this, you will probably want to include the hex version of the literal in your code as a comment, for the sake of people e.g. searching for the magic number in hex form, or seeing it elsewhere. With this consideration in mind, since it is probably best to have the hex version in there anyway, it may be best as others have said to define the number in its hex form and add a comment as to what it represents, i.e. use your original version.

Doing the same task old way with using template metaprogramming

metafunction idiom

template <char a,char b,char c , char d>
struct  MAGIC_NUMBER {
    enum { value =d + 
           (static_cast<uint32_t>(c) << 8) + 
           (static_cast<uint32_t>(b) << 16) + 
           (static_cast<uint32_t>(a) << 24)  };
};




/*
 * usage
 */  
    const  uint32_t ELF_MAGIC_NUMBER = MAGIC_NUMBER<0x7F, 'E', 'L', 'F'>::value;

Well this solution, using user-defined literals, is so ugly I might cry:

#include <string>
#include <sstream>
#include <cstdint>
#include <cstddef>
#include <cassert>

uint32_t operator"" _u32s(const char* str, std::size_t size)
{
    std::istringstream ss(std::string(str, size));
    std::string token;
    int shift = 24;
    uint32_t result = 0;
    while (std::getline(ss, token, ',') && shift >= 0) {
        int value = 0;
        if (token.substr(0,2) == "0x") {
            std::stringstream hexss;
            hexss << std::hex << token;
            hexss >> value;
        } else if (token.length() == 1) {
            value = token[0];
        }
        result |= (value << shift);
        shift -= 8;
    }
    return result;
}

int main() {
    assert("0x7F,E,L,F"_u32s == 0x7F454c46);
}

Basically, you can now use the literal "0x7F,E,L,F"_u32s. Obviously it's nowhere near as nice as using a compile-time solution, but it's an interesting experiment.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top