Container Wrappers

Container Accessors - Life like a clown で書いたコードでは,メンバ変数を初期化する前に基底クラスを(初期化前の)メンバ変数で初期化しているので問題が発生することがあるようです.改善方法ですが,More C++ Idioms/メンバによる基本クラスの初期化(Base-from-Member) - Wikibooks において,基底クラスを派生クラスのメンバ変数で初期化する方法が紹介されていたので,この方法を用いる事とします.

Container Generator

まず,以下のような STL コンテナのインスタンスを生成するためのクラスを作成します.

namespace clx {
    template <class Type>
    class container_generator;
    
    template <class Type>
    class comparable_container_generator;
}

container_generator は,生成する STL コンテナが std::vector, std::deque, std::list の場合に使用します.生成する STL コンテナが std::set, std::multiset, std::map, std::multimap の場合には代わりに comparable_container_generator を使用します.

コンストラクタは,それぞれの STL コンテナ・クラスで定義されているコンストラクタと同様のものが定義されているので,必要なものを用いて初期化を行います.生成された(STL コンテナの)インスタンスは,member と言う名前の変数で定義してあるので,生成されたインスタンスへアクセスしたい場合には member と記述します.

この container_generator を使用して書き変えたサンプル・プログラムは以下のようになります.

#include <iostream>
#include <string>
#include <map>
#include "clx/container_generator.h"
#include "clx/container_accessor.h"

/* ------------------------------------------------------------------------- */
//  exmap
/* ------------------------------------------------------------------------- */
class exmap :
    private clx::comparable_container_generator<std::map<int, std::string> >,
    public clx::map_accessor<std::map<int, std::string> > {
private:
    typedef clx::comparable_container_generator<std::map<int, std::string> > generator;
    typedef clx::map_accessor<std::map<int, std::string> > accessor;
    
public:
    exmap() :
        generator(), accessor(member) {}
    
    virtual ~exmap() throw() {}
    
    void do_something() {
        member[0] = "Hello, world!";
        member[1] = "foo";
        member[2] = "hoge";
        member[3] = "Bye bye.";
    }
};

/* ------------------------------------------------------------------------- */
//  main
/* ------------------------------------------------------------------------- */
int main(int argc, char* argv[]) {
    exmap v;
    v.do_something();
    
    // std::map が持っているメソッドを使用する.
    std::cout << "size: " << v.size() << std::endl;
    std::cout << "--" << std::endl;
    for (exmap::iterator pos = v.begin(); pos != v.end(); ++pos) {
        std::cout << pos->first << ": " << pos->second << std::endl;
    }
    std::cout << std::endl;
    
    if (v.find(0) != v.end()) {
        std::cout << "find: " << v[0] << std::endl;
    }
    std::cout << std::endl;
    
    return 0;
}

Container Wrapper

最後に,ユーザのコード記述量を減らすために,各 STL コンテナ・クラスに対応するラッパクラスを定義しておきます.現在,以下のものを定義しています.

    template <class Type, class Alloc = std::allocator<Type> >
    class vector_wrapper;
    
    template <class Type, class Alloc = std::allocator<Type> >
    class deque_wrapper;
    
    template <class Type, class Alloc = std::allocator<Type> >
    class list_wrapper;
    
    template <class Key, class Compare = std::less<Key>, class Alloc = std::allocator<Key> >
    class set_wrapper;
    
    template <class Key, class Compare = std::less<Key>, class Alloc = std::allocator<Key> >
    class multiset_wrapper;
    
    template <
        class Key, class Type,
        class Compare = std::less<Key>, class Alloc = std::allocator<Key>
    >
    class map_wrapper;
    
    template <
        class Key, class Type,
        class Compare = std::less<Key>, class Alloc = std::allocator<Key>
    >
    class multimap_wrapper;

この Container Wrapper クラスを使用して,先のサンプル・プログラムを書き換えたものは以下のようになります(main() はまったく同じなので省略).

#include <iostream>
#include <string>
#include <map>
#include "clx/container_wrapper.h"

/* ------------------------------------------------------------------------- */
//  exmap
/* ------------------------------------------------------------------------- */
class exmap : public clx::map_wrapper<int, std::string> {
private:
public:
    exmap() : clx::map_wrapper<int, std::string>() {}
    
    virtual ~exmap() throw() {}
    
    void do_something() {
        member[0] = "Hello, world!";
        member[1] = "foo";
        member[2] = "hoge";
        member[3] = "Bye bye.";
    }
};