如何从可变参数宏正确调用可变参数模板函数?

时间:2017-05-12 21:25:04

标签: c++ macros c++14 variadic-templates variadic-macros

好的,首先,我会提供我要问的代码。

EX_Factory.h

#ifndef EX_FACTORY_H
#define EX_FACTORY_H

#include <string>
#include <map>
#include "Base.h"

struct EX_Factory{
    template<class U, typename... Args>
    static void registerC(const std::string &name){
        registry<Args...>[name] = &create<U>;
    }
    template<typename... Args>
    static Base * createObject(const std::string &key, Args... args){
        auto it = registry<Args...>.find(key);
        if(it == registry<Args...>.end()) return nullptr;
        return it->second(args...);
    }
    private:
        EX_Factory() = delete;
        template<typename... Args>
        static std::map<std::string, Base*(*)(Args...)> registry;

        template<class U, typename... Args>
        static Base* create(Args... args){
            return new U(args...);
        }
};

template<typename... Args>
std::map<std::string, Base*(*)(Args...)> EX_Factory::registry;

template<typename U, typename... Args>
struct MiddleMan{
    MiddleMan(const std::string &name){
        EX_Factory::registerC<U,Args...>(name);
    }
};
  #define REGISTER_MACRO(NAME, TYPE, ...)\ // Updated the macro here
    static MiddleMan<TYPE, ##__VA_ARGS__> mm_##TYPE(#NAME);

#endif

上面的代码是我的Factory Design Pattern类的头文件。它允许此类的用户为使用不同参数从 Base 类派生的对象注册构造函数。

好的,快速解释我为什么使用__VA_ARGS__ vs ## __ VA_ARGS__,原因是,总会有至少1个参数,registerC模板函数中的模板类型U,传递给宏。

我要做的是构建一个预处理器宏 REGISTER_MACRO ,以便在运行时注册派生类。如何正确地从 Variadic Macro 调用可变参数模板函数

测试或main.cpp

#include "EX_Factory_1.h"
#include <iostream>

using namespace std;

struct derived_1 : public Base{
    derived_1(int i, int j, float f){
        cout << "Derived 1:\t" << i * j + f << endl;
    }
};
REGISTER_MACRO(name_1, derived_1, int,int,float);
struct derived_2 : public Base{
    derived_2(int i, int j){
        cout << "Derived 2:\t" << i + j << endl;
    }
};
REGISTER_MACRO(name_2, derived_2, int,int);

int main(){ // Program segfaults before entering main
    derived_1 * d_1 = static_cast<derived_1*>(EX_Factory::createObject<int, int, float>("name_1", 8, 8, 3.0));
    if(d_1 == nullptr) cout << "Why is it null?" << endl;
    else{
        delete d_1;
    }
    return 0;
}

我尝试编译上述代码时收到的错误: 请参阅下面的修改

