2016-04-25 6 views
0

3D(2D、4D)の規則的な長方形グリッドにハッシュ点を入れるために、C++ 11 std::unordered_mapを使いたいと思います。3D hasingのためのunordered_mapの最適な使用法:私はユニオンをキーとして使用する必要がありますか?

もともと私は手動梱包を行うと、次のようにインデックスを開梱した

inline int_fast64_t xyz2i(int_fast16_t ix, int_fast16_t iy, int_fast16_t iz){ 
    return ix | (iy << 16) | (iz << 32); 
} 

inline void i2xyz(int_fast64_t i, int_fast16_t& ix, int_fast16_t& iy, int_fast16_t& iz){ 
    iz=(i & 0xFF0000) >> 32; 
    iy=(i & 0x00FF00) >> 16; 
    ix=(i & 0x0000FF); 
} 

、私はunionsを使用すると、おそらく良いだろうことに気づい:

class NodeIndex4D{ 
    public: 
    union{ 
     struct{ int_fast16_t x,y,z,w; }; 
     int_fast16_t array[4]; 
     int_fast64_t i; 
    }; 
}; 

それでも、私はそのI感じていますそれを最善の方法でやってはいけません。 C++言語の機能は、おそらくこの目的のために使用することができます。

など。どのようなとても素敵ではありませんが、私のinsertNode機能である:

std::unordered_map<int_fast64_t,Node> nodes; 

Node& insertNode(int_fast16_t ix, int_fast16_t iy, int_fast16_t iz){ 
    NodeIndex4D inod; 
    inod.x=ix; inod.y=iy; inod.z=iz; inod.w=0; // NOT SO NICE 
    int sz0 = nodes.size(); 
    Node& node = nodes[inod.i]; // get valid reference (if new allocate, if old take it) 
    if(nodes.size() > sz0){ // new element 
     node.id = sz0; 
     index2pos({ix,iy,iz}, node.pos); // not relevant here 
     node.pos.add_mul(scaling, -0.5); // not relevant here 
    } 
    return node; 
} 

NodeIndex4D.i上の標準ハッシュ関数を使用しますstd::unordered_map<NodeIndex4D,Node>ような何かをするだろうことはできないのですか?

も私は部分的にポータブル方法は4 int16_tの配列にint64_tをエイリアスすることであろう Node& insertNode(NodeIndex4D inod)としての私の挿入機能を定義し、Node& insertNode({ix,iy,iz,0})

+1

ここで質問は何ですか? –

+0

あなたの質問は何ですか?あなたの最後の質問に対する私の答えについてのフィードバックをいただけますか?あなたはそれを放棄したようです。 –

+1

* "あまりにも良い" * - あなたは 'NodeIndex4D'のコンストラクタをオーバーロードしたり、いくつかの引数にデフォルト値で書くことができ、' NodeIndex4D inod {ix、iy、iz};を直接書くことができます。それでも 'union'の大きな問題はエイリアスです.C++は、' int16_t'フィールドを使って値を設定し、 'int_fast64_t i;'を使って値を読み込んだ場合、またはその逆の場合、未定義の動作をします。あなたの実装は、あなたのコードが確実に動作するかもしれないし、そうでないかもしれない独自の動作を規定しているかもしれないし、そうしていないかもしれない。あなたが移植性を気にしなければ、あなたのコンパイラドキュメントをチェックしなければならない。 –

答えて

1

としてそれを呼び出すように検討していました。標準によって明示的に許可されていなくても、それはint16_tint64_t(私が知っている限り)の両方をサポートする共通のアーキテクチャで動作するはずです。

x、y、z、wランクが0,1,2,3または3,2,1であるかどうかを静的要素でプログラムの先頭でテストする必要があるのは、エンディアンです、それぞれ0。コードに続いて、トリックを行う必要があります。

class Node4D { 
    uint64_t packed; 
    uint16_t (& unpacked)[4]; 

    static bool isBigEndian() { 
     static bool inited = false; 
     static bool bigEndian = false; 
     if (! inited) { 
      Node4D n; 
      inited = true; 
      n.packed = 1; 
      bigEndian = (n.unpacked[0] == 0); 
     } 
     return bigEndian; 
    } 

public: 
    Node4D(): unpacked(reinterpret_cast<uint16_t (&)[4]>(packed)) { 
     packed = 0; 
    } 
    Node4D(uint64_t p): unpacked(reinterpret_cast<uint16_t (&)[4]>(packed)) { 
     packed = p; 
    } 
    Node4D(uint16_t x, uint16_t y, uint16_t z, uint16_t w): 
    unpacked(reinterpret_cast<uint16_t (&)[4]>(packed)) { 
     set(x, y, z, w); 
    } 
    uint64_t get() { 
     return packed; 
    } 
    void get(uint16_t& x,uint16_t& y,uint16_t& z,uint16_t& w) { 
     if (isBigEndian()) { 
      w = unpacked[0]; 
      z = unpacked[1]; 
      y = unpacked[2]; 
      x = unpacked[3]; 
     } 
     else { 
      x = unpacked[0]; 
      y = unpacked[1]; 
      z = unpacked[2]; 
      w = unpacked[3]; 
     } 
    } 

    void set(uint16_t x, uint16_t y, uint16_t z, uint16_t w) { 
     if (isBigEndian()) { 
      unpacked[0] = w; 
      unpacked[1] = z; 
      unpacked[2] = y; 
      unpacked[3] = x; 
     } 
     else { 
      unpacked[0] = x; 
      unpacked[1] = y; 
      unpacked[2] = z; 
      unpacked[3] = w; 
     } 
    } 
}; 

は内部的には、構造体は、ただ1つの8バイト長の要素が含まれているので、それを安全にキーとして使用することができます。

+0

ありがとう、私はもう一度それを慎重に読む必要があります。また、 'uint16_t(&unpacked)[4];正確には –

+0

を意味します。@ProkopHapala:' int64_t'への参照を 'int16_t'の配列への参照にエイリアスするために使用されます。 –

+0

私は理解していますが、一般的に意味するもの(つまり、C++構文の一般的なロジックにどのように適合しているか)を知ることはできません.... uint16_tとunpacked [4]; –