Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaring char hexadecimal constants in C++11

Tags:

c++

char

c++11

hex

In various low-level parts of our code, we are required to send specific bytes to a device in order to make things happen. As such, we have plenty of code that looks like:

const char magic_bytes[] = { 0x01, 0xFA, 0x92 };

Which results in the error (on GCC 4.7.2)

test_char.cpp:6:51: warning: narrowing conversion of ‘250’ from ‘int’ to ‘const char’ inside { } is ill-formed in C++11 [-Wnarrowing]

Since 0xFA is outside the range -128 to 127.

There are two workarounds that I can think of:

const char magic_bytes[] = { static_cast<char>(0x01), static_cast<char>(0xFA), static_cast<char>(0x92) };

or:

const unsigned char magic_bytes[] = { 0x01, 0xFA, 0x92 };

Both of which are either ugly (the first case), or have other drawbacks (having to cast to (const char*) in the case of the latter)

Is there a better way to declare these strings?

like image 497
Damien Avatar asked Mar 29 '13 01:03

Damien


2 Answers

C++11 gives you variadic templates (with GCC support having existed for some time) to solve this problem.

template <typename... A>                                                                 
constexpr std::array<char, sizeof...(A)> byte_array(A... v)                              
{ return std::array<char, sizeof...(A)>{{static_cast<char>(v)...}}; }                    

constexpr auto arr = byte_array( 0x01, 0xFA, 0x92 );

Or to avoid repeatedly calling .data() for passing it to C funcs:

template <std::size_t S>
struct byte_array {
  char data_[S];
  char *data() { return data_; }
  operator char*() { return data_; }

  const char *data() const { return data_; }
  operator const char*() const { return data_; }

  constexpr std::size_t size() const { return S; }

  // one could add support for begin/end and things like that
};

template <typename... A>
constexpr byte_array<sizeof...(A)> make_byte_array(A... v)
{ return byte_array<sizeof...(A)>{{static_cast<char>(v)...}}; }

// beside constexpr, this can be also non-const
auto magic_bytes = make_byte_array( 0x01, 0xFA, 0x92 );
strtok(magic_bytes, "why?");

There is no overhead comparing to the plain char array.

like image 94
ipc Avatar answered Oct 03 '22 09:10

ipc


You can do something like this to have a single cast:

const unsigned char magic_bytesUC[] = { 0x01, 0xFA, 0x92 };
enum { NBYTES = sizeof(magic_bytesUC) };
const char *magic_bytes = reinterpret_cast<const char*>(magic_bytesUC);
like image 23
perreal Avatar answered Oct 03 '22 10:10

perreal