none
クラスメンバー変数を比較・マージ RRS feed

  • 質問

  • お世話になります。


    あるクラス(or 構造体)が100種類程度あります。

    このうち、ある特定のクラスのインスタンスが3つ存在していて、それぞれinstA,instB,instCと呼ぶこととします。
    instAとinstBの全てのメンバー変数をそれぞれ比較し、instAと異なるinstBのメンバー変数だけをinstCに代入させたいです。

    クラスの定義を

    class MyClass
    {
    public:
        int member1;
        char member2;
        bool member3[10];
    }

    とすると、通常であれば、クラス内にメソッドを追加し、

    if (instA.member1 != instB.member1)
    {
        instC.member1 = instB.member1;
    }
    if (instA.member2 != instB.member2)
    {
        instC.member2 = instB.member2;
    }
    if (memcmp(instA.member3, instB.member3, sizeof(instA.member3)) != 0)
    {
        instC.member3 = instB.member3;
    }

    というようなコードを記述すれば良いと思いますが、このクラスは今後定義がアップデートされる可能性が高く、また同様な処理を大量なクラスに適用したいため、なんとかスマートにコーディングできないものかと思案しています。

    クラス内のメソッドとして上記のコードを実装する以外になにか有効な解決方法が考えられるでしょうか。
    ちなみにクラス自体に自分が定義しているメンバー変数の数を格納するメンバー変数を追加することは可能です。

    よろしくお願いいたします。



    2011年5月19日 5:05

