エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

既存の構造体に代入する仕組みを作る

よく似てはいるものの、異なる構造体/クラスの間で値をやり取りすることを考えるので、正しくは「代入」ではないんですが、ここでは「代入」ということばを使うことにします。

たとえば、どこかでこういった構造体が定義されていたとします。

#include <cstdlib>

static const std::size_t MAX_LENGTH = 16;

struct HisStructure
{
    std::size_t length;
    char        value[MAX_LENGTH + 1];
};

自分で作ったこういうクラスがあるとします。

#include <string>

class MyClass
{
public:
    MyClass() : value_() {}
    MyClass(const std::string& value) : value_(value) {}

    void setValue(const std::string& value) { value_ = value; }
    const std::string& getValue() const { return value_; }

    MyClass& operator = (const HisStructure& hisStructure)
    {
        setValue(std::string(hisStructure.value, hisStructure.length));
        return *this;
    }

private:    
    std::string value_;
};

すでにコードに現れていますが、HisStructureMyClassという代入は、MyClassに代入演算子を定義することで可能になります。

    HisStructure hisStructure = { 10, "abcdefghij" };
    MyClass      myClass;

    // OK
    myClass = hisStructure;

逆にMyClassHisStructureという代入をやってみます。

「左値」を表すクラスを定義します。

template<typename T>
class LValue
{
public:
    explicit LValue(T& lvalue) : lvalue_(lvalue) {}

    template<typename U>
    T& operator = (const U& rhs)
    {
        Assignment<T, U>::exec(lvalue_, rhs);
        return lvalue_;
    }

private:
    T& lvalue_;
};

ここでAssignmentは次のような構造体テンプレートです。構造体とは言いつつ、欲しいのはexecという関数だけなんですが。関数テンプレートは特殊化ができないのでこうやって構造体テンプレートのメンバとして定義しています。

template<typename T, typename U>
struct Assignment
{
    static T& exec(const U& rhs);
};

LValueを返す関数テンプレートを書きます。

template<typename T>
LValue<T> lvalue(T& lvalue)
{
    return LValue<T>(lvalue);
}

MyClassからHisStructureへ値を移す関数(特殊化した構造体とそのメンバ関数)を書きます。

#include <cstring>
#include <algorithm>

template<>
struct Assignment<HisStructure, MyClass>
{
    static HisStructure& exec(HisStructure& lhs, const MyClass& rhs)
    {
        lhs.length = std::min(rhs.getValue().size(), MAX_LENGTH);
        std::memcpy(lhs.value, rhs.getValue().c_str(), lhs.length);
        return lhs;
    }
};

代入操作です。慣れないとちょっと面妖かもしれません。

    MyClass      myClass("0123456789");
    HisStructure hisStructure = { 0, "" };

    // OK
    lvalue(hisStructure) = myClass;

LValueクラス、lvalue関数、Assignment構造体を事前に用意しておけば、特殊化したAssignment構造体を書くことで代入OKになります。