Wrapping up factory methods - c++

I have 2 structs whose constructors accept different numbers and types of parameters passed in.
For example,
struct A:Base
{
A(int i, char c){}
};
struct B:Base
{
B(char c){}
};
And I also have a factory method to create these 2 structs's objects,
struct F
{
Base* Do(int i)
{
if (i==0)
{
return new A(i,'c');
}
else
{
return new B('c');
}
}
};
Now that I try to improve my Factory F like this
struct F
{
template<int i, class X>
Base* Do();
template<class X>
Base* Do<0>()
{
return new X(i, 'c');
}
template<class X>
Base* Do<1>()
{
return new X('c');
}
};
The only thing I am not interested in this method is that I have to write a lot of specialized templates in case I have plenty of structs A,B,C,... but I only love templates and I don't like return new X(...).
So my questions are:
How can I minimize the long code but still use templates ?
How can I stop returning a pointer to the base class ? Dynamic allocation sux, I prefer slow and tender classic allocators on the stack for my factory.
I like to use variadic templates for the arguments of my structs' ctors.

std::any and variadic templates will be your friend. Yuu can store anything in std::any. The any_cast will be a bit of a problem.
See the following working code:
#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;
}

Related

Elegant way to use std::get to access data in std::variant

I'm a new C++ user from the Java world. Here is the context: Different class needs to implement foo() function from Base class. But this function has different combination of my_type as input. I'm feeling using something like std::get<2>(input[1]) everywhere in the code is not easy to read and understand. Is there a elegant way to access content in an std::variant? Demo https://shorturl.at/tLQV3
#include <iostream>
#include <variant>
#include <vector>
using my_type = std::variant<int, float, bool>;
struct Base {
virtual void foo(std::vector<my_type>) = 0;
virtual ~Base() {};
};
struct B : Base {
static constexpr int kMetricRawDataTypes[] = {2,2,2,2};
void foo(std::vector<my_type> input) {
bool num_0 = std::get<2>(input[0]); // Question1.1 - is there any more elegant way to access the value?
bool num_1 = std::get<2>(input[1]);
bool num_2 = std::get<2>(input[2]);
bool num_3 = std::get<2>(input[3]);
std::cout << "input " << num_0 << " " << num_1 << " " << num_2 << " " << num_3 << std::endl;
}
};
struct A : Base {
static constexpr int kMetricRawDataTypes[] = {1, 2, 0, 1};
void foo(std::vector<my_type> input) {
float num_0 = std::get<1>(input[0]); // Question 1.2 - is there any more elegant way to access the value?
bool num_1 = std::get<2>(input[1]);
int num_2 = std::get<0>(input[2]);
float num_3 = std::get<1>(input[3]);
std::cout << "input " << num_0 << " " << num_1 << " " << num_2 << " " << num_3 << std::endl;
}
};
int main() {
my_type num1 = 1.0f;
my_type num2 = true;
my_type num3 = 5;
my_type num4 = 3.0f;
std::vector<my_type> input = {num1, num2, num3, num4};
A a;
a.foo(input);
return 0;
}
I feel there should be good solution using template. Please advise!
I'd use the validate function in my old answer as input and rewrite it slightly to fit your new class definition. Then to print the contained types, you could fold over operator,. To only have to implement foo once, you could put that in a CRTP base for A and B. I've chosen to make the validate function a member of the CRTP base here.
#include <iostream>
#include <variant>
#include <vector>
using my_type = std::variant<int, float, bool>;
struct Base {
virtual ~Base() = default;
virtual void foo(const std::vector<my_type>& input) const = 0;
};
template<class T> // A CRTP base for A and B
struct foo_impl : Base {
bool validate(const std::vector<my_type>& input) const {
return input.size() == std::size(T::kMetricRawDataTypes)
&& [&]<std::size_t... Is>(std::index_sequence<Is...>) {
// fold over && :
return (... && (input[Is].index() ==
T::kMetricRawDataTypes[Is]));
}(std::make_index_sequence<std::size(T::kMetricRawDataTypes)>());
}
void foo(const std::vector<my_type>& input) const override {
if(validate(input)) {
std::cout << "input";
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
// fold over , (comma) to print:
((std::cout << ' ' <<
std::get<T::kMetricRawDataTypes[Is]>(input[Is])),
...);
}(std::make_index_sequence<std::size(T::kMetricRawDataTypes)>());
std::cout << '\n';
} else {
std::cout << "not valid\n";
}
}
};
struct A : foo_impl<A> { // inherit with `A` as a template parameter
static constexpr int kMetricRawDataTypes[] = {1, 2, 0, 1};
};
struct B : foo_impl<B> { // inherit with `B` as a template parameter
static constexpr int kMetricRawDataTypes[] = {2, 2, 2, 2};
};
Demo

