C++ Factory 使用可变参数模板问题
C++ Factory using variadic template issues
我只是在尝试一些东西 - 我想创建一个通用工厂,它将 returns shared_ptr
到一个类型。
我有一个派生的 class,它使用静态方法 return shared_ptr
到基础 class。我的想法是,我希望能够向通用工厂注册这些方法,但它无法确定在编译时注册哪个方法。也许有一种使用 SFINAE 实现此目的的方法,但我才刚刚开始了解它的复杂性。
对于相当长的代码示例表示歉意,也可在 http://coliru.stacked-crooked.com/a/331e08de86004592
在'DerivedA'中启用多个工厂方法将导致编译错误。
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
// Factory which returns a shared_ptr of type T.
template<class T, class Tag, class... Args>
class NameFactory
{
public:
typedef std::function<std::shared_ptr<T>(Args...)> Function;
static NameFactory& instance();
void registerType(const std::string& type, const Function& createFunction);
std::shared_ptr<T> createObject(const std::string& type, Args&&... arguments);
private:
NameFactory() {}
std::unordered_map<std::string, Function> m_functionMap;
};
template<class T, class Tag, class... Args>
NameFactory<T, Tag, Args...>& NameFactory<T, Tag, Args...>::instance()
{
static NameFactory<T, Tag, Args...> m_instance;
return m_instance;
}
template<class T, class Tag, class... Args>
void NameFactory<T, Tag, Args...>::registerType(const std::string& type, const Function& createFunction)
{
m_functionMap[type] = createFunction;
}
template<class T, class Tag, class... Args>
std::shared_ptr<T> NameFactory<T, Tag, Args...>::createObject(const std::string& type, Args&&... arguments)
{
auto iter(m_functionMap.find(type));
if (iter != m_functionMap.end())
{
return (iter->second)(std::forward<Args>(arguments)...);
}
throw std::logic_error("Cannot find constructor for type '" + type + "'");
}
template<class T, class Tag, class... Args>
class NameFactoryRegistration
{
public:
typedef NameFactory<T, Tag, Args...> Factory;
NameFactoryRegistration(const std::string& type, const typename Factory::Function& createFunction)
{
Factory::instance().registerType(type, createFunction);
}
private:
};
class MyBase
{
public:
typedef std::shared_ptr<MyBase> SPtr;
};
class DerivedA : public MyBase
{
public:
static SPtr create()
{
return SPtr(new DerivedA);
}
// Enabling this factory method (and/or the two args method below causes an 'unresolved overloaded function type' error
//static SPtr create(const std::string& s)
//{
// return SPtr(new DerivedA(s));
//}
//static SPtr create(const std::string& s, double d)
//{
// return SPtr(new DerivedA(s,d));
//}
private:
DerivedA()
{
std::cout << "DerivedA - no args" << std::endl;
}
DerivedA(const std::string& s)
{
std::cout << "DerivedA - one arg: " << s << std::endl;
}
DerivedA(const std::string& s, double d)
{
std::cout << "DerivedA - two args: " << s << " : " << d << std::endl;
}
};
// Tags to help differentiate the factories
struct NoArgsReg;
struct SingleArgReg;
struct TwoArgReg;
typedef NameFactory<MyBase, NoArgsReg> NoArgsFactory;
typedef NameFactoryRegistration<MyBase, NoArgsReg> NoArgsRegistration;
typedef NameFactory<MyBase, SingleArgReg, const std::string&> SingleArgFactory;
typedef NameFactoryRegistration<MyBase, SingleArgReg, const std::string&> SingleArgRegistration;
typedef NameFactory<MyBase, TwoArgReg, const std::string&, double> TwoArgsFactory;
typedef NameFactoryRegistration<MyBase, TwoArgReg, const std::string&, double> TwoArgsRegistration;
// Register the factory methods into the NameFactory
NoArgsRegistration dAReg0("A", DerivedA::create);
//SingleArgRegistration dAReg1("A", DerivedA::create);
//TwoArgsRegistration dAReg2("A", DerivedA::create);
int main()
{
auto object0(NoArgsFactory::instance().createObject("A"));
// Not registered,
//auto object1(SingleArgFactory::instance().createObject("A","testString"));
//auto object2(TwoArgsFactory::instance().createObject("A","testString",3.142));
return 0;
}
问题是(在 C++14 之前)std::function<R(A...)>
可以从 任何东西构造, 而不仅仅是从支持 R(A...)
调用的东西构造.如果您添加一个 registerType
的重载,它将采用 R (&)(Args&&...)
参数,这应该会有所帮助。
问题是您无法推断重载集中的类型。即使我们将示例简化为我们可以尝试使用 SFINAE 的东西,我们也被困住了:
#include <functional>
struct A {
static void create() { }
static void create(int ) { }
};
template <typename F,
typename = decltype(std::declval<F>()(std::declval<int>()))>
void foo(F ) { }
int main() {
foo(&A::create); // error, even in this case
}
您必须为函数指针添加显式重载才能处理这种情况,因为标准中有一个允许这样做的例外:
void foo(void (*)(int)) { } // (1)
template <typename F,
typename = decltype(std::declval<F>()(std::declval<int>()))>
void foo(F ) { } // (2)
int main() {
foo(&A::create); // OK, calls (1)
}
在您的具体示例中,这意味着添加两个构造函数:
// in Factory
using Function = std::function<std::shared_ptr<T>(Args...)>;
using FunctionPtr = std::shared_ptr<T>(*)(Args...);
// in Registration
using Function = typename Factory::Function;
using FunctionPtr = typename Factory::FunctionPtr;
NameFactoryRegistration(const std::string& type, const Function& createFunction) {
/* same as before */
}
NameFactoryRegistration(const std::string& type, FunctionPtr createFunction)
: NameFactoryRegistration(type, Function(createFunction))
{ }
我只是在尝试一些东西 - 我想创建一个通用工厂,它将 returns shared_ptr
到一个类型。
我有一个派生的 class,它使用静态方法 return shared_ptr
到基础 class。我的想法是,我希望能够向通用工厂注册这些方法,但它无法确定在编译时注册哪个方法。也许有一种使用 SFINAE 实现此目的的方法,但我才刚刚开始了解它的复杂性。
对于相当长的代码示例表示歉意,也可在 http://coliru.stacked-crooked.com/a/331e08de86004592
在'DerivedA'中启用多个工厂方法将导致编译错误。
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
// Factory which returns a shared_ptr of type T.
template<class T, class Tag, class... Args>
class NameFactory
{
public:
typedef std::function<std::shared_ptr<T>(Args...)> Function;
static NameFactory& instance();
void registerType(const std::string& type, const Function& createFunction);
std::shared_ptr<T> createObject(const std::string& type, Args&&... arguments);
private:
NameFactory() {}
std::unordered_map<std::string, Function> m_functionMap;
};
template<class T, class Tag, class... Args>
NameFactory<T, Tag, Args...>& NameFactory<T, Tag, Args...>::instance()
{
static NameFactory<T, Tag, Args...> m_instance;
return m_instance;
}
template<class T, class Tag, class... Args>
void NameFactory<T, Tag, Args...>::registerType(const std::string& type, const Function& createFunction)
{
m_functionMap[type] = createFunction;
}
template<class T, class Tag, class... Args>
std::shared_ptr<T> NameFactory<T, Tag, Args...>::createObject(const std::string& type, Args&&... arguments)
{
auto iter(m_functionMap.find(type));
if (iter != m_functionMap.end())
{
return (iter->second)(std::forward<Args>(arguments)...);
}
throw std::logic_error("Cannot find constructor for type '" + type + "'");
}
template<class T, class Tag, class... Args>
class NameFactoryRegistration
{
public:
typedef NameFactory<T, Tag, Args...> Factory;
NameFactoryRegistration(const std::string& type, const typename Factory::Function& createFunction)
{
Factory::instance().registerType(type, createFunction);
}
private:
};
class MyBase
{
public:
typedef std::shared_ptr<MyBase> SPtr;
};
class DerivedA : public MyBase
{
public:
static SPtr create()
{
return SPtr(new DerivedA);
}
// Enabling this factory method (and/or the two args method below causes an 'unresolved overloaded function type' error
//static SPtr create(const std::string& s)
//{
// return SPtr(new DerivedA(s));
//}
//static SPtr create(const std::string& s, double d)
//{
// return SPtr(new DerivedA(s,d));
//}
private:
DerivedA()
{
std::cout << "DerivedA - no args" << std::endl;
}
DerivedA(const std::string& s)
{
std::cout << "DerivedA - one arg: " << s << std::endl;
}
DerivedA(const std::string& s, double d)
{
std::cout << "DerivedA - two args: " << s << " : " << d << std::endl;
}
};
// Tags to help differentiate the factories
struct NoArgsReg;
struct SingleArgReg;
struct TwoArgReg;
typedef NameFactory<MyBase, NoArgsReg> NoArgsFactory;
typedef NameFactoryRegistration<MyBase, NoArgsReg> NoArgsRegistration;
typedef NameFactory<MyBase, SingleArgReg, const std::string&> SingleArgFactory;
typedef NameFactoryRegistration<MyBase, SingleArgReg, const std::string&> SingleArgRegistration;
typedef NameFactory<MyBase, TwoArgReg, const std::string&, double> TwoArgsFactory;
typedef NameFactoryRegistration<MyBase, TwoArgReg, const std::string&, double> TwoArgsRegistration;
// Register the factory methods into the NameFactory
NoArgsRegistration dAReg0("A", DerivedA::create);
//SingleArgRegistration dAReg1("A", DerivedA::create);
//TwoArgsRegistration dAReg2("A", DerivedA::create);
int main()
{
auto object0(NoArgsFactory::instance().createObject("A"));
// Not registered,
//auto object1(SingleArgFactory::instance().createObject("A","testString"));
//auto object2(TwoArgsFactory::instance().createObject("A","testString",3.142));
return 0;
}
问题是(在 C++14 之前)std::function<R(A...)>
可以从 任何东西构造, 而不仅仅是从支持 R(A...)
调用的东西构造.如果您添加一个 registerType
的重载,它将采用 R (&)(Args&&...)
参数,这应该会有所帮助。
问题是您无法推断重载集中的类型。即使我们将示例简化为我们可以尝试使用 SFINAE 的东西,我们也被困住了:
#include <functional>
struct A {
static void create() { }
static void create(int ) { }
};
template <typename F,
typename = decltype(std::declval<F>()(std::declval<int>()))>
void foo(F ) { }
int main() {
foo(&A::create); // error, even in this case
}
您必须为函数指针添加显式重载才能处理这种情况,因为标准中有一个允许这样做的例外:
void foo(void (*)(int)) { } // (1)
template <typename F,
typename = decltype(std::declval<F>()(std::declval<int>()))>
void foo(F ) { } // (2)
int main() {
foo(&A::create); // OK, calls (1)
}
在您的具体示例中,这意味着添加两个构造函数:
// in Factory
using Function = std::function<std::shared_ptr<T>(Args...)>;
using FunctionPtr = std::shared_ptr<T>(*)(Args...);
// in Registration
using Function = typename Factory::Function;
using FunctionPtr = typename Factory::FunctionPtr;
NameFactoryRegistration(const std::string& type, const Function& createFunction) {
/* same as before */
}
NameFactoryRegistration(const std::string& type, FunctionPtr createFunction)
: NameFactoryRegistration(type, Function(createFunction))
{ }