Подтвердить что ты не робот

Могу ли я указать целочисленную константу по ее байтам?

Я использую С++ 11 в моем проекте и задаюсь вопросом, как лучше всего представить магическое число ELF. Я не поклонник шестнадцатеричных литералов, поэтому я искал что-то лучше, чем:

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

Итак, я попытался написать:

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

но компилятор жалуется, что в списке инициализаторов слишком много элементов, что понятно, хотя и раздражает.

Есть ли способ записать целочисленный литерал в терминах его байтов? Я чувствую, что первый вариант, хотя он работает, не так читается на втором.

4b9b3361

Ответ 1

Поскольку вы можете позволить себе С++ 11, вы можете просто определить небольшой помощник constexpr, который позволил бы оценить время компиляции:

#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);
}

Таким образом, ваш код не будет сильно отличаться от исходной версии:

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

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

Вот живой пример.

Ответ 2

Многие компиляторы имеют эту малоизвестную функцию: многозначные символы char.

uint32 ELF_MAGIC_NUMBER = '\177ELF';

Вам придется использовать восьмеричные числа для char, боюсь.

Ах, почти забыл! Смысл этого зависит от компилятора, поэтому я бы этого не сделал.

Но если вы можете использовать С++ 11, вы можете использовать constexpr и пользовательские литералы:

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;

У этой приятной функции вы можете использовать конкатенацию строк для использования шестнадцатеричных символов:

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

Ответ 3

Как насчет:

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

Потенциально более читаемый (вопрос мнений):

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

Изменить: Я бы исправил свой собственный ответ, сказав, что даже если вы примете такой подход, вы, вероятно, захотите включить шестнадцатеричную версию литерала в свой код в качестве комментария, для людей, например поиск магического числа в шестнадцатеричной форме или просмотр его в другом месте. Учитывая это соображение, поскольку, вероятно, лучше всего иметь шестнадцатеричную версию там, но может быть лучше, как говорили другие, определить число в его шестнадцатеричной форме и добавить комментарий к тому, что он представляет, т.е. Использовать оригинал версия.

Ответ 4

Выполнение одной и той же задачи с использованием метапрограммирования шаблона

метафорическая идиома

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;

Ответ 5

Ну, это решение, используя пользовательские литералы, настолько уродливое, что я могу плакать:

#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);
}

В принципе, теперь вы можете использовать литерал "0x7F,E,L,F"_u32s. Очевидно, что это не так хорошо, как использование решения времени компиляции, но это интересный эксперимент.