Maintaining a selected object in C++

I wanted to know the best approach to store the reference to an object selected, for more clarity:
#include <iostream>
using namespace std;
class A{
public:
int a;
A(int b)
{
a=b;
}
};
class B{
public:
int b;
B(int a)
{
b=a;
}
};
int main() {
A a(1);
B b(2);
**<<some type>>** x= &a;
//cout<<x.a or x->a<<endl;
x = &b;
//cout<<x.b or x->b;
return 0;
}
Use case:
Actually, I am parsing some formatted strings and returning parsed objects(of classes, A, B in return). While parsing the string I get to know what object to fill and what field to fill, to hold this information(changing at runtime) I am looking for a solution. This basically saves me a switch case, and the application is latency-sensitive.
The described use case sounds to me like an Abstract Factory pattern. You parse something, and then return the appropriate type with some set parameters. And the decision of the type will be made at runtime. By Uuing polymorphism, you can create output as you wish.
Standard problem of abstract factory is that the signature of the constructor must be the same.
But with the below shown solution, you can parse whatever, then instantiate the appropriate type and add parameter values as necessary.
Please check:
#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;
}
If the answer is not appropriate, please inform me and I will delete it.

Alternatives to Factory Method for large projects

I was looking into different methods to implement a pattern that works in loading game save data. For example the player can save the game and it would save their inventory of different items and the type of character the player is.
The items all inherit from a superclass and when the data is saved the name of derived classes are saved into a file. When the data is loaded every data about the items should be converted into their respective derived class objects and stored in a std::vector.
Below is a rough example of what I am trying to achieve (There could be some errors)
Example Save file:
DarkSword
GoldenShield
EyeOfOdin
This is an example code file:
std::vector<Item> inventory;
std::vector<std::string> loadedInventoryData = LoadFromFile("exampleInventoryFile.txt");
for (const auto& textItem : loadedInventoryData)
{
inventory.emplace_back(ObjectFromString(textItem));
}
My main question is about the ObjectFromString method. After some search on the internet I came across the factory pattern which required to make a factory class that returns the correct object using a switch-pattern but I wonder how big would this class grow if there are hundreds if not thousands of items in the game that need to be loaded in? Some suggest a hash_map but that surely also a lot to handle at a larger scales.
Is there a scalable method that would not require the programmer to work with a monstrous switch-block? What methods are used in large games such as Minecraft, World of Warcraft or Terraria?
Thank you and sorry if this is off-topic or in the wrong place.
Abstract Factory is the right pattern.
No switch statement is needed. Usually a std::map is used.
The problem is often the need to use constructors with a different set of parameters. That, because the Object need to be initalized with different number of properties. But this can also be solved easily.
See the below example, which may give you an idea.
#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;
}

Store generic callback in a map

i would like create a map of id / generic callback but I don't know if it's feasible. My idea is like this:
Different king od object
class Class1
{
public:
bool run(const int &temp){
std::cout << "worker1:" << temp << std::endl;
return 0;
}
};
class Class2
{
public:
bool run(const std::string &temp){
std::cout << "worker2:" << temp << std::endl;
return 0;
}
};
The template for the Callback object:
template <typename ReturnType, typename... Args>
class Callback
{
private:
std::function<ReturnType> _function;
public:
Callback(std::function < ReturnType(Args...)> function)
: _function(function){}
auto run(Args... args)
{
return function(std::forward<Args>(args)...);
}
};
an example of use
int main() {
Class1 class1;
Class2 class2;
std::map<int, Callback> cbs;
cbs[1] = std::bind(&Class1::run, &class1, std::placeholders::_1));
cbs[2] = std::bind(&Class1::run, &class1, std::placeholders::_1));
cbs[1].run(1);
cbs[2].run("string msg");
}
Maybe you can implement something with std::any
I created factory where I stored functions with any signature in a map.
Please have a look, if it could give your an idea. Otherwise I will delete this answer
#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;
}

Writing factory method in base class

class Base {
public:
static std::unique_ptr<Base> CreateBase();
}
class Factory {
public:
static std::unique_ptr<Base> CreateBase();
}
We can either declare the factory method in the base class or we can create a separate class and write the factory method inside it. Which one is better and why?
Neither.
You can also declare the factory as a free function std::unique_ptr<Base> CreateBase() a.k.a std::make_unique<Base>
In my very humble opionion the "create" is a function that naturally belongs to the factory. The factory creates something.
It can be implemented in any way. But still I do see it as a part of the factory.
You could also use a more sophisticated solution. With the class to be created defined in a very general manner and using a hybrid approach.
Please see example below:
#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;
}