具有可变参数和可定制特征的类模板

时间:2014-08-12 05:06:22

标签: templates c++11 sfinae generic-programming typetraits

我知道这个问题的标题并不是很好。如果有人能够更清楚地解决它,我会很感激。

我的整体问题简要概述:

我有一个我设计的通用工厂类,我已用于许多项目。 Factory类使用可变参数来允许我指定传递给已创建类的构造函数的0个或多个参数。

当我最初设计这个Generic_Factory类时,我没有将构造的类T作为std :: shared_ptr返回的问题。但是,出现了新的要求,我想使我的Generic_Factory类更通用,并允许工厂用户指定返回的指针类型 - std :: shared_ptr,std :: unique_ptr或T *

工作解决方案

我通过添加另一个模板参数来修改Generic_Factory类。我不喜欢这种方法,因为(a)它不允许我指定默认行为,(b)我发现将Pointer_Type作为模板参数在阅读代码时有点混乱。然而,它确实有效。下面是测试的Generic_Factory调用,然后使用此类的示例:

    template <class T>
    struct Shared_Pointer{
        using Type = std::shared_ptr<T>;
    };
    template <class T>
    struct Unique_Pointer{
        using Type = std::unique_ptr<T>;
    };
    template <class T>
    struct Raw_Pointer{
        using Type = T*;
    };


    template <
                class AbstractType, 
                template <typename> class Pointer_Type<AbstractType>,
                class...ConstructorArgs>
    class Generic_Factory{

    public:
        static typename Pointer_Type<AbstractType>::Type 
                Construct(std::string key, ConstructorArgs... arguments){
            auto it = Get_Registry()->find(key);
            if (it == Get_Registry()->cend())
                return nullptr;

            auto constructor = it->second;
            return constructor(std::forward<ConstructorArgs>(arguments)...);
        }

        using Constructor_t = std::function<
                typename Pointer_Type<AbstractType>::Type(ConstructorArgs...)>;
        using Registry_t = std::map< std::string, Constructor_t>;

        Generic_Factory(Generic_Factory const&) = delete;
        Generic_Factory& operator=(Generic_Factory const&) = delete;

    protected:
        Generic_Factory(){}
        static Registry_t* Get_Registry();

    private:
        static Registry_t* _registry_;

    };

    template <
                class ConcreteType, 
                class AbstractType, 
                template <typename> class Pointer_Type<AbstractType>, 
                class...ConstructorArgs>
    struct Factory_Registrar : 
                private Generic_Factory<AbstractType, Pointer_Type, ConstructorArgs...>
                {
        using Factory = Generic_Factory<AbstractType, Pointer_Type, ConstructorArgs...>;
        using Constructor_t = typename Factory::Constructor_t;

public:
        Factory_Registrar(std::string const& designator, Constructor_t  object_constructor){
            auto registry = Factory::Get_Registry();
            if (registry->find(designator) == registry->cend())
                registry->insert(std::make_pair(designator, object_constructor));
        }
        unsigned int NO_OP(){ return 0; }
    };

    template <class Return_t, template <typename> class Pointer_Type, class...Args>
    typename Generic_Factory<Return_t, Pointer_Type, Args...>::Registry_t* Generic_Factory<Return_t, Pointer_Type, Args...>::Get_Registry(){
    if (_registry_ == nullptr)
        _registry_ = new Generic_Factory<Return_t, Pointer_Type, Args...>::Registry_t();
    return _registry_;
}

    template <class Return_t, template <typename> class Pointer_Type, class...Args>
    typename Generic_Factory<Return_t, Pointer_Type, Args...>::Registry_t* Generic_Factory<Return_t, Pointer_Type, Args...>::_registry_ = nullptr;

此泛型类的用法如下:

class My_Abstract_Type; // Forward-declaration of abstract type
class Ctor_Arg1; // Forward declaration of arbitrary type, argument to constructor
class Ctor_Arg2; // Forward declaration of arbitrary type, argument to constructor

template <class ConcreteType>
using My_Factory_Registrar =
    Factory_Registrar<ConcreteType, My_Abstract_Type, Shared_Pointer, Ctor_Arg1&&, Ctor_Arg2 const&>;
using My_Factory =
    Generic_Factory<My_Abstract_Type, Shared_Pointer, Ctor_Arg1&&, Ctor_Arg2 const&>;

