私は非常に驚くべきことに直面しています。(シナリオ: Win7 pro 64 ビット、VC2008 コンパイル 32 ビット コード)
メイン プログラムが std::map を使用するクラスをインスタンス化するとします。
これは std::map<std::string,Props> であり、クラス Props には 2 つのメンバーしかありません。
class Props {
public:
/**
* value of property
* @var boost::any
*/
boost::any _a ;
/**
* expire time
* @var time_t
*/
time_t _tExpiry ;
/* Somethign more I'm not writing down here... */
}
今...私はDLLを構築し、同じクラスを独自のビジネスに使用しています。
DLL はそのクラスをインスタンス化し、std::map をフィードします。
ええと...メインプログラムがマップにフィードすると、すべてがうまくいきますが、最初のアイテムの挿入後にDLLがクラッシュします。
何か(非常に興味深い)もっと。
メイン プログラムによって行われた挿入を深く掘り下げると、単一の _Node のコンストラクターに到達して std::map
_Node に次のように表示されます (c:\Programmi\Microsoft Visual Studio 9.0\VC\include\xtree)。
struct _Node
{ // tree node
_Node(_Nodeptr _Larg, _Nodeptr _Parg, _Nodeptr _Rarg,
const value_type& _Val, char _Carg)
: _Left(_Larg), _Parent(_Parg), _Right(_Rarg),
_Myval(_Val), _Color(_Carg), _Isnil(false)
{ // construct a node with value
}
_Nodeptr _Left; // left subtree, or smallest element if head
_Nodeptr _Parent; // parent, or root of tree if head
_Nodeptr _Right; // right subtree, or largest element if head
value_type _Myval; // the stored value, unused if head
char _Color; // _Red or _Black, _Black if head
char _Isnil; // true only if head (also nil) node
};
非常に細かい...つまり、_Node 構造は _Left (4 バイト)、_Parent (4 バイト)、_Right (4 バイト)、_Myval (キーのサイズとマップの値)、_Color (1 バイト)、および _Isnil (1 バイト) を持っています。
さて...マップにフィードしたい要素は、<std::string, Props> のペアです。
私のデバッガーによると、std::string には 0x20 バイトが必要ですが、Props は 8 バイトしか必要ありません。
ここで、デバッガーに単一ノードのサイズを尋ねると、0x38 であることがわかります。つまり...0x4 + 0x4 + 0x4 + 0x20 + 0x8 + 0x1 + 0x1 = 0x36 + パディング = 0x38 です。
これは、_Color メンバーが _Node の先頭から 0x34 バイト後に始まることを意味します。
(わかりました...すべてのプロジェクトで /Zp4 を使用するように指定したいので、すべての構造は 4 バイトでパックされます)。
続けてみましょう... DLL の動作をたどると、非常に驚くべきことがわかります。
std::map の _Buynode() メソッドは、アロケータを呼び出して、挿入しようとしている新しい要素の新しいサイズを取得します。_Node() インプレースコンストラクターが呼び出されるよりもアロケーターが呼び出されます....そして、別の方法で動作します!!
この場合、コンストラクターは、_Node の先頭から 0x38 バイトの後に _Color メンバーが開始するように動作します... あたかも別のパディングがあるかのように。
この後、新しい値を挿入しようとすると、_Color と _Isnil の値が間違っているため、手順は失敗します (メモリのその部分が初期化されていないため 0xcc)。
すべてのプロジェクトで /Zp4 をソリューションに設定したと確信しているので...
何が問題なのですか?
アラインメントに何か問題があると「感じ」ますが、何とも言えません...
前もって感謝します!
わかりました...さらに何かを追加します。
これは、c:\Programmi\Microsoft Visual Studio 9.0\VC\include\xtree の _Node 構造です。
struct _Node
{ // tree node
_Node(_Nodeptr _Larg, _Nodeptr _Parg, _Nodeptr _Rarg,
const value_type& _Val, char _Carg)
: _Left(_Larg), _Parent(_Parg), _Right(_Rarg),
_Myval(_Val), _Color(_Carg), _Isnil(false)
{ // construct a node with value
}
_Nodeptr _Left; // left subtree, or smallest element if head
_Nodeptr _Parent; // parent, or root of tree if head
_Nodeptr _Right; // right subtree, or largest element if head
value_type _Myval; // the stored value, unused if head
char _Color; // _Red or _Black, _Black if head
char _Isnil; // true only if head (also nil) node
};
_Tree_nod(const key_compare& _Parg,
allocator_type _Al)
: _Traits(_Parg, _Al), _Alnod(_Al)
{ // construct traits from _Parg and allocator from _Al
}
typename allocator_type::template rebind<_Node>::other
_Alnod; // allocator object for nodes
};
ご覧のとおり、コンストラクターは非常に単純です。
前に言われたように、std::map を asf するときに、この構造体から使用されるメモリを調べます。
- _Left の場合は
4バイト - _Parent の場合は 4 バイト - _Right
の場合は 4 バイト
- _Myval の場合は 0x28 バイト (std::string の場合は 0x20、自分のクラスの場合は 8 です。確認したところ、alqays は 8 バイト
です) - _Color の場合は 1 バイト
- 1 _Isnil のバイト
ツリーの一番上 (つまり、一番左) に配置された新しい要素が作成されると、このコストラクタは、_Left、_Parent、_Right、_Myval を、初期化されていない 4 バイト分 (たとえば 0xcc で埋められている) よりも埋め、_Color と見なされるものを埋めます。および _Isnil 4 バイト先。
最もばかげたことは、std::map の他のメソッドがこれらの 4 バイトを「感じない」ことです。
これらは、デアサーションを引き起こす行です。
if (_Isnil(_Ptr))
{
_Ptr = _Right(_Ptr); // end() ==> rightmost
if (_Isnil(_Ptr))
#if _HAS_ITERATOR_DEBUGGING
{
_DEBUG_ERROR("map/set iterator not decrementable");
_SCL_SECURE_OUT_OF_RANGE;
}
#elif _SECURE_SCL
{
_SCL_SECURE_OUT_OF_RANGE;
}
#else
return; // begin() shouldn't be incremented, don't move
#endif
}
これは、_IsNil(_Ptr) のテストで _Isnil が 0xcc であることを検出するとエラーが発生するために発生します。リリース バージョンはおそらくおかしくありません。これは喜ばしいことではありません。
何か案が?
もう一歩!
ソリューション全体 (2 つの大きなプロジェクト、18 の小さなプロジェクト、約 250000 の C/C++ コード行) を取り、Linux (gcc4.1.2) でコンパイルしました。
すべてうまくいきます。何の問題もなく、std::map は適切に動作します。
Linux Makefile は、すべてをより簡単にすると同時に複雑にすると言わざるを得ません。自分ですべてを行わなければならないので複雑ですが、したくない場合は何も起こらないので簡単です。
これは私に 1 つのことを教えてくれます: Visual Studio 2008 には何か問題があり、特定の条件でステージ上でジャンプする何かがあります...問題は、「これを引き起こす条件は何ですか?」です。
アイデアを待っています...