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

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

メソッドホルダ・失敗篇

Delphiでは、戻り値の型と引数の数と型さえあっていればどんなオブジェクトのメソッドも格納できるメソッドホルダ(と言うんだったかな?)がありますが、これをC++で実践しようとした結果。


一種類で100行超えるとはどういうこったい。なにかやり方を間違えてる、たぶん。
ダメ、やり直し。


一応貼っておく。以下コード。

#ifndef EMATTSAN_METHODHOLDER_H
#define EMATTSAN_METHODHOLDER_H

#include <algorithm>
#include <stdexcept>

class MethodNotHeld : public std::logic_error
{
public:
    MethodNotHeld() : std::logic_error("method has not been held") {}
};

template<typename T>
struct MethodHolder;

template<typename T>
class Method;

template<typename RESULT>
struct MethodHolder<RESULT ()>
{
    typedef RESULT result_t;

    virtual ~MethodHolder() {}

    virtual result_t exec() = 0;
};

template<typename RESULT>
class Method<RESULT ()>
{
public:
    typedef RESULT                    result_t;
    typedef MethodHolder<result_t ()> MethodHolder;

    Method() : holder_(0)
    {
    }

    Method(const Method& method) : holder_(0)
    {
        if(method.holder_ != 0)
        {
            holder_ = method.holder_->clone();
        }
    }

    template<typename T>
    Method(T& object, result_t (T::*method)()) : holder_(0)
    {
        holder_ = createMethodHolder(object, method);
    }

    ~Method()
    {
        delete holder_;
    }

    Method& operator = (const Method& other)
    {
        Method method(other);
        std::swap(holder_, method.holder_);
        return *this;
    }

    template<typename T>
    void set(T& object, result_t (T::*method)())
    {
        MethodHolder* newHolder = createMethodHolder(object, method);
        delete holder_;
        holder_ = newHolder;
    }

    void clear()
    {
        delete holder_;
        holder_ = 0;
    }

    bool isValid() const
    {
        return holder_ != 0;
    }

    result_t operator () ()
    {
        if(holder_ == 0)
        {
            throw MethodNotHeld();
        }
        return holder_->exec();
    }

private:
    template<typename T>
    MethodHolder* createMethodHolder(T& object, result_t (T::*method)())
    {
        struct MethodHolderImpl : public MethodHolder
        {
            typedef T object_t;
            typedef result_t (object_t::*method_t)();

            MethodHolderImpl(object_t& object, method_t method) : object(object), method(method) {}
            
            object_t& object;
            method_t  method;

            result_t exec()
            {
                return (object.*method)();
            }
        };

        return new MethodHolderImpl(object, method);
    };

    MethodHolder* holder_;
};

#endif//EMATTSAN_METHODHOLDER_H
#include "MethodHolder.h"

#include <iostream>

struct Foo
{
    int get10() { return 10; }
};

struct Bar
{
    int get20() { return 20; }
};


int main(int, char* [])
{
    Foo foo;
    Bar bar;

    Method<int()> method1(foo, &Foo::get10);

    std::cout << method1() << std::endl;

    method1.set(bar, &Bar::get20);

    std::cout << method1() << std::endl;

    Method<int()> method2;

    method2.set(bar, &Bar::get20);

    std::cout << method2() << std::endl;

    method2.clear();

    try
    {
        std::cout << method2() << std::endl;
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}


実行結果

10
20
20
method has not been held


実はBoost Libraryでできるんだけどね。

#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>

struct Foo
{
    int get10() { return 10; }
};

struct Bar
{
    int get20() { return 20; }
};


int main(int, char* [])
{
    Foo foo;
    Bar bar;

    boost::function<int ()> f1(boost::bind(&Foo::get10, foo));

    std::cout << f1() << std::endl;

    f1 = boost::bind(&Bar::get20, bar);

    std::cout << f1() << std::endl;

    boost::function<int ()> f2;

    f2 = boost::bind(&Bar::get20, bar);

    std::cout << f2() << std::endl;;

    f2.clear();

    try
    {
        std::cout << f2() << std::endl;
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}


実行結果。

10
20
20
call to empty boost::function