In file included from test.cpp:1:0:
EX_Factory.h:39:38: error: expected constructor, destructor, or type conversion before ‘(’ token
    EX_Factory::registerC<__VA_ARGS__>(NAME);
                                      ^
test.cpp:11:1: note: in expansion of macro ‘REGISTER_MACRO’
 REGISTER_MACRO("derived_1",derived_1,int,int,float)
 ^~~~~~~~~~~~~~
EX_Factory.h:39:38: error: expected constructor, destructor, or type conversion before ‘(’ token
    EX_Factory::registerC<__VA_ARGS__>(NAME);
                                      ^
test.cpp:17:1: note: in expansion of macro ‘REGISTER_MACRO’
 REGISTER_MACRO("derived_2",derived_2,int,int)

我正在使用 g ++ 6.3 编译器,因此默认情况下,它在Ubuntu 16.04环境中使用 C ++ 14 标准进行编译。

我找到的潜在解决方案,但不适合我的问题:

Is there a way to define variadic template macro

Using variadic macros or templates to implement a set of functions

潜在的解决方案,但无法解释其解决方案:

How to use variadic template with variadic macro ? 我没有使用QT,而且我使用的是完全支持的C ++ 11编译器。

修改

我现在知道为什么我的文件不能编译,但我不知道如何处理创建一个对象来为自己调用该方法。我添加了一个中间人类,MiddleMan,并将静态registerC类的调用添加到其构造函数中,并修改了我的宏以创建对此类的静态引用。这允许我的程序编译,但它使用此方法进行了段错误。您现在将看到上面提到的反思。

我在this question的答案中使用main.cpp测试了这个类和宏。

1 个答案:

答案 0 :(得分:1)

好的,在@rici的帮助下,进行了一些研究,之前提到了@ T.C的原因。我开发了一个改进的设计,它使用 Registrator 对象将 Base 派生对象注册到我的工厂设计中。

为了绕过我遇到的段错误,我决定实现Functors来存储我的模板创建功能。我设计了一个基本仿函数, F ,以及一个模板派生的仿函数, DF

我创建了一个 getMap()方法,该方法将引用返回到存储 std :: string 的静态地图对象基本仿函数指针, F * ,在其中成对。

我的解决方案代码如下!

FunctorFactory.h

#ifndef FUNCTOR_FACTORY_H
#define FUNCTOR_FACTORY_H

#include <map>
#include <string>

#include "Base.h"

struct FunctorFactory{
    private:
        class F{ // Base Functor
            public:
                virtual ~F(){}
        };
        template<typename R, typename... A>
        struct DF : public F{ // Templated Derived Functors
            DF(R (*f)(A...)); // Regular Constructor
            DF(const DF<R,A...>& df); // Copy Constructor
            virtual ~DF(); // Virtual Destructor
            virtual R operator()(A... args); // Overloaded () to allow function like use.
            virtual DF<R,A...>& operator=(const DF<R,A...>& df); // Overloaded = to allow assignment

            private:
                R (*_f)(A...); // The actual function being called
        };
        static std::map<std::string,F*>& getMap(); // Map pairing string keys to their Functors(stored as Base Functors)
        template<typename U, typename... A> // Templated create function which will create the derived objects being requested!
        static Base * create(A... args);
    public:
        template<typename U, typename... A> // Registrator class which registers objects
        struct Registrator{
            Registrator(const std::string &key);
        };
        template<typename... A> // createObject static method which creates the requested object
        static Base * createObject(const std::string &skey, A... args);
};

// Implementation of the derived functor's methods
template<typename R, typename... A> // Basic Constructor.
FunctorFactory::DF<R,A...>::DF(R (*f)(A...)){_f = f;}
template<typename R, typename... A> // Copy Constructor.
FunctorFactory::DF<R,A...>::DF(const FunctorFactory::DF<R,A...>& df){_f = df._f;}
template<typename R, typename... A> // Destructor
FunctorFactory::DF<R,A...>::~DF(){}
template<typename R, typename... A> // Overloaded () operator
R FunctorFactory::DF<R,A...>::operator()(A... args){return _f(args...);}
template<typename R, typename... A> // Overloaded = operator
FunctorFactory::DF<R,A...>& FunctorFactory::DF<R,A...>::operator=(const FunctorFactory::DF<R,A...>& df){_f = df._f;}

std::map<std::string,FunctorFactory::F*>& FunctorFactory::getMap(){ // Get map class
    static std::map<std::string,FunctorFactory::F*> m_map; // The actual map storing the functors.
    return m_map;
}

template<typename U, typename... A>
Base * FunctorFactory::create(A... args){
    return new U(args...);
}

template<typename U, typename... A>
FunctorFactory::Registrator<U,A...>::Registrator(const std::string &key){
    DF<Base*,A...> * df = new DF<Base*,A...>(FunctorFactory::create<U,A...>);
    getMap()[key] = df;
}
template<typename... A>
Base * FunctorFactory::createObject(const std::string &skey, A... args){
    auto it = getMap().find(skey);
    if(it == getMap().end()) return nullptr;
    DF<Base*,A...> * df = static_cast<DF<Base*,A...>*>(it->second);
    return (*df)(args...);
}

    #ifndef FF_MACRO
        #define FF_MACRO(NAME,TYPE,...)\
            namespace { \
                ::FunctorFactory::Registrator<TYPE,##__VA_ARGS__> registrator_##NAME(#NAME); \
            }
    #endif

#endif // FUNCTOR_FACTORY_H

Base.h

#ifndef BASE_H
#define BASE_H

class Base{
    public:
        virtual ~Base(){}
};

#endif

的main.cpp

#include "FunctorFactory.h"
#include <iostream>

using namespace std;

struct derived_1 : public Base{
    derived_1(int i, int j, float f){
        cout << "Derived 1:\t" << i * j + f << endl;
    }
};
FF_MACRO(name_1, derived_1, int,int,float);

struct derived_2 : public Base{
    derived_2(int i, int j){
        cout << "Derived 2:\t" << i + j << endl;
    }
};
FF_MACRO(name_2, derived_2, int,int);

int main(){
    derived_1 * d_1 = static_cast<derived_1*>(FunctorFactory::createObject<int, int, float>("name_1", 8, 8, 3.0));
    if(d_1 == nullptr) cout << "The function being requested must either have different parameters or a different key." << endl;
    else{
        delete d_1;
    }
    return 0;
}

输出

Derived 1:  67

我希望这可以帮助遇到类似问题的人。 如果需要,也可以随意使用此工厂模型。它能够为重载的构造函数存储多个不同的映射到同一个对象类型以及多个不同的派生对象。