2013-03-29 23 views
6

W różnych częściach kodu niskiego poziomu, jesteśmy zobowiązani do wysyłania określonych bajtów do urządzenia, aby wszystko mogło się zdarzyć. Jako taki, mamy mnóstwo kodu, który wygląda tak:Deklarowanie stałych szesnastkowych char w C++ 11

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

co prowadzi do błędu (na 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] 

Od 0xFA jest spoza zakresu -128 do 127.

Istnieją dwa obejścia że mogę myśleć:

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

lub:

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

które są albo brzydkie (pierwszy przypadek), albo mają inne wady (o oddanych do (const char *) W przypadku tych drugich)

Czy istnieje lepszy sposób deklarowania Zarówno te struny?

+4

ponieważ technicznie pracujesz z 'unsigned char's, dlaczego musisz rzucić na' char'? Naprawdę nie ma lepszego rozwiązania, poza ukrywaniem konwersji za pomocą makr. – Dave

+2

@Dave: Niestety, uniwersalnym symbolem "array of bytes" w C i C++ jest "char *", a nie "unsigned char *". Dlatego zazwyczaj potrzebujesz obsady. Tak, naprawdę powinno to być "void *", ale wiele API przyjmuje zamiast tego 'char *' s. –

Odpowiedz

4

C++ 11 daje variadic templates (z GCC wsparcie które istniało przez pewien czas), aby rozwiązać ten 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); 

lub w celu uniknięcia wielokrotnie nazywając .data() za przekazaniem go do funcs C:

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?"); 

Nie ma narzut w porównaniu do zwykłej tablicy char.

1

można zrobić coś takiego mieć pojedynczą obsadę:

const unsigned char magic_bytesUC[] = { 0x01, 0xFA, 0x92 }; 
enum { NBYTES = sizeof(magic_bytesUC) }; 
const char *magic_bytes = reinterpret_cast<const char*>(magic_bytesUC); 
1

Ponieważ podano C++ 11, będę zakładać można użyć variadic macros. W takim przypadku istnieje rozwiązanie, które jest eleganckie w użyciu, ale równie brzydkie, jak dzieje się za kulisami.

Więc zacznę pokazując, w jaki sposób chcesz go używać:

char myBytes1[] = MAKE_BYTES(0x00, 0x40, 0x80, 0xC0); 
char myBytes2[] = MAKE_BYTES(0xFF); 

a teraz kod backend: To może być krótszy, ale użyłem standardowych metod zapętlenie więc można dostać niektóre z nich wykorzystują ponownie. Może obsługiwać listy od 1 do 24 bajtów. Można go powiększyć, powtarzając końcowe linie jeszcze bardziej. Witamy w świecie metaprogramowania przedprocesorowego.

#define EXPAND(a) a 
#define ARGS_COUNT_(\ 
    _96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81,\ 
    _80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,\ 
    _64,_63,_62,_61,_60,_59,_58,_57,_56,_55,_54,_53,_52,_51,_50,_49,\ 
    _48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,\ 
    _32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,\ 
    _16,_15,_14,_13,_12,_11,_10, _9, _8, _7, _6, _5, _4, _3, _2, _1,\ 
    N,...) N 
#define ARGS_COUNT(...) ARGS_COUNT_(__VA_ARGS__,\ 
    96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,\ 
    80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,\ 
    64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,\ 
    48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,\ 
    32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,\ 
    16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1) 
#define ARGS_HEAD(a,...) (a) 
#define ARGS_TAIL(a,...) (__VA_ARGS__) 

#define FOREACH(macro,lmacro,list) FOREACH_(ARGS_COUNT list,macro,lmacro,list) 
#define FOREACH_(n,macro,lmacro,list) FOREACH__(n,macro,lmacro,list) 
#define FOREACH__(n,macro,lmacro,list) FOREACH_##n(macro,lmacro,list) 
#define FOREACH_1(macro,lmacro,list) lmacro list 
#define FOREACH_2(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_1(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_3(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_2(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_4(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_3(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_5(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_4(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_6(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_5(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_7(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_6(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_8(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_7(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_9(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_8(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_10(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_9(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_11(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_10(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_12(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_11(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_13(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_12(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_14(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_13(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_15(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_14(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_16(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_15(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_17(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_16(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_18(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_17(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_19(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_18(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_20(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_19(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_21(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_20(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_22(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_21(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_23(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_22(macro,lmacro,ARGS_TAIL list) 
#define FOREACH_24(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_23(macro,lmacro,ARGS_TAIL list) 

#define MAKE_BYTE(x) static_cast<char>(x), 
#define MAKE_LAST_BYTE(x) static_cast<char>(x) 
//#define MAKE_BYTES(x) { FOREACH(MAKE_BYTE,MAKE_LAST_BYTE,x) } // uncomment this if you would rather use double bracket ((0x00,0x40,...)) syntax 
#define MAKE_BYTES(...) { FOREACH(MAKE_BYTE,MAKE_LAST_BYTE,(__VA_ARGS__)) } 
+0

Czy makra variadic są wstecznie kompatybilne z C++ 03? Lub, do zaakceptowania przez nas, zostały wprowadzone jako rozszerzenie GCC we wcześniejszych wersjach (na przykład GCC 4.2)? – Damien

+1

@Damien: Tak (rozszerzenie GCC), chociaż jeśli cofniesz się wystarczająco daleko, składnia zmieni się na nazwane parametry, z 'nameOfArgs ...'. W moich projektach mam kontrole do zamiany makr variadic (i używam komentarza z ostatnich linii, aby zachować wszystkie zmiany we wspólnych bitach), ale to oczywiście oznacza znacznie więcej kodu. Problem polega na tym, że nie standaryzowano go aż do C99 i C++ 11.Nie wiem, jak długo formuła zgodna z normami była w GCC, ale jestem pewna, że ​​4.2 to miała. Prawdopodobnie dużo wcześniejszych wersji. – Dave