Могу ли я указать целочисленную константу по ее байтам?
Я использую С++ 11 в моем проекте и задаюсь вопросом, как лучше всего представить магическое число ELF. Я не поклонник шестнадцатеричных литералов, поэтому я искал что-то лучше, чем:
const uint32 ELF_MAGIC_NUMBER = 0x7F454c46; // 0x7F, E, L, F
Итак, я попытался написать:
const uint32 ELF_MAGIC_NUMBER = { 0x7F, 'E', 'L', 'F' };
но компилятор жалуется, что в списке инициализаторов слишком много элементов, что понятно, хотя и раздражает.
Есть ли способ записать целочисленный литерал в терминах его байтов? Я чувствую, что первый вариант, хотя он работает, не так читается на втором.
Ответы
Ответ 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
. Очевидно, что это не так хорошо, как использование решения времени компиляции, но это интересный эксперимент.