回答

  • 慣れてないけど書いてみました。とりあえず int、char、bool[10]をサポートできそうなものを書きました。

    まずmember.hには次のように書いていきます。セミコロンを書かないのがミソです。これをデータベースのように使っていきます。

    MEMBER(int, member1)
    MEMBER(char, member2)
    MEMBER(bool10, member3)

    次にMyClass.hですが

    typedef bool bool10[10];
    class MyClass{
    public:
    #define MEMBER(TYPE, NAME) TYPE NAME;
    #include "member.h"
    #undef MEMBER
    };

    とします。copy関数はtype_traitsを使ってがんばって書きます。cbr600rrさんは「boostの」と書かれていますがVisual Studio 2008 SP1から標準で含まれています。

    #include <cstring>
    #include <type_traits>

    template<typename Type, bool Bool>
    void copy( Type& dst, const Type& src, const Type& ref, const std::integral_constant<bool, Bool>& ){
      if( src != ref )
        dst = src;
    }

    template<typename Type, size_t Size>
    void copy( Type (&dst)[Size], const Type (&src)[Size], const Type (&ref)[Size], const std::true_type& ){
      if( std::memcmp( src, ref, Size * sizeof(Type) ) )
        std::memcpy( dst, src, Size * sizeof(Type) );
    }

    void copy( MyClass& dst, const MyClass& src, const MyClass& ref ){
    #define MEMBER(TYPE, NAME) copy( dst.NAME, src.NAME, ref.NAME, std::is_array<TYPE>() );
    #include "member.h"
    #undef MEMBER
    }
    こうしておけば、メンバーを増やしたいときはmember.hに行を増やせばいいです。bool10はもっといい書き方があるのかもしれませんが、C++初心者なので…。

    • 回答としてマーク Askie 2011年5月20日 10:19
    2011年5月19日 10:57
  • type_traitsを使ってコンパイラにコピー方法を判断させましたが、人力で簡単に振り分けることもできます。

    member.h:

    MEMBER(int, member1)
    MEMBER(char, member2)
    MEMBERA(bool, member3, [10])

    としてMEMBER()とMEMBERA()に分けてあれば、それに合わせた#defineをするだけで済みます。

    #include <string.h>

    struct MyStruct{
    #define MEMBER(TYPE, NAME) TYPE NAME;
    #define MEMBERA(TYPE, NAME, RANK) TYPE NAME RANK;
    #include "member.h"
    #undef MEMBER
    #undef MEMBERA
    };

    void copy( struct MyStruct* dst, const struct MyStruct* src, const struct MyStruct* ref ){
    #define MEMBER(TYPE, NAME) \
      if( src->NAME != ref->NAME ) \
        dst->NAME = src->NAME;
    #define MEMBERA(TYPE, NAME, RANK) \
      if( memcmp( src->NAME, ref->NAME, sizeof(TYPE RANK) ) ) \
        memcpy( dst->NAME, src->NAME, sizeof(TYPE RANK) );
    #include "member.h"
    #undef MEMBER
    #undef MEMBERA
    }

    sizeof(TYPE RANK) は sizeof(bool [10]) に展開され正しくサイズも取得できるかと。

    方法はいろいろあると思うので、都合のいいように#defineしていくといいでしょう。

    • 回答としてマーク Askie 2011年5月20日 10:19
    2011年5月19日 12:00
  • VisualStudio2010でstd::tupleを使って実装してみました。

    説明は末尾に。

     

    #include <tuple>
    #include <array>
    
    namespace cbr
    {
     typedef std::tr1::_Nil Nil;
    
     template <typename T>
     void copyIfDifferent(T& dst, T& src)
     {
      if(dst != src)
      {
       dst = src;
      }
     }
    
     template <typename A0>
     bool copyDifferences(std::tuple<A0, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil>& dst,
           const std::tuple<A0, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
     }
    
     template <typename A0, typename A1>
     bool copyDifferences(std::tuple<A0, A1, Nil, Nil, Nil, Nil, Nil, Nil, Nil>& dst,
           const std::tuple<A0, A1, Nil, Nil, Nil, Nil, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
     }
    
     template <typename A0, typename A1, typename A2>
     bool copyDifferences(std::tuple<A0, A1, A2, Nil, Nil, Nil, Nil, Nil, Nil>& dst,
           const std::tuple<A0, A1, A2, Nil, Nil, Nil, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, Nil, Nil, Nil, Nil, Nil>& dst,
           const std::tuple<A0, A1, A2, A3, Nil, Nil, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3, typename A4>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, A4, Nil, Nil, Nil, Nil>& dst,
           const std::tuple<A0, A1, A2, A3, A4, Nil, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
      copyIfDifferent(std::get<4>(dst), std::get<4>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, A4, A5, Nil, Nil, Nil>& dst,
           const std::tuple<A0, A1, A2, A3, A4, A5, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
      copyIfDifferent(std::get<4>(dst), std::get<4>(src));
      copyIfDifferent(std::get<5>(dst), std::get<5>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, A4, A5, A6, Nil, Nil>& dst,
         const std::tuple<A0, A1, A2, A3, A4, A5, A6, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
      copyIfDifferent(std::get<4>(dst), std::get<4>(src));
      copyIfDifferent(std::get<5>(dst), std::get<5>(src));
      copyIfDifferent(std::get<6>(dst), std::get<6>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, Nil>& dst,
           const std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
      copyIfDifferent(std::get<4>(dst), std::get<4>(src));
      copyIfDifferent(std::get<5>(dst), std::get<5>(src));
      copyIfDifferent(std::get<6>(dst), std::get<6>(src));
      copyIfDifferent(std::get<7>(dst), std::get<7>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, A8>& dst,
           const std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, A8>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
      copyIfDifferent(std::get<4>(dst), std::get<4>(src));
      copyIfDifferent(std::get<5>(dst), std::get<5>(src));
      copyIfDifferent(std::get<6>(dst), std::get<6>(src));
      copyIfDifferent(std::get<7>(dst), std::get<7>(src));
      copyIfDifferent(std::get<8>(dst), std::get<8>(src));
     }
    
     template <typename Tuple, size_t INDEX>
     class TupleProperty
     {
     public:
      typedef typename std::tuple_element<INDEX, Tuple>::type Value;
    
      TupleProperty(Tuple& tuple)
       : m_tuple(tuple)
      {
      }
    
      operator Value&()
      {
       return std::get<INDEX>(this->m_tuple);
      }
    
      operator const Value&() const
      {
       return std::get<INDEX>(this->m_tuple);
      }
    
      TupleProperty& operator=(const Value& rhs)
      {
       std::get<INDEX>(this->m_tuple) = rhs;
       return *this;
      }
    
     private:
      Tuple& m_tuple;
    
      TupleProperty(const TupleProperty&);
      TupleProperty& operator=(const TupleProperty&);
     };
    }
    
    class Hoge
    {
    private:
     typedef std::tuple<int, char, std::array<bool, 10>> Tuple;
     Tuple       m_members;
     cbr::TupleProperty<Tuple, 0> m_member1; // int
     cbr::TupleProperty<Tuple, 1> m_member2; // char
     cbr::TupleProperty<Tuple, 2> m_member3; // bool[10]
    
    public:
     Hoge()
      : m_members()
      , m_member1(m_members)
      , m_member2(m_members)
      , m_member3(m_members)
     {
     }
    
     void copyDifferences(const Hoge& source) // operator=()か?
     {
      if(this != &source)
      {
       cbr::copyDifferences(m_members, source.m_members);
      }
     }
    
     void doSomething(int)
     {
     }
    
     void doSomething()
     {
      m_member1 = 100;
      doSomething(m_member1);
      ::printf("%d\n", (int)m_member1);
     }
    };
    
    

     

     

    メンバ変数の値はすべてstd::tupleに格納します。

     

    とりあえず「違いのある箇所だけコピー」をするため、std::tupleのすべての部分特殊化にてその機能を実装しておきます。

    それがcopyIfDifferent()とcopyDifferences()です。

    #可変個引数templateができると、もっと楽になるんですかね・・・。

     

    TuplePropertyは、各変数領域に識別子をつけるために試しに用意してみました。たとえばHogeの最初のメンバ変数(int)にm_member1という変数名がついているように見える、ということを狙っています。


    • 回答としてマーク Askie 2011年5月20日 10:19
    2011年5月20日 7:48

すべての返信

  • 普通に外部関数でいいじゃん・・・て思うのですが、いかがでしょう(vv;)。

    class MyCLass; // 宣言のみ
    BOOL // TRUE:差分クラスを取得できた
    Diff_MyClass(
      MyClass *  ot_Diff,    // 差分結果
      const MyClass &  ex_A, // 比較対照A
      const MyClass &  ex_B) // 比較対照B
    {
       // AとBを比較する
       if(・・・・){
         return TRUE; // 差分を取得した
       }
       return FALSE;  // 差異は無かった
    }

    class MyClass{
      friend BOOL Diff_MyClass(...);
    };

    なんかのoperator をオーバーライドする手もあるけど、わかりづらいしですね(vv;)。

    2011年5月19日 7:26
  • boostのtype traits系で、型にメンバ変数がいくつあるか調べたり、メンバ変数にアクセスする機能がありませんでしたっけ?

    それを使ったらわりと簡単に書けそうな気がしますが・・・・。

    2011年5月19日 8:59
  • >仲澤@失業者 さん

    質問の意図が分かりづらくて申し訳ありません。その手法ですと結局 //AとBを比較する の部分を

    クラス毎に個別に記述しなくてはいけない、かつ、クラスのメンバーに変更があった場合にその部分も

    見直す必要が出てきます。

    これをうまく解決できないかなぁというのが質問の意図です。

     

    >cbr600rr さん

    なるほど。boostにはそんなライブラリまでありましたか。

    てっきり.NETのフレクション機能のようなものは実現できないものとばかり思っていました。

    ただ、こちらの制約によりboostを使ってはいけないことになっていまして(TT

    個人的にはtype traitsについても調べてみたいと思います。

     

    2011年5月19日 10:41
  • 今1.46.1のドキュメントを見ましたが、少なくともtype traitsにはありませんでした。すみません。

     

    VisualStudioで利用できるtype traitsは

    http://msdn.microsoft.com/en-us/library/ms177194.aspx

    に記載がありますが、ここにも当該機能はないですね。申し訳ないです。

    2011年5月19日 10:57
  • 慣れてないけど書いてみました。とりあえず int、char、bool[10]をサポートできそうなものを書きました。

    まずmember.hには次のように書いていきます。セミコロンを書かないのがミソです。これをデータベースのように使っていきます。

    MEMBER(int, member1)
    MEMBER(char, member2)
    MEMBER(bool10, member3)

    次にMyClass.hですが

    typedef bool bool10[10];
    class MyClass{
    public:
    #define MEMBER(TYPE, NAME) TYPE NAME;
    #include "member.h"
    #undef MEMBER
    };

    とします。copy関数はtype_traitsを使ってがんばって書きます。cbr600rrさんは「boostの」と書かれていますがVisual Studio 2008 SP1から標準で含まれています。

    #include <cstring>
    #include <type_traits>

    template<typename Type, bool Bool>
    void copy( Type& dst, const Type& src, const Type& ref, const std::integral_constant<bool, Bool>& ){
      if( src != ref )
        dst = src;
    }

    template<typename Type, size_t Size>
    void copy( Type (&dst)[Size], const Type (&src)[Size], const Type (&ref)[Size], const std::true_type& ){
      if( std::memcmp( src, ref, Size * sizeof(Type) ) )
        std::memcpy( dst, src, Size * sizeof(Type) );
    }

    void copy( MyClass& dst, const MyClass& src, const MyClass& ref ){
    #define MEMBER(TYPE, NAME) copy( dst.NAME, src.NAME, ref.NAME, std::is_array<TYPE>() );
    #include "member.h"
    #undef MEMBER
    }
    こうしておけば、メンバーを増やしたいときはmember.hに行を増やせばいいです。bool10はもっといい書き方があるのかもしれませんが、C++初心者なので…。

    • 回答としてマーク Askie 2011年5月20日 10:19
    2011年5月19日 10:57
  • type_traitsを使ってコンパイラにコピー方法を判断させましたが、人力で簡単に振り分けることもできます。

    member.h:

    MEMBER(int, member1)
    MEMBER(char, member2)
    MEMBERA(bool, member3, [10])

    としてMEMBER()とMEMBERA()に分けてあれば、それに合わせた#defineをするだけで済みます。

    #include <string.h>

    struct MyStruct{
    #define MEMBER(TYPE, NAME) TYPE NAME;
    #define MEMBERA(TYPE, NAME, RANK) TYPE NAME RANK;
    #include "member.h"
    #undef MEMBER
    #undef MEMBERA
    };

    void copy( struct MyStruct* dst, const struct MyStruct* src, const struct MyStruct* ref ){
    #define MEMBER(TYPE, NAME) \
      if( src->NAME != ref->NAME ) \
        dst->NAME = src->NAME;
    #define MEMBERA(TYPE, NAME, RANK) \
      if( memcmp( src->NAME, ref->NAME, sizeof(TYPE RANK) ) ) \
        memcpy( dst->NAME, src->NAME, sizeof(TYPE RANK) );
    #include "member.h"
    #undef MEMBER
    #undef MEMBERA
    }

    sizeof(TYPE RANK) は sizeof(bool [10]) に展開され正しくサイズも取得できるかと。

    方法はいろいろあると思うので、都合のいいように#defineしていくといいでしょう。

    • 回答としてマーク Askie 2011年5月20日 10:19
    2011年5月19日 12:00
  • そうなると、変数の、先頭メンバ変数からのオフセットアドレス(offsetof)と、
    検査するBYTE数(各メンバのsizeof)のセットを構造にして、配列にし
    メンバ数の分だけ登録しておき、検査関数はエントリされた上記の
    全ての要素についてmemcmp()する方法がすぐに思いつきますが、
    結構負担がかかりそうですね。
    2011年5月19日 12:09
  • そうなると、変数の、先頭メンバ変数からのオフセットアドレス(offsetof)と、
    検査するBYTE数(各メンバのsizeof)のセットを構造にして、配列にし
    メンバ数の分だけ登録しておき、検査関数はエントリされた上記の
    全ての要素についてmemcmp()する方法がすぐに思いつきますが、
    結構負担がかかりそうですね。

    これはC++ではやってはいけません。

     

    C++ですと比較演算子operator==()やコピー演算子operator=()がオーバーロードされている可能性があります。

    したがってmemcmp()ではなく、それぞれの型のoperator==()にて比較をかける必要があります。コピーも同様にmemcpy()は使ってはいけません。

     

    C++コンパイラは各型のメンバ変数の構成を親クラスも含めて知っているはずなので、type traits系のサポートをしてくれれば、あとはtemplateの再帰でなんとかできる、みたいな議論をどっかで見たことあります。

    2011年5月20日 2:44
  • ちょっとお聞きしたいのですが、

    ・開発環境はなんでしょうか。VisualStudio2010?2008?

    ・1クラスあたりのメンバ変数の個数は、最大何個くらいになりますか。

    ・メンバ変数として想定されるものの型は、具体的にはどのようなものになりますか。単純値型やその配列のみですか。

    ・テンプレートを用いてもOKですか。

     

    VisualStudio2010でメンバ変数の個数が9個までなら、std::tupleを使えば楽になりそうな気がします。その代わりメンバ変数への識別子によるアクセスが犠牲になりますが。


    2011年5月20日 4:24
  • VisualStudio2010でstd::tupleを使って実装してみました。

    説明は末尾に。

     

    #include <tuple>
    #include <array>
    
    namespace cbr
    {
     typedef std::tr1::_Nil Nil;
    
     template <typename T>
     void copyIfDifferent(T& dst, T& src)
     {
      if(dst != src)
      {
       dst = src;
      }
     }
    
     template <typename A0>
     bool copyDifferences(std::tuple<A0, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil>& dst,
           const std::tuple<A0, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
     }
    
     template <typename A0, typename A1>
     bool copyDifferences(std::tuple<A0, A1, Nil, Nil, Nil, Nil, Nil, Nil, Nil>& dst,
           const std::tuple<A0, A1, Nil, Nil, Nil, Nil, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
     }
    
     template <typename A0, typename A1, typename A2>
     bool copyDifferences(std::tuple<A0, A1, A2, Nil, Nil, Nil, Nil, Nil, Nil>& dst,
           const std::tuple<A0, A1, A2, Nil, Nil, Nil, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, Nil, Nil, Nil, Nil, Nil>& dst,
           const std::tuple<A0, A1, A2, A3, Nil, Nil, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3, typename A4>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, A4, Nil, Nil, Nil, Nil>& dst,
           const std::tuple<A0, A1, A2, A3, A4, Nil, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
      copyIfDifferent(std::get<4>(dst), std::get<4>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, A4, A5, Nil, Nil, Nil>& dst,
           const std::tuple<A0, A1, A2, A3, A4, A5, Nil, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
      copyIfDifferent(std::get<4>(dst), std::get<4>(src));
      copyIfDifferent(std::get<5>(dst), std::get<5>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, A4, A5, A6, Nil, Nil>& dst,
         const std::tuple<A0, A1, A2, A3, A4, A5, A6, Nil, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
      copyIfDifferent(std::get<4>(dst), std::get<4>(src));
      copyIfDifferent(std::get<5>(dst), std::get<5>(src));
      copyIfDifferent(std::get<6>(dst), std::get<6>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, Nil>& dst,
           const std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, Nil>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
      copyIfDifferent(std::get<4>(dst), std::get<4>(src));
      copyIfDifferent(std::get<5>(dst), std::get<5>(src));
      copyIfDifferent(std::get<6>(dst), std::get<6>(src));
      copyIfDifferent(std::get<7>(dst), std::get<7>(src));
     }
    
     template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
     bool copyDifferences(std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, A8>& dst,
           const std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, A8>& src)
     {
      copyIfDifferent(std::get<0>(dst), std::get<0>(src));
      copyIfDifferent(std::get<1>(dst), std::get<1>(src));
      copyIfDifferent(std::get<2>(dst), std::get<2>(src));
      copyIfDifferent(std::get<3>(dst), std::get<3>(src));
      copyIfDifferent(std::get<4>(dst), std::get<4>(src));
      copyIfDifferent(std::get<5>(dst), std::get<5>(src));
      copyIfDifferent(std::get<6>(dst), std::get<6>(src));
      copyIfDifferent(std::get<7>(dst), std::get<7>(src));
      copyIfDifferent(std::get<8>(dst), std::get<8>(src));
     }
    
     template <typename Tuple, size_t INDEX>
     class TupleProperty
     {
     public:
      typedef typename std::tuple_element<INDEX, Tuple>::type Value;
    
      TupleProperty(Tuple& tuple)
       : m_tuple(tuple)
      {
      }
    
      operator Value&()
      {
       return std::get<INDEX>(this->m_tuple);
      }
    
      operator const Value&() const
      {
       return std::get<INDEX>(this->m_tuple);
      }
    
      TupleProperty& operator=(const Value& rhs)
      {
       std::get<INDEX>(this->m_tuple) = rhs;
       return *this;
      }
    
     private:
      Tuple& m_tuple;
    
      TupleProperty(const TupleProperty&);
      TupleProperty& operator=(const TupleProperty&);
     };
    }
    
    class Hoge
    {
    private:
     typedef std::tuple<int, char, std::array<bool, 10>> Tuple;
     Tuple       m_members;
     cbr::TupleProperty<Tuple, 0> m_member1; // int
     cbr::TupleProperty<Tuple, 1> m_member2; // char
     cbr::TupleProperty<Tuple, 2> m_member3; // bool[10]
    
    public:
     Hoge()
      : m_members()
      , m_member1(m_members)
      , m_member2(m_members)
      , m_member3(m_members)
     {
     }
    
     void copyDifferences(const Hoge& source) // operator=()か?
     {
      if(this != &source)
      {
       cbr::copyDifferences(m_members, source.m_members);
      }
     }
    
     void doSomething(int)
     {
     }
    
     void doSomething()
     {
      m_member1 = 100;
      doSomething(m_member1);
      ::printf("%d\n", (int)m_member1);
     }
    };
    
    

     

     

    メンバ変数の値はすべてstd::tupleに格納します。

     

    とりあえず「違いのある箇所だけコピー」をするため、std::tupleのすべての部分特殊化にてその機能を実装しておきます。

    それがcopyIfDifferent()とcopyDifferences()です。

    #可変個引数templateができると、もっと楽になるんですかね・・・。

     

    TuplePropertyは、各変数領域に識別子をつけるために試しに用意してみました。たとえばHogeの最初のメンバ変数(int)にm_member1という変数名がついているように見える、ということを狙っています。


    • 回答としてマーク Askie 2011年5月20日 10:19
    2011年5月20日 7:48
  • 開発環境としてはVC6でもビルドできることが条件となっています。
    また、各クラスのメンバー数は100以上のものもありメンバーの型はプリミティブ型かその配列のみです。

    現在は質問に書きました各クラスについてそれぞれのメンバーを比較し、違いがあったら代入するというようなメソッドを全クラスに書いていくことになりました。
    ただ、記述方法に法則がありますので各クラスのメンバーを読んで上記メソッドを出力するようなプログラムを別に作成する予定です。


    それはそれとしまして、BoostやC++0xを用いた手法も個人的には興味があるので○○だったらこんな方法があるよということがありましたらアドバイスいただきたいです。

    私も少しBoostについて調べたところBoost.FusionのBOOST_FUSION_ADAPT_STRUCTマクロでユーザー定義型をアダプトするとFusionのコンテナとして利用できるようなので、boost::fusion::for_each()で各メンバーを列挙したりできるようです。

    2011年5月20日 10:09
  • ありがとうございます。

    プリプロセッサマクロでここまでできるんですね。驚きです。

    展開方法を変化させるというのは思いつきませんでした。

    2011年5月20日 10:19
  • ありがとうございます。

    メンバー変数が9個以内ならタプルで定義することで可能ということですね。

    そもそもタプルが10個以上の引数をとれないことを初めて知りました;

    ということはBoost.FusionのBOOST_FUSION_ADAPT_STRUCTマクロもユーザー定義型のメンバー数が10個以上は使えなさそうですね。


    2011年5月20日 10:40