I'm trying to write a factory class which will have a standard interface that looks like this:
Register<MyBase, MyDerived> g_regDerived("myderived"); // register to factory
now calling:
auto* d = Factory<MyBase>::instance().create("myderived", 1, 2, 3);
will invoke the constructor MyDerived(1,2,3) and return a pointer to the created object
This sounds like something that should be possible with C++11 but I could not figure out how to do it.
Starting from a standard type-erasure factory:
template<typename BaseT>
class Factory {
public:
static Factory* instance() {
static Factory inst;
return &inst;
}
template<typename T>
void reg(const string& name) {
m_stock[name].reset(new Creator<T>);
}
BaseT* create(const string& name) {
return m_stock[name]->create();
}
private:
struct ICreator {
virtual BaseT* create() = 0;
};
template<typename T>
struct Creator : public ICreator {
virtual BaseT* create() {
return new T;
}
};
std::map<string, std::unique_ptr<ICreator>> m_stock;
};
template<typename BaseT, typename T>
class Register {
public:
Register(const QString& name) {
Factory<BaseT>::instance()->reg<T>(name);
}
};
The problem here is fact that once you erase the type of the created object, you can no longer pass arbitrary template forwarded arguments since you need to pass them through a virtual function.
The answer to this question:
How to pass a function pointer that points to constructor?
talks about something similar but the answer there is to go through a function which is specific for every derived class. I want to use the class constructor directly and not have to write a create() function.
I don't know why your aversion to writing a create() function. So here is one that I implemented.
#include <iostream>
#include <utility>
using namespace std;
class C
{
public:
virtual char const* whoAmI() const = 0;
};
class A : public C
{
public:
A(int a1)
{
cout << "A(" << a1 << ")" << endl;
}
A(float a1)
{
cout << "A(" << a1 << ")" << endl;
}
virtual char const* whoAmI() const override
{
return "A";
}
};
class B : public C
{
public:
B(int a1)
{
cout << "B(" << a1 << ")" << endl;
}
B(float a1)
{
cout << "B(" << a1 << ")" << endl;
}
virtual char const* whoAmI() const override
{
return "B";
}
};
template<typename BASET>
class Factory
{
public:
// could use a is_base type trait test here
template <typename T, typename...ARGs>
static BASET* create(ARGs&&...args)
{
return new T(forward<ARGs>(args)...);
}
};
int main()
{
Factory<C> factory;
C* a = factory.create<A>(1);
C* b = factory.create<B>(1.0f);
cout << a->whoAmI() << endl;
cout << b->whoAmI() << endl;
return 0;
}
NOTE: I didn't do everything that yours does, I merely implemented the create function. I leave the final implementation up to you.
This uses perfect forwarding to enable a varidict template to pass any number of parameters to a constructor. Your register function can then store a function pointer of a particular template instance, for a particular parameter set.
EDIT
I forgot to use the appropriate forward<ARGs>(args)... call to implement perfect forwarding. It has now been added.
As for you thinking that this is not useful, here is the full implementation of your factory using perfect forwarding and varidict templates allowing a specific number of parameters of particular types for a particular factory instance:
#include <string>
#include <map>
#include <memory>
#include <utility>
#include <iostream>
using namespace std;
template<typename BaseT, typename...ARGs>
class Factory {
public:
static Factory* instance() {
static Factory inst;
return &inst;
}
template<typename T>
void reg(const string& name) {
m_stock[name].reset(new Creator<T>);
}
BaseT* create(const string& name, ARGs&&...args) {
return m_stock[name]->create(forward<ARGs>(args)...);
}
private:
struct ICreator
{
virtual BaseT* create(ARGs&&...) = 0;
};
template<typename T>
struct Creator : public ICreator {
virtual BaseT* create(ARGs&&...args) override
{
return new T(forward<ARGs>(args)...);
}
};
std::map<string, std::unique_ptr<ICreator>> m_stock;
};
template<typename BaseT, typename T, typename...ARGs>
class Register {
public:
Register(const string& name) {
auto instance = Factory<BaseT, ARGs...>::instance();
instance->template reg<T>(name);
}
};
struct C
{
virtual char const * whoAmI() const = 0;
};
struct A : public C
{
A(int a1, int a2)
{
cout << "Creating A(" << a1 << ", " << a2 << ")" << endl;
}
virtual char const * whoAmI() const override
{
return "A";
}
};
struct B : public C
{
B(int b1, int b2)
{
cout << "Creating B(" << b1 << ", " << b2 << ")" << endl;
}
B(int b1, int b2, int b3)
{
cout << "Creating B(" << b1 << ", " << b2 << ", " << b3 << ")" << endl;
}
virtual char const * whoAmI() const override
{
return "B";
}
};
typedef int I;
Register<C, A, I, I> a("a");
Register<C, B, I, I> b("b");
Register<C, B, I, I, I> b3("b");
int main()
{
C* a = Factory<C, I, I>::instance()->create("a", 1, 2);
C* b = Factory<C, I, I>::instance()->create("b", 3, 4);
C* b3 = Factory<C, I, I, I>::instance()->create("b", 5, 6, 7);
cout << "I am a " << a->whoAmI() << endl;
cout << "I am a " << b->whoAmI() << endl;
cout << "I am a " << b3->whoAmI() << endl;
return 0;
}
Is that what you want? If you don't want to deal with the function parameters, use a helper template function to deduce them for you like so:
template <typename BaseT, typename...ARGs>
BaseT* create(const string& name, ARGs&&...args)
{
return Factory<C, ARGs...>::instance()->create(name, forward<ARGs>(args)...);
}
int main()
{
C* a = create<C>("a", 1, 2);
C* b = create<C>("b", 3, 4);
C* b3 = create<C>("b", 3, 4, 5);
cout << "I am a " << a->whoAmI() << endl;
cout << "I am a " << b->whoAmI() << endl;
cout << "I am a " << b3->whoAmI() << endl;
return 0;
}
Which has the added bonus of allowing multiple constructor signatures available through the apparent single function API (it only looks like one, but is actually N where N is the number of different signatures you allow). This all can be viewed through this online demo.
You'll still need to use the same registration as I depicted before though, which could be shortened by way of a macro.
If this is still not what you want, then add additional detail to your question.
Related
Is there any way to achieve the functionality of below code without creating the mapping between strings and classes manually?
template<class base, typename T>
base* f(const std::string &type, T &c) {
if(type == "ClassA") return new ClassA(c);
else if(type == "ClassB") return new ClassB(c);
// many more else if...
return nullptr;
}
All classes looks something like this:
class ClassA: public BaseClass {
public:
std::string label="ClassA";
...
};
And we can use it as:
BaseClass *b = f<BaseClass>("ClassA", DifferentObject);
Each new class results in a new if else line of code. Is there any way to automate this so f function "updates" itself when new supported class is added? The solution must work for C++11.
A possible macro:
#include <memory>
#include <string>
class BaseClass {};
class ClassA : public BaseClass {
public:
std::string label = "ClassA";
explicit ClassA(int /*unused*/) {}
};
class ClassB : public BaseClass {
public:
std::string label = "ClassB";
explicit ClassB(int /*unused*/) {}
};
template<class base, typename T>
auto f(const std::string &type, T c) -> std::unique_ptr<base> {
#define CASE(NAME) \
if (type == "NAME") { \
return std::unique_ptr<base>(new NAME(c)); \
}
CASE(ClassA)
CASE(ClassB)
//...
#undef CASE
return nullptr; // Statement at the end needed for last else!
}
auto main() -> int {
auto b = f<BaseClass>("ClassA", 0);
}
Also use unique_ptr since memory managing raw pointers are EVIL.
In that case if the class name is equal to the string, you can simplify your code with the following macro:
#define STRING_TO_CLASS (className) if(type == "className") return new className(c);
template<class base, typename T>
base* f(const std::string &type, T &c) {
STRING_TO_CLASS(ClassA)
STRING_TO_CLASS(ClassB)
return nullptr;
}
Personally I hate macros, but it disturbs only me. However, at compile time, the following code wil be generated, after the macros are resolved.
template<class base, typename T>
base* f(const std::string &type, T &c) {
if(type == "ClassA") return new ClassA(c);
if(type == "ClassB") return new ClassB(c);
return nullptr;
}
As you see, in the end only the else keyword is removed. Also, you need to modify your code if a new class is added.
You could use the registry pattern like this:
#include <map>
#include <functional>
#include <string>
template< typename T, typename X >
using Factory = std::function< T* ( X& ) >;
template< typename Base, typename X >
struct Registry {
using Map = std::map<std::string,Factory<Base,X> >;
static Map registry;
template< typename T >
struct Register {
Register( const std::string& name ) {
registry[ name ] = []( X& x ) -> T* { return new T(x); };
}
};
};
template< typename Base, typename X >
Base* factory(const std::string &type, X &c ) {
auto it = Registry<Base,X>::registry.find( type );
if ( it!=Registry<Base,X>::registry.end() ) {
return (it->second)(c);
}
return nullptr;
}
struct X {};
struct A {
A( X& x ) {};
virtual ~A() {}
};
struct B : public A {
B( X& x ) : A(x) {};
};
struct C : public A {
C( X& x ) : A(x) {};
};
struct D : public B {
D( X& x ) : B(x) {};
};
// Register class
template<> Registry<A,X>::Map Registry<A,X>::registry{};
Registry<A,X>::Register<B> regB( "B" );
Registry<A,X>::Register<C> regC( "C" );
Registry<A,X>::Register<D> regD( "D" );
#include <iostream>
int main() {
X x;
A* ptr = factory<A,X>( "B", x );
B* bptr = dynamic_cast<B*>( ptr );
if ( bptr!= nullptr ) {
std::cout << "Success!" << std::endl;
return 0;
}
std::cout << "Failed!" << std::endl;
return 1;
}
The correct pattern to use here is the "Abstract Factory" pattern.
Maybe you can look it up.
To give you and idea (and not more), what is possible, I will show you the below code, which even accepts constructors with different signature.
#include <iostream>
#include <map>
#include <utility>
#include <any>
// Some demo classes ----------------------------------------------------------------------------------
struct Base {
Base(int d) : data(d) {};
virtual ~Base() { std::cout << "Destructor Base\n"; }
virtual void print() { std::cout << "Print Base\n"; }
int data{};
};
struct Child1 : public Base {
Child1(int d, std::string s) : Base(d) { std::cout << "Constructor Child1 " << d << " " << s << "\n"; }
virtual ~Child1() { std::cout << "Destructor Child1\n"; }
virtual void print() { std::cout << "Print Child1: " << data << "\n"; }
};
struct Child2 : public Base {
Child2(int d, char c, long l) : Base(d) { std::cout << "Constructor Child2 " << d << " " << c << " " << l << "\n"; }
virtual ~Child2() { std::cout << "Destructor Child2\n"; }
virtual void print() { std::cout << "Print Child2: " << data << "\n"; }
};
struct Child3 : public Base {
Child3(int d, long l, char c, std::string s) : Base(d) { std::cout << "Constructor Child3 " << d << " " << l << " " << c << " " << s << "\n"; }
virtual ~Child3() { std::cout << "Destructor Child3\n"; }
virtual void print() { std::cout << "Print Child3: " << data << "\n"; }
};
using UPTRB = std::unique_ptr<Base>;
template <class Child, typename ...Args>
UPTRB createClass(Args...args) { return std::make_unique<Child>(args...); }
// The Factory ----------------------------------------------------------------------------------------
template <class Key, class Object>
class Factory
{
std::map<Key, std::any> selector;
public:
Factory() : selector() {}
Factory(std::initializer_list<std::pair<const Key, std::any>> il) : selector(il) {}
template<typename Function>
void add(Key key, Function&& someFunction) { selector[key] = std::any(someFunction); };
template <typename ... Args>
Object create(Key key, Args ... args) {
if (selector.find(key) != selector.end()) {
return std::any_cast<std::add_pointer_t<Object(Args ...)>>(selector[key])(args...);
}
else return nullptr;
}
};
int main()
{
// Define the factory with an initializer list
Factory<int, UPTRB> factory{
{1, createClass<Child1, int, std::string>},
{2, createClass<Child2, int, char, long>}
};
// Add a new entry for the factory
factory.add(3, createClass<Child3, int, long, char, std::string>);
// Some test values
std::string s1(" Hello1 "); std::string s3(" Hello3 ");
int i = 1; const int ci = 1; int& ri = i; const int& cri = i; int&& rri = 1;
UPTRB b1 = factory.create(1, 1, s1);
UPTRB b2 = factory.create(2, 2, '2', 2L);
UPTRB b3 = factory.create(3, 3, 3L, '3', s3);
b1->print();
b2->print();
b3->print();
b1 = factory.create(2, 4, '4', 4L);
b1->print();
return 0;
}
I ran into a problem while passing a function as a parameter in a method. My problem is that I want to be able to pass any void function with any type and amount of arguments and then store it in a data member. What I currently do is an overload for a void function without arguments and an overload for a void function with a vector argument of type std::any and then add an extra parameter for the vector. Here is an example of what I did:
typedef void (*FunctionT1)();
typedef void (*FunctionT2)(std::vector<std::any>);
class ExampleClass {
public:
void saveFunction(FunctionT1 f) {
emptyFunction = f;
}
void saveFunction(FunctionT2 f, std::vector<std::any> args) {
vectorFunction = f;
vectorFunctionArgs = args;
}
private:
FunctionT1 emptyFunction;
FunctionT2 vecotorFunction;
std::vector<std::any> vectorFunctionArgs;
}
Now this works and I'm able to do what I want but it's clearly a pretty bad solution. Is there a way to do this in an other way? (I was thinking of template packs but couldn't figure out how those would work)
From what I understand, you want to call a function void(), where caller might register other function type with bind parameters. You might do it with std::function:
class ExampleClass {
public:
void saveFunction(std::function<void()> f) {
this->f = f;
}
void call() { f(); }
private:
std::function<void()> f;
};
with usage
void foo();
void bar(std::vector<std::any>);
std::vector<std::any> v;
ExampleClass ex;
ex.SaveFunction(&foo); ex.call(); // foo();
ex.SaveFunction([&]() { bar(v); }); ex.call(); // bar(v);
// You might capture by copy/move if you prefer (instead of by reference)
I would like to show to you an example with an abstract factory.
With the factory pattern we have normally the problem, that all contstructors should have the same signature. But often we want to use functions with different parameter sets.
If you look in the below example, I can add functions with any number of parameters to the factory.
I know that this is not exactly what you want, but it may give you a hint on how to implement your own functionality.
#include <iostream>
#include <map>
#include <utility>
#include <any>
// Some demo classes ----------------------------------------------------------------------------------
struct Base {
Base(int d) : data(d) {};
virtual ~Base() { std::cout << "Destructor Base\n"; }
virtual void print() { std::cout << "Print Base\n"; }
int data{};
};
struct Child1 : public Base {
Child1(int d, std::string s) : Base(d) { std::cout << "Constructor Child1 " << d << " " << s << "\n"; }
virtual ~Child1() { std::cout << "Destructor Child1\n"; }
virtual void print() { std::cout << "Print Child1: " << data << "\n"; }
};
struct Child2 : public Base {
Child2(int d, char c, long l) : Base(d) { std::cout << "Constructor Child2 " << d << " " << c << " " << l << "\n"; }
virtual ~Child2() { std::cout << "Destructor Child2\n"; }
virtual void print() { std::cout << "Print Child2: " << data << "\n"; }
};
struct Child3 : public Base {
Child3(int d, long l, char c, std::string s) : Base(d) { std::cout << "Constructor Child3 " << d << " " << l << " " << c << " " << s << "\n"; }
virtual ~Child3() { std::cout << "Destructor Child3\n"; }
virtual void print() { std::cout << "Print Child3: " << data << "\n"; }
};
using UPTRB = std::unique_ptr<Base>;
template <class Child, typename ...Args>
UPTRB createClass(Args...args) { return std::make_unique<Child>(args...); }
// The Factory ----------------------------------------------------------------------------------------
template <class Key, class Object>
class Factory
{
std::map<Key, std::any> selector;
public:
Factory() : selector() {}
Factory(std::initializer_list<std::pair<const Key, std::any>> il) : selector(il) {}
template<typename Function>
void add(Key key, Function&& someFunction) { selector[key] = std::any(someFunction); };
template <typename ... Args>
Object create(Key key, Args ... args) {
if (selector.find(key) != selector.end()) {
return std::any_cast<std::add_pointer_t<Object(Args ...)>>(selector[key])(args...);
}
else return nullptr;
}
};
int main()
{
Factory<int, UPTRB> factory{
{1, createClass<Child1, int, std::string>},
{2, createClass<Child2, int, char, long>}
};
factory.add(3, createClass<Child3, int, long, char, std::string>);
// Some test values
std::string s1(" Hello1 "); std::string s3(" Hello3 ");
int i = 1; const int ci = 1; int& ri = i; const int& cri = i; int&& rri = 1;
UPTRB b1 = factory.create(1, 1, s1);
UPTRB b2 = factory.create(2, 2, '2', 2L);
UPTRB b3 = factory.create(3, 3, 3L, '3', s3);
b1->print();
b2->print();
b3->print();
b1 = factory.create(2, 4, '4', 4L);
b1->print();
return 0;
}
From reading the question and going through the comments, this is what I think best suits your problem,
#include <vector>
class ExampleClass {
public:
ExampleClass() = default;
/**
* Save a function pointer.
*
* #param Function: The function poitner.
* #return The index of the function.
*/
template<class Return = void, class... Args>
size_t SaveFunction(Return(*Function)(Args... args))
{
mFunctions.insert(mFunctions.end(), Function);
return mFunctions.size() - 1;
}
/**
* Call a saved function.
*
* #tparam Return: The return type of the function.
* #param index: The index of the function.
* #param args: The arguments the function requires.
* #return The return of the function.
*/
template<class Return = void, class... Args>
Return CallFunction(size_t index, const Args&... args)
{
typedef Return(*Function)(Args...);
return reinterpret_cast<Function>(mFunctions[index])(args...);
}
private:
std::vector<void*> mFunctions;
};
And then you can save and call any function as so,
void Function() { std::cout << "Hello World\n"; }
void Add(int x, int y) { std::cout << x + y << std::endl; }
int main()
{
ExampleClass c;
size_t f1 = c.SaveFunction(Function);
size_t f2 = c.SaveFunction(Add);
c.CallFunction(f1);
c.CallFunction(f2, 1, 2);
}
This works with any function with any amount of arguments.
Note: If the return type is always gonna be a void, you can replace the Return with void.
Edit: with final keyword on the implementations of the virtual function results in printing the correct string, but why is the final keyword here needed? Could somebody explain?
I am tinkering with variadic templates, I have pretty generic classes D1, D2, D3, ... and they all derive from a class A. Each class has a static and a dynamic print functions, the parent class has a virtual dynamic print function for dynamic dispatching. When I try to reproduce it on a single file:
class A {
public:
virtual void printDynamic();
static void printStatic();
}
class D1 : public A {
public:
virtual void printDynamic();
static void printStatic();
}
And I have following variants:
std::variant<A,As...> apvar;
std::variant<A*,As*...> avar;
I instantiate both variants with all the derived classes D1,D2,... (I know upcasting Pointers i just want to dereference to their types and do random stuff)
I have implemented recursive visitors for the wrappers, i need to capture this because I encapsulated most of the functions in a class, when I call the visitor on classes I get the names "DX", "DX" ; X corresponding to 1.
template<class X, class Y, class... Zs>
void visit_actor(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>,expr_type<X>>::value){
value.printStaticName();
value.printDynamicName();
} else{
visit_actor<Y,Zs...>();
}
}, avar
);
}
But if I call the visitor on pointer variants and when I call the functions:
For the static function I get: "DX" with X corresponding to I, but when I call the dynamic function I get the name: "Abstract A".
template<class X, class Y, class... Zs>
void visit_pointer(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>,expr_type<X>>::value){
value->printStaticName();
value->printDynamicName();
} else{
visit_pointer<Y,Zs...>();
}
}, apvar
);
}
I have tried reading about it in c++ documentation but could not find the reason yet. What could be the reason that the static function of the derived class is called but the parents virtual function is called ?
For A Minimal Producable example, you need to instantiate the classes:
#include <variant>
#include <iostream>
#include <string>
template <class T>
using expr_type = std::remove_cv_t<std::remove_reference_t<T>>;
template<class A,class... As>
class ActorWrapper{
public:
std::variant<A,As...> var;
template<class Ins>
ActorWrapper(Ins ins) : var(ins) {}
};
template<class A,class... As>
class ActorPointer{
public:
std::variant<A,As... > var;
template<class T>
ActorPointer(T* t) : var(t) {}
};
class X {
public:
int a;
virtual std::string getDynamicName() {
return "dynamic X";
}
static std::string getStaticName(){
return "static X";
}
};
class D1 : public X{
public:
bool b;
std::string getDynamicName() override {
return "dynamic D1";
}
static std::string getStaticName(){
return "static D1";
}
};
class D2: public X {
public:
bool b;
std::string getDynamicName() override {
return "dynamic D2";
}
static std::string getStaticName(){
return "static D2";
}
};
template<class A, class... As>
class TemplatedAGraph{
private:
//change aw to correspond to ap
template<class X>
void visit_actor(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
std::cout << "z" << std::endl;
std::cout << value.getStaticName() << std::endl;
std::cout << value.getDynamicName() << std::endl;
}else{
std::cout << "d" << std::endl;
return;
}
}, aw.var
);
}
template<class X, class Y, class... Zs>
void visit_actor(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
std::cout << "x" << std::endl;
std::cout << value.getStaticName() << std::endl;
//std::cout << value.getDynamicName() << std::endl;
} else{
std::cout << "y" << std::endl;
visit_actor<Y,Zs...>();
}
}, aw.var
);
}
template<class X>
void visit_pointer(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
std::cout << "a" << std::endl;
std::cout << value->getStaticName() << std::endl;
std::cout << value->getDynamicName() << std::endl;
}else{
std::cout << "b" << std::endl;
return;
}
}, ap.var
);
}
template<class X, class Y, class... Zs>
void visit_pointer(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
std::cout << "c" << std::endl;
std::cout << value->getStaticName() << std::endl;
std::cout << value->getDynamicName() <<std::endl;
} else{
//std::cout << typeid(decltype(value)).name() <<std::endl;
//std::cout << typeid(X).name() <<std::endl;
//std::cout << std::is_same_v<decltype(value),X> << std::endl;
std::cout << "d" << std::endl;
visit_pointer<Y,Zs...>();
}
}, ap.var
);
}
public:
ActorPointer<A*,As*...> ap;
ActorWrapper<A,As...> aw;
void print_names(){
visit_actor<A,As...>();
}
void print_names_w_pointer(){
visit_pointer<A*,As*...>();
}
//change ap to coresspond to aw
template<class X>
TemplatedAGraph(X a) : ap(&a), aw(a) {}
};
int main(){
D2 d2;
D2* d2ref = &d2;
std::cout << d2ref->getDynamicName() << std::endl;
TemplatedAGraph<D1,D2> tag(d2);
tag.print_names();
tag.print_names_w_pointer();
}
The Output is:
thrud#thrud ~/wörk/test $ g++ main.cpp -std=c++17
thrud#thrud ~/wörk/test $ ./a.out
dynamic D2
y
z
static D2
dynamic D2
d
a
static D2
Segmentation fault
So I think I have stumbled upon an undefined behaviour, but still I would like to know the reason.
TemplatedAGraph(X a) : ap(&a), aw(a) {} stores a pointer to a local variable in ap. That pointer becomes dangling soon afterwards. Any attempt to access it then exhibits undefined behavior.
You might have meant TemplatedAGraph(X& a) :.... This way, your code works, as far as I can tell.
I created some small factory class. See the code below. The factory uses a simple map with a key and a class creator function. The challenge was that I wanted to use different signatures for the createInstance functions. With that, I could use different derived classes with different constructor signatures. So, I would be able to pass different values to the constructor of the classes.
Since I cannot store functions with different signatueres in std::function and finally in a std::map, I have written a short wrapper that "erases" the type parameter part. So, basically I can then store the function in a std::function, and finally in a std::any.
The code works fine. You can compile it and run it without problem.
There is also some test code attached.
Now my question:
I tried to come up with only one constructor in my class "Creator". But I could not achieve that. In my opinion it should be possible. I would like to ask for your support to come up with a possible solution.
Please note: I am in the phase of refactoring. So the code is not yet "perfect".
#include <iostream>
#include <map>
#include <utility>
#include <functional>
#include <any>
// Some demo classes ----------------------------------------------------------------------------------
struct Base {
Base(int d) : data(d) {};
virtual ~Base() { std::cout << "Destructor Base\n"; }
virtual void print() { std::cout << "Print Base\n"; }
int data{};
};
struct Child1 : public Base {
Child1(int d, std::string s) : Base(d) { std::cout << "Constructor Child1 " << d << " " << s << "\n"; }
virtual ~Child1() { std::cout << "Destructor Child1\n"; }
virtual void print() { std::cout << "Print Child1: " << data << "\n"; }
};
struct Child2 : public Base {
Child2(int d, char c, long l) : Base(d) { std::cout << "Constructor Child2 " << d << " " << c << " " << l << "\n"; }
virtual ~Child2() { std::cout << "Destructor Child2\n"; }
virtual void print() { std::cout << "Print Child2: " << data << "\n"; }
};
struct Child3 : public Base {
Child3(int d, long l, char c, std::string s) : Base(d) { std::cout << "Constructor Child3 " << d << " " << l << " " << c << " " << s << "\n"; }
virtual ~Child3() { std::cout << "Destructor Child3\n"; }
virtual void print() { std::cout << "Print Child3: " << data << "\n"; }
};
using Ret = std::unique_ptr<Base>;
template <typename ... Args>
using Func = std::function<Ret(Args...)>;
// ---------------------------------------------------------------------------------------------------
// Hide away the different signatures for std::function
class Creator
{
public:
Creator() {}
// I want to combine the follwong 2 constructors in one
template<typename Function>
Creator(Function&& fun) : Creator(std::function(fun)) {}
template<typename ... Args>
Creator(Func<Args...> fun) : m_any(fun) {}
template<typename ... Args>
Ret operator()(Args ... args) { return std::any_cast<Func<Args...>>(m_any)(args...); }
protected:
std::any m_any;
};
template <class Child, typename ...Args>
std::unique_ptr<Base> createClass(Args...args) { return std::make_unique<Child>(args...); }
// The Factory ----------------------------------------------------------------------------------------
template <class Key>
class Factory
{
std::map<Key, Creator> selector;
public:
Factory(std::initializer_list<std::pair<const Key, Creator>> il) : selector(il) {}
template <typename ... Args>
Ret createInstance(Key key, Args ... args) {
if (selector.find(key) != selector.end()) {
return selector[key](args ...);
}
else {
return std::make_unique<Base>(0);
}
}
};
int main()
{
Factory<int> factory {
{1, createClass<Child1, int, std::string>},
{2, createClass<Child2, int, char, long>},
{3, createClass<Child3, int, long, char, std::string>}
};
// Some test values
std::string s1(" Hello1 "); std::string s3(" Hello3 ");
int i = 1; const int ci = 1; int& ri = i; const int& cri = i; int&& rri = 1;
std::unique_ptr<Base> b1 = factory.createInstance(1, 1, s1);
std::unique_ptr<Base> b2 = factory.createInstance(2, 2, '2', 2L);
std::unique_ptr<Base> b3 = factory.createInstance(3, 3, 3L, '3', s3);
b1->print();
b2->print();
b3->print();
b1 = factory.createInstance(2, 4, '4', 4L);
b1->print();
return 0;
}
So, again, I would like to get rid of the 2 constructors
template<typename Function>
Creator(Function&& fun) : Creator(std::function(fun)) {}
template<typename ... Args>
Creator(Func<Args...> fun) : m_any(fun) {}
and have only one in the end. How?
Sorry for the lengthy code.
How about simply removing the constructor taking the std::function?
template<typename Function>
Creator(Function&& fun) : m_any(std::function(fun)) {}
This will wrap fun only if it's not already a std::function. For cases if it's a std::function then it's a noop.
Live example
Looking at your code, you only use function pointers as your factory function. You could save yourself some indirections using function pointers directly instead of std::function, which is already a layer like std::any.
template <typename ... Args>
using Func = std::add_pointer_t<Ret(Args...)>;
// in you class:
template<typename Function, std::enable_if_t<std::is_pointer_v<Function>, int> = 0>
Creator(Function fun) : m_any(fun) {}
If you use that, in C++20 you can remove the ugly enable if and use a concept:
template<typename T>
concept FunctionPointer = requires(T t) {
requires std::is_pointer_v<T>;
requires std::is_function_v<std::remove_pointer_t<T>>;
{ std::function(t) };
};
// then in your class:
Creator(FunctionPointer auto fun) : m_any(fun) {}
Live example
I have a such factories, which instantiate objects by passed template class name T:
template<class T>
class Factory0
{
public:
static void *Create(){ return new T(); }
};
template<class T, class Argument1>
class Factory1
{
public:
static void *Create( Argument1 &arg1 ){ return new T( arg1 ); }
};
And i need to do something like such:
map<string[ClassName], &factory] _builder;
...
template<class T>
Add(){
if( T derived from BaseClass ) _builder[T] = &factory1::Create
else if( T derived from BaseClass ) _builder[T] = &factory0::Create;
}
template<class T>
Create() {
return _builder[T]( (factory0) ? <nothing> : <some_argument> );
}
This is hard for two reasons:
Calling create with the wrong arguments can only be caught at runtime, so we need a bit of dynamic typing.
C++ really doesn't like casting function pointers. Or creating pointers to templated functions. Or generally doing anything complex with function pointers.
But it can be done:
#include<string>
#include<map>
#include<iostream>
using namespace std;
struct returnable {
// Put some interesting virtual functions here
};
struct foo : public returnable {
foo() {
cout << "defaulFoo" << endl;
}
foo(int x) {
cout << "Foo:" << x << endl;
}
};
struct bar : public returnable {
bar(char a, char b){
cout << "bar:" << a << "," << b << endl;
}
};
template<typename... ARGS>
struct newmakerbase {
virtual returnable* make(ARGS... args) = 0;
};
template<typename OUT, typename... ARGS>
struct newmaker : public newmakerbase<ARGS...> {
virtual returnable* make(ARGS... args) {
return new OUT(args...);
}
};
// Boost might provide a neater version of this
int nextId = 0;
template<typename... T>
struct typeId {
static const int id;
};
template<typename... T>
const int typeId<T...>::id = nextId++;
map<string,void*> builders;
map<string,int> argtypes;
template<typename OUT, typename... ARGS>
void registerClas(string name) {
builders[name] = static_cast<void*>(new newmaker<OUT,ARGS...>());
argtypes[name] = typeId<ARGS...>::id;
}
template<typename... ARGS>
returnable* create(string name, ARGS... args) {
int argsgiven = typeId<ARGS...>::id;
if (argsgiven != argtypes[name]) {
// TODO: maybe throw an exception or something?
return NULL;
}
newmakerbase<ARGS...>* builder = static_cast<newmakerbase<ARGS...>*>(builders[name]);
return builder->make(args...);
}
main() {
registerClas<foo>("defaultFoo");
registerClas<foo,int>("foo");
registerClas<bar,char,char>("bar");
returnable* a = create("defaultFoo");
returnable* b = create("foo", 42);
returnable* c = create("foo", 'a', 'b'); // returns NULL
returnable* d = create("foo", 42.0); // also returns NULL
returnable* e = create("bar", 'c', 'd');
cout << a << " " << b << " " << c << " " << d << " " << e << endl;
}