我在具体类本身中使用宏作为样板注册码。它只是一个静态变量声明:

class Concrete1{
   /* ...  */
   static My_Factory_Registrar<Concrete1>;
}

如果需要,此变量的定义将提供键(字符串)和任何其他参数。

我理想的解决方案是使用traits类来消除Pointer_Type模板参数。我已经通过创建一个带有相关辅助类的traits类来尝试这个,但这并没有正常工作。我自己的尝试涉及尝试为每个AbstractType特化traits类(至少那些我想与std :: shared_ptr不同的那些),但是有很多问题。其中大部分我甚至都没有确定(不会编译,而且代码只是闻起来有点错误 - 我觉得这是错误的方法,所以我&#39;在浪费大量时间在我真正认为是错误的事情之前,我要求一些指导。

我真的希望能够使用类似于Generic_Factory类的其余使用的声明性语法 - 理想情况下是某种类型的别名。

非常感谢任何想法和/或建议。

谢谢和最诚挚的问候, 什穆埃尔

1 个答案:

答案 0 :(得分:0)

经过一些工作和一些额外的想法,我已经成功地使用Traits方法使代码工作,在&#34;我真正想要的内容&#34中所述;

简短摘要(我将复制下面的完整代码):

  • 我试图让我的Generic_Factory更通用 - 允许我选择指针的类型(我可以通过值或引用返回对象而不会有太多麻烦扩展它。)
  • 最简单的方法是添加另一个模板参数,但我觉得这很难看:
    • 无默认参数
    • 使界面更加混乱
  • 基于特质的方法是理想的,但我在实施这个
  • 时遇到了麻烦
  • 我对traits方法的主要困难是Traits类本身的部分特化。这通常与工厂代码位于不同的命名空间中,此外,它不具备声明性语法,以及其他麻烦。
  • 经过进一步思考,目标答案是Traits类使用的Pointer_Type信息应该添加到AbstractType的目录中。这完全有道理,它似乎绝对解决了我的所有麻烦。

代码的Traits部分如下。有关完整代码,请参阅我的Github repository

   namespace internal{
    template <typename T, bool> struct Factory_Pointer_Traits_Impl;
    template<typename T> struct CheckForType;
    }

    template <typename T>
    struct Factory_Pointer_Traits
    {
        typedef typename internal::Factory_Pointer_Traits_Impl<T, internal::CheckForType<T>::value>::type pointer_t;
    };

    namespace internal {

        template <typename T, bool>
        struct Factory_Pointer_Traits_Impl
        {
            typedef Shared_Pointer<T> type;
        };

       template <typename T>
       struct Factory_Pointer_Traits_Impl<T, true>
       {
           typedef typename T::Pointer_Type type;
       };   

       template<typename T>
       struct CheckForType
       {
       private:
           typedef char                      yes;
           typedef struct { char array[2]; } no;   

           template<typename C> static yes test(typename C::Pointer_Type*);
           template<typename C> static no  test(...);
       public:
           static const bool value = sizeof(test<T>(0)) == sizeof(yes);
       };
    }

这需要Generic_Factory类中的新别名:

    template <class AbstractType, class...ConstructorArgs>
    class Generic_Factory{
        using Pointer_T = typename core::Factory_Pointer_Traits<AbstractType>::pointer_t::type;
    public:
        /* ... */
    };

当然,所有对旧模板模板参数Pointer_Type的引用都会替换为新别名Pointer_T

在为我将拥有Factory的层次结构的根声明一个抽象类时,需要一个简单的别名或typedef:

class IPerson{
    public:
        using Pointer_Type = Unique_Pointer<IPerson>;
        std::string first_name, last_name;
        /* ... */
    };

替代方案可以使用宏:

#define FACTORY_POINTER_TYPE(CLASS_NAME, POINTER_TYPE)  \
using Pointer_Type = fx::core::POINTER_TYPE<CLASS_NAME>;

class IPerson{
    public:
        FACTORY_POINTER_TYPE(IPerson, Unique_Pointer);
        std::string first_name, last_name;
        /* ... */
    };

不幸的是,我无法找到一种简单的方法来摆脱宏的CLASS_NAME参数。也许像boost :: make_shared_from_this和decltype之类的东西可能会起作用......我不确定这是否值得付出努力。

我仍然希望听到您的任何建议或反馈。

谢谢和问候, 什穆埃尔