2017-02-15 3 views
1

私はofstreamというマップを持っています。私はメッセージのパケットを調べ、特定のシンボルに関する各メッセージをそれぞれのファイルに書きたいと思っています。本質的には、パケットファイルをより小さなものに分割します。 ofstreamにI <<が書き込まれるとファイルに書き込まれます。しかし、マップイテレータ経由でアクセスしたofstreamへの<<を試してみると、うまくコンパイルされてもクラッシュします。マップイテレータを使ってofstreamに書き込む方法

私がマップを使用している理由は、パケットヘッダーが与えられたシンボルに対して複数回書き込まれないようにするためです。

void write_packet_to_symbol_file(packet p) 
{ 

    string path = "E:\\20170131\\"; 
    map<string,ofstream&> outs; 
    for (message m : p.messages) { 
    map<string,ofstream&>::iterator it = outs.find(m.symbol.name); 
    if (it == outs.end()) { 
     string full_path = path + m.symbol.name + ".CAP"; 
     ofstream of; 
     of.open(full_path, ios_base::app); 
     // write packet header since first message for that symbol 
     of << p.get_header(); 
     outs.emplace(m.symbol.name,of); 
    } 

    map<string,ofstream&>::iterator it2 = outs.find(m.symbol.name); 
    if (it2 != outs.end()) 
     it2->second << m.get_message_content(); 
} 

私は間違っていますか?

+1

名前が見つからない場合は、新しいストリームを開いてマップに追加します。しかし、それを指すように決めることは決してありません。 – Barmar

+1

あなたは2つのことを間違っている:プログラムがコンパイルされただけであると仮定しているが、それは正しい。それは明らかに真実ではありません。第2に、ストリームオブジェクトはコピー可能ではないため、ストリームオブジェクトを作成して開くことはできず、後でマップにコピーすることはできません。それらは移動可能ですが、ストリームオブジェクトを適切に移動する必要があります。 –

+0

新しいノードをマップに間違って挿入した後、あなたが派遣しようとしているイテレータ 'it'はまだouts.end()と等しくなります。 – felix

答えて

6

あなたのofstreamインスタンスはif文のローカル変数で、if文が終了すると閉じられ、破棄されます。マップのような参照のコレクションを使うのではなく、ストリームポインタ(できればスマートポインタ)のマップを作成し、ストリームを動的に作成し、既に書いたすべてのコードをスクラップする必要があります。

+0

@Neil Butterworth ifブロックの最後に破棄される場合、どのようにポインタがそれを保持しますか? – bkarj

+1

@Behポインタはそれを保持しません。 'new'でそれを作成すると、それを永続させます。また、移動セマンティクスは使用できますが、参照マップは使用できません。 –

+0

この作業を行うためにポインタや移動セマンティクスは必要ありません。私の答えを見てください。 – zett42

0

mapの文字列からのストリーム参照があるようです。私はそれがどのようにコンパイルされているのか分かりませんが、参照されたオブジェクトが破壊されるので、間違いなくバグです。

私はあなたがマップに、あなたはit->second.open()を呼び出す必要が返さイテレータであなたべき.insert().emplace()その後map<string,ofstream> を使うべきだと思います。

ifstreamがコピー可能ではなくmake_pairコールをスキップしている、emplaceに対する同様

map.insert(make_pair(some_key, ifstream(...))); //this will work, calls move 

ifstream x; 
map.insert(make_pair(some_key, x)); //this will NOT work, calls copy 

map.insert(make_pair(some_key, std::move(x))); // should work 

ように移動可能であるので、これは少し難しいかもしれません。

+2

構文的に正しいためコンパイルされます。コンパイラがあなたのために行う他の何か、警告のようなものは、グレイビーです。論理が正しい文法から正しいと仮定することはできません。私は例として政治家の例を挙げています。 – user4581301

1

他の人はすでに説明したように、ifブロック内に一時的なストリームオブジェクトのみを作成しています。 ifブロックが終了したときにこのオブジェクトが破棄され、マップに格納された参照が無効になります。

map<string,ofstream>が必要なのは、マップがストリームオブジェクトを参照するだけでなく実際に格納する必要があるためです。

今、私たちは問題を抱えています。一時的なストリームをどのようにマップに入れるのですか?ストリームにはコピーコンストラクタや代入演算子がないため、ストリームオブジェクトのコピーはできません。

オペレータ[]を使用してストリームオブジェクトをマップ内に直接作成できるため、一時オブジェクトは不要です。

したがって、一部のユーザーの提案に応じて、ポインタやセマンティクスは必要ありません。

void write_packet_to_symbol_file(packet p) 
{ 

    string path = "E:\\20170131\\"; 
    map<string,ofstream> outs; 
    for (message m : p.messages) { 
    map<string,ofstream>::iterator it = outs.find(m.symbol.name); 
    if (it == outs.end()) { 
     // Create stream object in the map and get reference 'of' to it 
     ofstream& of = outs[ m.symbol.name ]; 

     string full_path = path + m.symbol.name + ".CAP"; 
     of.open(full_path, ios_base::app); 
     // write packet header since first message for that symbol 
     of << p.get_header(); 

     // No need to insert 'of' into 'outs', because it is already in there! 
    } 

    map<string,ofstream>::iterator it2 = outs.find(m.symbol.name); 
    if (it2 != outs.end()) 
     it2->second << m.get_message_content(); 
} 
関連する問題