async_delegate

汎用的に非同期メッセージを投げて戻り値を返してくれるクラスを作りました。


async_delegate::invoke に関数を設定すると、その関数を別スレッドから呼び出します。
async_delegate::invoke の戻り値である async_result_ptr を使って、同期処理を行い、呼び出した関数の戻り値を取得します。
現在は void を返すメソッドには対応していません。
Future パターンを汎用的にした感じですね。
一応 Worker Thread も設定できるようにしています。そっちはテストしてないですが。


使い方としてはこんな感じ

#include <boost/lexical_cast.hpp>
#include <iostream>
#include <string>
#include "async_delegate.h"

// for ::Sleep
#include <windows.h>

class test
{
private:
    async_delegate<int> async_delegate_;
    async_result_ptr<int> result1_;
    async_result_ptr<int> result2_;

    int calc_func(int count)
    {
        std::cout << "BEGIN calc_func: " + boost::lexical_cast<std::string>(count) + "\n";

        int n = 0;
        for (int i = 0; i < count; i++)
        {
            n += 10;
            ::Sleep(100);
        }

        std::cout << "END calc_func: " + boost::lexical_cast<std::string>(count) + "\n";

        return n;
    }

public:
    void pre_calc()
    {
        result1_ = async_delegate_.invoke(boost::bind(&test::calc_func, this, 10));
        result2_ = async_delegate_.invoke(boost::bind(&test::calc_func, this, 20));
    }

    void display()
    {
        std::cout << "display result1_\n";
        std::cout << result1_->get_result() << std::endl;
        std::cout << "display result2_\n";
        std::cout << result2_->get_result() << std::endl;
    }
};

void main()
{
    test t;
    // 先に計算させておく
    t.pre_calc();

    // こっちでいろいろ処理を行う
    ::Sleep(1500);

    // 本当に必要になったので表示してもらう。
    t.display();
}

実行結果

BEGIN calc_func: 10
BEGIN calc_func: 20
END calc_func: 10
display result1_
100
display result2_     // この段階でまだ calc_func(20) は完了していない
END calc_func: 20    // ここで完了する
200                  // 無事に結果を表示することが出来た


以下ソース
async_delegate.h

#ifndef ASYNC_DELEGATE_H_INCLUDED
#define ASYNC_DELEGATE_H_INCLUDED

#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include "async_result.h"

class no_use_thread_pool
{
private:
    boost::thread_group group_;

public:
    ~no_use_thread_pool()
    {
        group_.join_all();
    }

    void invoke(boost::function0<void> function)
    {
        group_.create_thread(function);
    }
};

template<class TResult, class TThreadPool = no_use_thread_pool>
class async_delegate
{
private:
    boost::shared_ptr<TThreadPool> thread_pool_;

public:
    async_delegate() { }
    async_delegate(boost::shared_ptr<TThreadPool> pool) : thread_pool_(pool) { }

    async_result_ptr<TResult> invoke(boost::function0<TResult> function)
    {
        async_result_ptr<TResult> ar(new async_result<TResult>());
        if (!thread_pool_)
        {
            thread_pool_.reset(new TThreadPool());
        }
        thread_pool_->invoke(boost::bind(&async_delegate<TResult>::_invoke, ar, function));
        return ar;
    }

private:
    static void _invoke(async_result_ptr<TResult> ar, boost::function0<TResult> function)
    {
        detail::async_result_setter<TResult>()(ar, function());
    }
};

#endif // ASYNC_DELEGATE_H_INCLUDED

async_result.h

#ifndef ASYNC_RESULT_H_INCLUDED
#define ASYNC_RESULT_H_INCLUDED

#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>

namespace detail
{
    template<class TResult>
    struct async_result_setter;
}

template<class TResult>
class async_result
{
private:
    boost::optional<TResult> result_;
    boost::mutex mutex_;
    boost::condition condition_;

public:
    bool is_completed()
    {
        boost::mutex::scoped_lock lock(mutex_);
        return result_.is_initialized();
    }

