std::vector<CString> をシリアライズしたいのですが

[戻る]
NOMURA 2010/02/02(火) 14:11:22
最近「シリアライズについて」というタイトルの質問させていただいた者です、お世話に
なります。
質問の内容は掲題の通りで、vector コンテナをシリアライズしたいと思い、検索したところ
「vector<int> に Serialize を使いたい!!」
http://okwave.jp/qa/q2261986.html
他の掲示板でも、同じ内容の質問がありました、回答内容は
CArchive に>> 演算子と<< 演算子をオーバーロードしたらどうでしょうか、ということ
ですが、
真似をして、下記のように書いてみたのですが
error LNK2019: 未解決の外部シンボル が出てしまいます、このエラーは私自身、
過去に何度となく付き合っていて、定義はされているが、その実体が無い場合
出るものと理解しております、多分m_CstrVec の定義の方法に間違いがあるのだと思うの
ですが、解決できません、ご指導よろしく願います。
VS2005 MFC

// CxxxDoc.h
template <class T, class Allocator>
CArchive& operator<<(CArchive&, const std::vector<T, Allocator>&);

template <class T, class Allocator>
CArchive& operator>>(CArchive&, std::vector<T, Allocator>&);

CxxxDoc.h
class CxxxDoc : public CDocument
{
protected: // シリアル化からのみ作成します。
    CxxxDoc();
    DECLARE_DYNCREATE(CxxxDoc)
    
public:
    static std::vector<CString> m_CstrVec;
    〜省略〜
};

// CxxxDoc.cpp
std::vector<CString> CxxxDoc::m_CstrVec;

// CxxxDoc シリアル化

void CxxxDoc::Serialize(CArchive& ar)
{
    if( ar.IsLoading() == TRUE)
    {
        ar >> m_CstrVec;
    }
    else
    {
        
        ar << m_CstrVec;
    }
}

・エラー内容
CxxxDoc.obj : error LNK2019: 未解決の外部シンボル "class CArchive & __cdecl
operator<<<class ATL::CStringT<char,class StrTraitMFC<char,class
ATL::ChTraitsCRT<char> > >,class std::allocator<class ATL::CStringT<char,class
StrTraitMFC<char,class ATL::ChTraitsCRT<char> > > > >(class CArchive &,class
std::vector<class ATL::CStringT<char,class StrTraitMFC<char,class
ATL::ChTraitsCRT<char> > >,class std::allocator<class ATL::CStringT<char,class
StrTraitMFC<char,class ATL::ChTraitsCRT<char> > > > > const &)"
(??$?6V?$CStringT@DV?$StrTraitMFC@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@V?$allocator@V?$CStringT@DV?$StrTraitMFC@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@@std@@@@YAAAVCArchive@@AAV0@ABV?$vector@V?$CStringT@DV?$StrTraitMFC@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@V?$allocator@V?$CStringT@DV?$StrTraitMFC@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@@std@@@std@@@Z)
が関数 "public: virtual void __thiscall CCxxxDoc::Serialize(class CArchive &)"
(?Serialize@CCxxxDoc@@UAEXAAVCArchive@@@Z) で参照されました。

Blue 2010/02/02(火) 14:19:00
>template <class T, class Allocator>
>CArchive& operator<<(CArchive&, const std::vector<T, Allocator>&);
>
>template <class T, class Allocator>
>CArchive& operator>>(CArchive&, std::vector<T, Allocator>&);
の定義はどこ?

NOMURA 2010/02/02(火) 14:47:50
Blue さんお世話になります。
確かにそこの部分の実装が無いですね、考えます。

Blue 2010/02/02(火) 14:56:27
ちなみにCStringArray(もしくはCArray)であればSerializeメソッドがあるので

CxxxDoc.h
class CxxxDoc : public CDocument
{
protected: // シリアル化からのみ作成します。
    CxxxDoc();
    DECLARE_DYNCREATE(CxxxDoc)
    
public:
    static CStringArray m_CstrVec; // static なんだ・・・
    〜省略〜
};

// CxxxDoc.cpp
CStringArray CxxxDoc::m_CstrVec;

// CxxxDoc シリアル化

void CxxxDoc::Serialize(CArchive& ar)
{
    m_CstrVec.Serialize(ar);
}

と記述できます。

bun 2010/02/02(火) 15:43:20
演算子のオーバーロードではないですが、
シリアライズしたいだけなら以下でOK。

void CxxxDoc::Serialize(CArchive& ar)
{
    if(ar.IsLoading())
    {
        int nVecSize;
        ar >> nVecSize;
        m_CstrVec.resize(nVecSize);
        for(int i=0; i<nVecSize; i++)
        {
            ar >> m_CstrVec[i];
        }
    } else {
        int nVecSize = m_CstrVec.size();
        ar << nVecSize;
        for(int i=0; i<nVecSize; i++)
        {
            ar << m_CstrVec[i];
        }
    }
}

未確認ですので、間違いがあったら、てきべん修正してください。

