2016-06-12 9 views
1

私は生のバイナリデータブロックを持っています(実際にはCBOR - エンコードされています)。このソリューションは、x86/x86_64 PCとarm/arm64のiOS上で動作バイナリデータ、クロスプラットフォーム(C/C++)から数値を読み取る方法は?

template <typename T> // T can be uint64_t, double, uint32_t, etc... 
auto read(const uint8_t *ptr) -> T { 
    return *((T *)(ptr)); // all endianess-aware functions will be performed later 
} 

:のような数値を読み取るには、私は一般的な形式を使用します。 しかし、arm/armv7のAndroidでは、デフォルトのリリース最適化レベル(-Os)でclangコンパイラを使用していますが、1(アライメントのない読み取り)のSIGBUSがあります。私は別のソリューションとその問題を解決:

template <typename T> 
auto read(const uint8_t *ptr) -> T { 
    union { 
     uint8_t buf[sizeof(T)]; 
     T value; 
    } u; 
    memcpy(u.buf, ptr, sizeof(T)); 
    return u.value; 
} 

は、あらゆるプラットフォームに依存しないソリューションはあり、それがパフォーマンスに影響を与えないのだろうか?

+0

私はそれはおそらくあなたが得る限り良いことだと思います。 –

+0

未定義の振る舞いの再解釈の代わりに、適切な(デ)直列化を使用してください。あなたはすでにいくつかの問題に遭遇しました。 – Olaf

答えて

4

caveat - この回答は、マシンの整数表現が質問のようにリトルエンディアンであることを前提としています。

は、プラットフォームに依存せず、正しい方法はmemcpyを使用することです。組合は必要ありません。

効率を心配しないでください。 memcpyは魔法の関数であり、コンパイラは「正しいことをする」でしょう。

例えば、x86用にコンパイル:

#include <cstring> 
#include <cstdint> 

template <typename T> 
auto read(const uint8_t *ptr) -> T { 
    T result; 
    std::memcpy(&result, ptr, sizeof(T)); 
    return result; 
} 

extern const uint8_t* get_bytes(); 
extern void emit(std::uint64_t); 

int main() 
{ 
    auto x = read<std::uint64_t>(get_bytes()); 
    emit(x); 

} 

は、アセンブラが得られます。

main: 
     subq $8, %rsp 
     call get_bytes() 
     movq (%rax), %rdi   ; note - memcpy utterly elided 
     call emit(unsigned long) 
     xorl %eax, %eax 
     addq $8, %rsp 
     ret 

ノート:エンディアン

あなたは、ランタイム・エンディアンを追加することによって、このソリューションは、本当に移植することができますチェック。コンパイラはそれを見るように実際には、チェックが省略されます。

constexpr bool is_little_endian() 
{ 
    short int number = 0x1; 
    char *numPtr = (char*)&number; 
    return (numPtr[0] == 1); 
} 


template <typename T> 
auto read(const uint8_t *ptr) -> T { 
    T result = 0; 
    if (is_little_endian()) 
    { 
    std::memcpy(&result, ptr, sizeof(result)); 
    } 
    else 
    { 
    for (T i = 0 ; i < sizeof(T) ; ++i) 
    { 
     result += *ptr++ << 8*i; 
    } 
    } 
    return result; 
} 

たマシンコード変更されません:

main: 
     subq $8, %rsp 
     call get_bytes() 
     movq (%rax), %rdi 
     call emit(unsigned long) 
     xorl %eax, %eax 
     addq $8, %rsp 
     ret 
+0

ナンセンス!唯一の準拠したプラットフォームに依存しない方法は、ビットシフトによる直列化です。 'memcpy'は、エンディアンや表現を気にしません。 – Olaf

+0

Endianessは問題ではありません。なぜなら、私が必要とするコンパイラはすべて、バイトオーダーを変換するための組み込み関数を持っているからです。内在がないときは、私はビットシフトに後退します。 – SBKarr

+0

@olaf私はエンディアンの問題を認識しており、OPがそれを知っていると想定しています。バイトストリームを整数に転送するという問題に取り組んでいました。私は答えに警告を追加します。 –

関連する問題