c++ - smart - ¿Puedo especificar una constante entera por sus bytes?
programar contratos inteligentes (5)
Estoy usando C ++ 11 en un proyecto mío y me preguntaba cómo representar mejor el número mágico de ELF. No soy un fanático de los literales hexadecimales, así que estaba buscando algo mejor que:
const uint32 ELF_MAGIC_NUMBER = 0x7F454c46; // 0x7F, E, L, F
Entonces, traté de escribir:
const uint32 ELF_MAGIC_NUMBER = { 0x7F, ''E'', ''L'', ''F'' };
pero el compilador se queja de que hay demasiados elementos en la lista de inicializadores, lo cual es comprensible, aunque molesto.
¿Hay alguna manera de escribir un literal entero en términos de sus bytes? Siento que la primera opción, mientras funciona, no es tan legible en la segunda.
Bueno, esta solución, usando literales definidos por el usuario, es tan fea que podría llorar:
#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);
}
Básicamente, ahora puede usar el literal "0x7F,E,L,F"_u32s
. Obviamente, no es tan bueno como usar una solución en tiempo de compilación, pero es un experimento interesante.
Como puede pagar C ++ 11, podría definir un pequeño ayudante constexpr
, que permitiría la evaluación en tiempo de compilación:
#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);
}
De esta manera, su código no se verá muy diferente de la versión original:
const std::uint32_t ELF_MAGIC_NUMBER = from_bytes(0x7F, ''E'', ''L'', ''F'');
int main()
{
static_assert(ELF_MAGIC_NUMBER == 0x7F454c46, "!");
}
Aquí hay un ejemplo en vivo .
Haciendo la misma tarea de la forma antigua con el uso de metaprogramación de plantillas
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;
Muchos compiladores tienen esta característica poco conocida: los literales de carácter multi-char.
uint32 ELF_MAGIC_NUMBER = ''/177ELF'';
Tendrás que usar números octales para los que no son char, me temo.
Ah, casi lo olvido! El significado de eso depende del compilador, así que no lo haría.
Pero si puede usar C ++ 11, puede usar constexpr
y literales definidos por el usuario:
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;
Esto tiene la buena característica de que puedes usar la concatenación de cadenas para usar caracteres hexadecimales:
constexpr uint32_t ELF_MAGIC_NUMBER = "/x7F""ELF"_mc;
Qué tal si:
const uint32 i = 0x1000000 * 0x7F
+ 0x10000 * ''E''
+ 0x100 * ''L''
+ 0x1 * ''F'';
Potencialmente más legible (una cuestión de opinión):
const uint32 i = 0x01000000 * 0x7F
+ 0x00010000 * ''E''
+ 0x00000100 * ''L''
+ 0x00000001 * ''F'';
Edit: enmendaría mi propia respuesta diciendo que incluso si adopta un enfoque como este, probablemente querrá incluir la versión hexadecimal del literal en su código como un comentario, por el bien de las personas, por ejemplo, buscando la magia. Número en forma hexadecimal, o verlo en otro lugar. Con esta consideración en mente, ya que probablemente es mejor tener la versión hex en allí de todos modos, puede ser lo mejor que otros hayan dicho para definir el número en su forma hexadecimal y agregar un comentario sobre lo que representa, es decir, usar su original versión.