具有多个构造函数签名的C ++通用工厂? [英] C++ generic factory with multiple constructor signatures?

查看:78
本文介绍了具有多个构造函数签名的C ++通用工厂?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

任何人都曾经结合过Andrei Alexandrescu的经典通用工厂( 现代C ++设计)中的第8章), Boost的多功能功能.TypeErasure ?也就是说,可以灵活地创建多个创建者函数签名,这些签名随参数的数量和类型而变化(但仍具有相同的返回类型,并且在编译时是已知的)。

Has anyone ever combined the classic generic factory by Andrei Alexandrescu (page 208 of Chapter 8 in Modern C++ Design) with the 'multifunction' capabilities of Boost.TypeErasure? That is, the flexibility to have several creator function signatures that vary with respect to number and type of parameters (but still have the same return type and are known at compile time).

换句话说,如何结合这个稍微简化的通用工厂:

In other words, how to combine this slightly simplified generic Factory:

#include <map>
#include <utility>
#include <stdexcept>

template <class AbstractProduct, typename IdentifierType, typename ProductCreator>
class Factory
{
public:
    bool Register(const IdentifierType& id, ProductCreator creator) {
        return associations_.emplace(id, creator).second;
    }

    bool Unregister(const IdentifierType& id) {
        return associations_.erase(id) == 1;
    }

    template <typename... Arguments>
    AbstractProduct CreateObject(const IdentifierType& id, Arguments&& ... args) {
        auto i = associations_.find(id);
        if (i != associations_.end()) {
            return (i->second)(std::forward<Arguments>(args)...);
        }
        throw std::runtime_error("Creator not found.");
    }

private:
    std::map<IdentifierType, ProductCreator> associations_;
};

具有此(不完整)功能类型的擦除模式:

with this (incomplete) function type erasure 'pattern':

#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/builtin.hpp>
#include <boost/type_erasure/callable.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/variant.hpp>    

template<class... Sig>
using multifunction = any< mpl::vector< copy_constructible<>, typeid_<>, relaxed, callable<Sig>... > >;
using variant_type = boost::make_recursive_variant< void, double, ... >::type;
using function_type = multifunction<AbstractProduct(void), AbstractProduct(double), AbstractProduct(double, double)>;

class variant_handler
{
public:
    void handle(const variant_type& arg) {
        boost::apply_visitor(impl, arg);
    }
    void set_handler(function_type f) {
        impl.f = f;
    }
private:
    struct dispatcher : boost::static_visitor<void>
    {
        template<class T>
        void operator()(const T& t) { f(t); }
        // For a vector, we recursively operate on the elements
        void operator()(const vector_type& v)
        {
            boost::for_each(v, boost::apply_visitor(*this));
        }
        function_type f;
    };
    dispatcher impl;
};

这样最终人们可以像这样使用它:

So that ultimately one can use it like:

Factory<Arity*, int, ???> factory;
factory.Register(0, boost::bind( boost::factory<Nullary *>() ));
factory.Register(1, boost::bind( boost::factory<Unary *>(), _1 ));
auto x = factory.CreateObject(0);
auto y = factory.CreateObject(1, 0.5);

我没有在野外找到现有的实现,我目前仍在尝试做到这一点。我的第一次尝试犯了一个错误,试图将 boost :: bind()的结果存储在 function_type 中对这个 SO问题产生了相同的错误。我怀疑答案需要将 ProductCreator 模板参数移至 Register 函数并在其中进行操作。

I haven't found an existing implementation in the wild, and I am currently stuck in my own attempt to make it. My first attempt made the mistake of trying to store the result of boost::bind() in the function_type, which resulted the same error to this SO question. I suspect the answer will require moving the ProductCreator template parameter to the Register function and doing something there.

所以我想我最终是在寻找通用多功能工厂的完整,可行的实现,该工厂可能已经存在,而我只是忽略了它。

So I guess I am ultimately looking for a full, working implementation of a generic multifunction factory, which may already exist and I just overlooked it. But any help with just getting it together would be really appreciated.

我更喜欢C ++ 11解决方案,但是显然C ++ 14比没有解决方案好,等等。 。

I would prefer a C++11 solution, but obviously C++14 is better than none, etc.

在此先感谢您的帮助!

推荐答案

Hallelujah,我找到了使用Boost.Variant的解决方案,但没有类型擦除。我认为这比我以前的答案要好得多,因为:

Hallelujah, I found a solution using Boost.Variant but no type erasure. I think this is much better than my earlier answer, as:


  • 创建者ID是唯一的。

  • CreateObject 支持将参数隐式转换为构造函数。

  • Creator id is unique.
  • CreateObject supports implicit conversion of parameters to constructor.