    TResult get_result()
    {
        boost::mutex::scoped_lock lock(mutex_);

        while (!result_.is_initialized())
        {
            condition_.wait(lock);
        }
        return boost::get<TResult>(result_);
    }

private:
    void set_result(TResult result)
    {
        boost::mutex::scoped_lock lock(mutex_);

        result_ = result;
        condition_.notify_all();
    }
    friend struct detail::async_result_setter<TResult>;
};

// template<class TResult>
// typedef boost::shared_ptr<async_result<TResult> > async_result_ptr<TResult>;
// ↑こうやって書ければいいのに、この機能がないので↓みたいな書き方になる

// shared_ptr の typedef
template<class T>
class async_result_ptr : public boost::shared_ptr<async_result<T> >
{
private:
    typedef boost::shared_ptr<async_result<T> > base;

public:
    async_result_ptr() : base() { }
    template<class Y>
    explicit async_result_ptr(Y* p) : base(p) { }
    template<class Y, class D>
    async_result_ptr(Y* p, D d) :base(p, d) { }
    template<class Y, class D, class A>
    async_result_ptr(Y* p, D d, A a) : base(p, d, a) { }

#if defined(__BORLANDC__) || defined(__GNUC__)

    boost::shared_ptr<T>& operator=(boost::shared_ptr<T> const & r) // never throws
    {
        return base::operator=(r);
    }

#endif

    template<class Y>
    explicit async_result_ptr(boost::weak_ptr<Y> const & r) : base(r) { }
    template<class Y>
    async_result_ptr(boost::shared_ptr<Y> const & r) : base(r) { }
    template<class Y>
    async_result_ptr(boost::shared_ptr<Y> const & r, boost::detail::static_cast_tag t)
        : base(r, t) { }
    template<class Y>
    async_result_ptr(boost::shared_ptr<Y> const & r, boost::detail::const_cast_tag t)
        : base(r, t) { }
    template<class Y>
    async_result_ptr(boost::shared_ptr<Y> const & r, boost::detail::dynamic_cast_tag t)
        : base(r, t) { }
    template<class Y>
    async_result_ptr(boost::shared_ptr<Y> const & r, boost::detail::polymorphic_cast_tag t)
        : base(r, t) { }

#ifndef BOOST_NO_AUTO_PTR

    template<class Y>
    explicit async_result_ptr(std::auto_ptr<Y> & r) :base(r) { }

#if !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION )

    template<class Ap>
    explicit async_result_ptr(Ap r, typename boost::detail::sp_enable_if_auto_ptr<Ap, int>::type t = 0)
        : base(r, t) { }

#endif // BOOST_NO_SFINAE, BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION

#endif // BOOST_NO_AUTO_PTR

#if !defined(BOOST_MSVC) || (BOOST_MSVC >= 1300)

    template<class Y>
    boost::shared_ptr<T>& operator=(boost::shared_ptr<Y> const & r) // never throws
    {
        return base::operator=(r);
    }

#endif

#ifndef BOOST_NO_AUTO_PTR

    template<class Y>
    boost::shared_ptr<T>& operator=(std::auto_ptr<Y> & r)
    {
        return base::operator=(r);
    }

#if !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION )

    template<class Ap>
    typename boost::detail::sp_enable_if_auto_ptr<Ap, shared_ptr<T>&>::type operator=(Ap r)
    {
        return base::operator=(r);
    }

#endif // BOOST_NO_SFINAE, BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION

#endif // BOOST_NO_AUTO_PTR

};

namespace detail
{
    template<class TResult>
    struct async_result_setter
    {
        void operator()(async_result_ptr<TResult> ar, TResult result)
        {
            ar->set_result(result);
        }
    };
}

#endif // ASYNC_RESULT_H_INCLUDED