NOMURA 2010/02/02(火) 17:04:37
NOMURA です、お世話になります。
Blueさん
>ちなみにCStringArray(もしくはCArray)であればSerializeメソッドがあるので
そうですね、コンテナがCStringArray であればいいのですが、向学のためにもvector コ
ンテナをシリアライズする方法を知りたい次第です。
bunさん
> ar >> nVecSize;
この部分ですが、これでar の配列数を取得する意図だと思うのですが
一行しか書き込んでいない、ファイルを開き当該箇所をデバッグすると
ar の値が、1836345404 と不可解な値が入ってきます、当然
m_CstrVec.resize(nVecSize);の部分でメモリエラーが発生するのですが?

Blue 2010/02/02(火) 17:24:35
>一行しか書き込んでいない、ファイルを開き当該箇所をデバッグする
とは?

シリアライズできるのはシリアライズしたファイルのみです。
普通のテキストファイルのように扱いたいのであれば、CArchive::ReadStringをつかって
読み込んでみては。

Unicode文字セットだと、UnicodeテキストしかCArchive::ReadStringがうまくいかないか?
(調査中...)

Blue 2010/02/02(火) 17:42:55
Unicode文字セットのReadStringだと文字読み込み処理で wchar_t として読み込むような
ので、Shift_JISコード(CP932)で保存されているようなテキストファイルは読み込めない
ようです。
自力で1バイトずつ読み込み改行文字で分割し、Shift_JIS→CString変換(コンストラクタ
で可)で格納していくぐらいしかないかも。
当然書き込み時も、Shift_JISで書き込むのであれば CString→Shift_JIS変換(CStringAク
ラスを使うと便利)でWriteメソッドで書き込むんでしょうけど。



読み込みも書き込みもUnicode(シリアライズできるのはシリアライズしたファイルという
ルールを守れば)であれば、

void CxxxDoc::Serialize(CArchive& ar)
{
    if(ar.IsLoading())
    {
        m_CstrVec.RemoveAll();
        CString strLine;
        while (ar.ReadString(strLine)) {
            m_CstrVec.Add(strLine);
        }
    }
    else
    {
        for (INT_PTR i = 0; i < m_CstrVec.GetCount(); ++i)
        {
            ar.WriteString(m_CstrVec[i]);
            ar.WriteString(_T("\r\n"));
        }
    }
}

という記述で行けそう。

どっちにせよ入力ファイルの文字コードが一定じゃない場合は文字コード判定処理が不可
欠になっちゃいますが。(これが面倒なんだ)

Blue 2010/02/02(火) 17:54:53
>Shift_JISコード(CP932)で保存されているようなテキストファイル
はOnOpenDocumentをオーバーライドしてSerialize経由で開かなければできそうです。
保存時は
OnSaveDocument
をオーバーライドすのかな。

ファイル処理はCStdioFileやらfopenやらfstreamやらを使えばいいです。
(setlocaleが必要かも)

まぁ
>入力ファイルの文字コードが一定じゃない場合
は結局カバーしきれませんけど。

PATIO 2010/02/02(火) 18:42:08
補足というか同じ事になりますが、

シリアライズで取り込んで元の状態を再現できるのは、
そのクラスを出力方向でシリアライズする事で作成したファイルを
対象にした時だけになります。
シリアライズの部分に関しては各変数毎に定義されたシリアライズの
処理によって入/出力されますのでどういう形式で出力されるのかを
基本的に問題にはしません。
つまりあるクラスのシリアライズによって出力された内容は
そのクラスのシリアライズの機能によって取り込んだ場合にのみ
保証されます。

そういう意味ではシリアライズの結果をバイナリでダンプすれば、
どういうフォーマットで出力されているのかを把握は出来ますが、
それを元に作成したファイルをシリアライズで取り込んだ時の
結果に関しては保証されません。
あくまでもその部分はブラックボックスであると考えた方が良いと
思います。

NOMURA 2010/02/02(火) 20:03:28
NOMURA です、皆様ありがとうございました。
>シリアライズで取り込んで元の状態を再現できるのは、
>そのクラスを出力方向でシリアライズする事で作成したファイルを
>対象にした時だけになります。
MFCでのシリアライズの意味の理解が、はっきりと分かりました
Serialize(CArchive& ar) この場合のar オブジェクトはテキスト文字であろうが、画像
データであろうが、ようはビット列として処理してしまうということですね、
View に表示されているオブジェクトが、文字列であった場合、
それを文字列としてシリアライズしようとした場合は、当たり前のことですが、
それ相応の処理を書いてやらなければ、駄目だということだと理解しました。
結果、シリアライズ処理の部分で、文字列読込み、保存を行うという感覚で使うという感
じですね。

あと大変そうなのですが、非常に興味があるCArchive に>>、<< 演算子をオーバーロード
する方法ですが、
template <class T, class Allocator>
CArchive& operator<<(CArchive&, const std::vector<T, Allocator>&);

template <class T, class Allocator>
CArchive& operator>>(CArchive&, std::vector<T, Allocator>&);

template <class T, class Allocator>
この部分ですが
調べるとstd::vector は実際には
template <class T, class Allocator = allocator<T> >
class vector;
このように定義されているということなのですが、これと等価の意味をもつ、書き方と解
釈してよいのでしょうか?

NOMURA 2010/02/02(火) 23:11:01
[[解決]]
難しそうなので、ゆっくり勉強します、
ありがとうございました。

古いスレッドには追加発言できません。