构造函数必须使用 const& 参数。

The same limitation that the constructors must take const& parameters exists.

我已经简化了总体设计,以关注基本行为。缺少的是错误处理和可配置关联容器类型的策略,该策略应该是附加的类模板参数。我还留下了一些最少的调试代码,以便您自己进行测试即可看到它的工作原理。

I have simplified the overall design somewhat to focus on the essential behaviour. What's missing is the policy for error handling and configurable associative container type, which should be additional class template parameters. I have also left some minimal debugging code in so that you can see for yourself that it works when you test it out.

#include <boost/functional/factory.hpp>
#include <boost/function.hpp>
#include <boost/variant.hpp>

#include <map>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
// Just for debugging.
#include <cassert>
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>

// Tuple manipulation.

template <typename Signature>
struct signature_impl;

template <typename ReturnType, typename... Args>
struct signature_impl<ReturnType(Args...)>
{
    using return_type = ReturnType;
    using param_types = std::tuple<Args...>;
};

template <typename T>
using signature_t = signature_impl<T>;


template <std::size_t... Ints>
struct indices {};

template <std::size_t N, std::size_t... Ints>
struct build_indices : build_indices<N-1, N-1, Ints...> {};

template <std::size_t... Ints>
struct build_indices<0, Ints...> : indices<Ints...> {};

template <typename Tuple>
using make_tuple_indices = build_indices<std::tuple_size<typename std::remove_reference<Tuple>::type>::value>;

// The multiple-signature factory.
template <class AbstractProduct, typename IdentifierType, typename... ProductCreators>
class multifactory
{
    using functions = boost::variant<boost::function<ProductCreators>...>;

    std::map<IdentifierType, functions> associations_;

    template <typename Signature>
    struct dispatch_foo
    {
        template <typename CreateArgs, std::size_t... Indices>
        typename std::enable_if<std::is_convertible<CreateArgs, typename signature_t<Signature>::param_types>::value, AbstractProduct>::type
        static apply(boost::function<Signature> const &f, CreateArgs && t, indices<Indices...>)
        {
            return f(std::get<Indices>(std::forward<CreateArgs>(t))...);
        }

        template <typename CreateArgs, std::size_t... Indices>
        typename std::enable_if<!std::is_convertible<CreateArgs, typename signature_t<Signature>::param_types>::value, AbstractProduct>::type
        static apply(boost::function<Signature> const &, CreateArgs &&, indices<Indices...>)
        {
            return nullptr;
        }
    };

    template <typename... CreateArguments>
    struct dispatcher : boost::static_visitor<AbstractProduct>
    {
        std::tuple<CreateArguments...> args;

        dispatcher(CreateArguments const&... args) : args{std::forward_as_tuple(args...)} {}

        template <typename Signature>
        AbstractProduct operator()(boost::function<Signature> const &f) const
        {
            int status;
            std::cout << "visitor: " << abi::__cxa_demangle(typeid(Signature).name(), nullptr, 0, &status) << "\n";
            return dispatch_foo<Signature>::apply(f, args, make_tuple_indices<std::tuple<CreateArguments...>>{});
        }
    };

public:
    template <typename ProductCreator>
    bool Register(IdentifierType id, ProductCreator &&creator) {
        return associations_.emplace(id, std::forward<ProductCreator>(creator)).second;
    }

    bool Unregister(const IdentifierType& id) {
        return associations_.erase(id) == 1;
    }

    template <typename... Arguments>
    AbstractProduct CreateObject(const IdentifierType& id, Arguments const& ... args) {
        auto i = associations_.find(id);
        if (i != associations_.end()) {
            dispatcher<Arguments...> impl(args...);
            return boost::apply_visitor(impl, i->second);
        }
        throw std::runtime_error("Creator not found.");
    }
};


struct Arity {
    virtual ~Arity() = default;
};

struct Nullary : Arity {};

struct Unary : Arity {
    Unary() {} // Also has nullary ctor.
    Unary(int) {}
};


int main(void)
{
    multifactory<Arity*, int, Arity*(), Arity*(const int&)> factory;
    factory.Register(0, boost::function<Arity*()>( boost::factory<Nullary*>() ));
    factory.Register(1, boost::function<Arity*(const int&)>(boost::factory<Unary*>()) );
    auto a = factory.CreateObject(0);
    assert(a);
    assert(typeid(*a) == typeid(Nullary));
    auto b = factory.CreateObject(1, 2);
    assert(b);
    assert(typeid(*b) == typeid(Unary));
}

这篇关于具有多个构造函数签名的C ++通用工厂?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