wstring な JSON 処理系 (その2)

コピー・コンストラクタとムーブ・コンストラクタを定義します。

  1. json クラスのコンストラクタとデスタラクタ
  2. json クラスのコピー・コンストラクタとムーブ・コンストラクタ (本稿)
  3. dump
  4. load の構文解析
  5. load の字句解析

コンストラクタと代入演算子の定義は、類似のコードが並んで単調なので、淡々と埋めていきます。というのは表向きの説明で、実際には m4 マクロ・プロセッサを使って生成させています。C++ のソース・コードでプリプロセッサを使うのを嫌がるなら、他のマクロ・プロセッサでテンプレートな部分を生成させたものを、テキスト・エディタで体裁を整えて打ち込んだかのような顔をしておけば良いのです。

コンストラクタの placement new の引数に std::move を指定したムーブ・セマンティクス版を作ります。さらに、 コピー・コンストラクタとムーブ・コンストラクタを追加します。コピー・コンストラクタは共用体のコピーに copy_data メンバ関数を使います。ムーブ・コンストラクタも同様に move_data メンバ関数を使います。

//@<コピーおよびムーブ・コンストラクタを定義します@>=
    json (std::wstring&& x) : mguard (STRING) { new (&mstring) std::wstring (std::move(x)); }
    json (array&& x) : mguard (ARRAY) { new (&marray) array (std::move(x)); }
    json (object&& x) : mguard (OBJECT) { new (&mobject) object (std::move(x)); }
    json (json const& x) : mguard (x.mguard) { copy_data (x); }
    json (json&& x) : mguard (x.mguard) { move_data (x); }

代入演算子では、 destroy メンバ関数で必要に応じて現在のメンバの開放をした上で、新しいメンバで上書きします。コンストラクタと同様に、共用体へは copy_data でコピーし、move_data でムーブします。自己代入を避けるのはセオリー通りです。

//@<コピーおよびムーブ代入演算子を定義します@>=
    json& operator=(json const& x)
    {
        if (this != &x) {
            destroy ();
            mguard = x.mguard;
            copy_data (x);
        }
        return *this;
    }

    json& operator=(json&& x)
    {
        if (this != &x) {
            destroy ();
            mguard = x.mguard;
            move_data (x);
        }
        return *this;
    }

replace メンバ関数は、コピー代入演算子と同じ処理をします。違いは戻り値がないことです。これも、自己置き換えをしないようにします。

//@<replace メンバ関数を定義します@>=
    void replace (json const& x)
    {
        if (this != &x) {
            destroy ();
            mguard = x.mguard;
            copy_data (x);
        }
    }

ムーブ・セマンティクス用の move_data では、メンバの開放が済んでいることを前提に、共用体部分の移動をおこないます。移動で特別扱いしなければならないのはコンテナである文字列・配列・オブジェクトだけで、他はコピー・セマンティクスと同じです。

//@<move_data メンバ関数を定義します@>=
    void move_data (json& x)
    {
        switch (mguard) {
        case STRING:
            new (&mstring) std::wstring (std::move (x.mstring));
            break;
        case ARRAY:
            new (&marray) array (std::move (x.marray));
            break;
        case OBJECT:
            new (&mobject) object (std::move (x.mobject));
            break;
        default:
            copy_data (x);
            break;
        }
    }

コピー・セマンティクス用の copy_data では、共用体部分のコピーをおこないます。

//@<copy_data メンバ関数を定義します@>=
    void copy_data (json const& x)
    {
        switch (mguard) {
        case BOOLEAN:
            mboolean = x.mboolean;
            break;
        case INTEGER:
            minteger = x.minteger;
            break;
        case NUMBER:
            mnumber = x.mnumber;
            break;
        case STRING:
            new (&mstring) std::wstring (x.mstring);
            break;
        case ARRAY:
            new (&marray) array (x.marray);
            break;
        case OBJECT:
            new (&mobject) object (x.mobject);
            break;
        default:
            break;
        }
    }

swap も定義しておきます。ストージされている型と比較して相手が別の型のときは、型変換せずに例外を投げます

//@<swap メンバ関数を定義します@>=
    void swap (json& x)
    {
        if (mguard != x.mguard)
            throw std::logic_error ("swap only same type together");
        std::swap (mguard, x.mguard);
        switch (mguard) {
        case BOOLEAN:
            std::swap (mboolean, x.mboolean);
            break;
        case INTEGER:
            std::swap (minteger, x.minteger);
            break;
        case NUMBER:
            std::swap (mnumber, x.mnumber);
            break;
        case STRING:
            std::swap (mstring, x.mstring);
            break;
        case ARRAY:
            std::swap (marray, x.marray);
            break;
        case OBJECT:
            std::swap (mobject, x.mobject);
            break;
        default:
            break;
        }
    }