I want to store some data to container. For example I have such code:
#include <iostream>
#include <string>
#include <memory>
#include <map>
class Base
{
public:
Base() {}
virtual ~Base() {}
};
class Class1 : public Base
{
public:
Class1() : Base() {}
~Class1() {}
};
class Class2 : public Base
{
public:
Class2() : Base() {}
~Class2() {}
};
class Class3 : public Base
{
public:
Class3() : Base() {}
~Class3() {}
};
std::map<std::string, std::shared_ptr<Base>> myContainer;
void save(const std::string& id, std::shared_ptr<Base> obj)
{
auto obj1 = std::dynamic_pointer_cast<Class1>(obj);
if (obj1)
{
std::cout << "save obj1" << std::endl;
myContainer.emplace(std::piecewise_construct,
std::make_tuple(id),
std::make_tuple(std::move(obj1))
);
}
auto obj2 = std::dynamic_pointer_cast<Class2>(obj);
if (obj2)
{
std::cout << "save obj2" << std::endl;
myContainer.emplace(std::piecewise_construct,
std::make_tuple(id),
std::make_tuple(std::move(obj2))
);
}
auto obj3 = std::dynamic_pointer_cast<Class3>(obj);
if (obj3)
{
std::cout << "save obj3" << std::endl;
myContainer.emplace(std::piecewise_construct,
std::make_tuple(id),
std::make_tuple(std::move(obj3))
);
}
}
int main()
{
std::shared_ptr<Class1> a1 = std::make_shared<Class1>();
std::shared_ptr<Class2> a2 = std::make_shared<Class2>();
std::shared_ptr<Class3> a3 = std::make_shared<Class3>();
save("id1", a1);
save("id2", a2);
save("id3", a3);
std::cout << "size is " << myContainer.size() << std::endl;
return 0;
}
But function save() has too much complicated implementation. How to make it easier? Somehow to get correct object type and invoke save() once but not in every checking. Maybe it possible to implement it with std::variant or std::tuple? What is much optimized solution you can propose?
You seem to understand virtual functions.
Your entire save function could be implemented as:
void save(const std::string& id, std::shared_ptr<Base> obj)
{
std::cout << "save " << obj->name() << std::endl;
myContainer.emplace(std::piecewise_construct,
std::make_tuple(id),
std::make_tuple(std::move(obj))
);
}
name() would be a virtual function that returns the correct string for the type.
Note that this implementation always saves the pointer passed to it, while your implementation may not save anything.
Assuming you've provide a shared pointer containing the real class instead of a std::shared_ptr<Base> when calling the function, you can rewrite this as a template:
template<class T>
char const* TypeName();
template<>
char const* TypeName<Class1>() { return "obj1"; }
template<>
char const* TypeName<Class2>() { return "obj2"; }
template<>
char const* TypeName<Class3>() { return "obj3"; }
template<class T>
void save(const std::string& id, std::shared_ptr<T> obj)
{
std::cout << "save " << TypeName<T>() << std::endl;
myContainer.emplace(std::piecewise_construct,
std::make_tuple(id),
std::make_tuple(std::move(obj))
);
}
Related
I have something like this
class myclass
{};
int main()
{
std::string mystring("myclass");
return 0;
}
And I want to do something like std::shared_ptr<mystring> mysharedptr; that is equal to std::shared_ptr<myclass> mysharedptr;. Do you have an idea of how I should proceed?
I want to be able to do something like this because I have 8 classes and I should create a pointer depending of the name. In order to not have a huge structure with a lot of if/else if, I was wondering if there is a solution.
So to answer my question (this works) :
#include <map>
#include <memory>
#include <string>
class Base
{
public:
using Ptr = std::shared_ptr<Base>;
};
class DerivedA : public Base
{
public:
DerivedA() { std::cout << "DerivedA" << std::endl; }
};
class DerivedB : public Base
{
public:
DerivedB() { std::cout << "DerivedB" << std::endl; }
};
template<typename T>
std::shared_ptr<Base> myCreate()
{
return std::make_shared<T>();
}
int main()
{
static const std::map<std::string, std::shared_ptr<Base> (*)()> myMap
{
{"DerivedA", &myCreate<DerivedA>},
{"DerivedB", &myCreate<DerivedB>}
};
Base::Ptr first, second;
first = myMap.find("DerivedA")->second();
std::cout << first << std::endl;
second = myMap.find("DerivedA")->second();
std::cout << second << std::endl;
return 0;
}
I need to implement one abstract class, three its concrete subclasses, class which goal to create one of this three classes instances and last class executor of three classes. Requirements are c++98, and not to use if/elseif/else to construct class instance, like i did in a Maker class method make Form. What mechanism i need to avoid if / elseif / else?
For example:
test.h
#ifndef TEST_H
#define TEST_H
#include <iostream>
class Executor {
private:
const std::string name;
public:
Executor(const std::string &name = "") {};
const std::string getname() const {return name;}
};
class BForm {
private:
const std::string _name;
public:
BForm(const std::string &name = "") : _name(name) {};
virtual ~BForm() {};
virtual void execute(const Executor &src) = 0;
const std::string getname() {return _name;}
virtual const std::string gettarget() = 0;
};
class Form1 : public BForm{
private:
std::string _target;
public:
Form1(const std::string &target = "") : BForm("form1"), _target(target) {};
virtual ~Form1() {};
virtual void execute(const Executor &src) {
std::cout << src.getname() << " exec form1 target:" << _target << std::endl;
}
virtual const std::string gettarget() {return _target;}
};
class Form2 : public BForm {
private:
std::string _target;
public:
Form2(const std::string &target = "") : BForm("form2"), _target(target) {};
virtual ~Form2() {};
virtual void execute(const Executor &src) {
std::cout << src.getname() << " exec form2 target:" << _target << std::endl;
};
virtual const std::string gettarget() {return _target;}
};
class Form3 : public BForm {
private:
std::string _target;
public:
Form3(const std::string &target = "") : BForm("form3"), _target(target) {};
virtual ~Form3() {};
virtual void execute(const Executor &src) {
std::cout << src.getname() << " exec form3 target:" << _target << std::endl;
};
virtual const std::string gettarget() {return _target;}
};
class Maker {
public:
BForm *makeForm(const std::string &name, const std::string &target)
{
/* need to avoid */
if (name == "form1")
return new Form1(target);
else if (name == "form2")
return new Form2(target);
else
return new Form3(target);
}
};
#endif
main.cpp
#include "test.h"
int main() {
Maker maker;
BForm *form;
Executor exec("executor");
form = maker.makeForm("form1", "A");
std::cout << form->getname() << " " << form->gettarget() << std::endl;
form->execute(exec);
delete form;
return (0);
}
You could typedef a pointer to function and then use a map from string to this type (pointer to function). And then use your parameter with indexer syntax to access the correct pointer to function.
Here is an example:
#include <iostream>
#include <map>
// The class definitions with a virtual function hello() common to all
class Base { public: virtual void hello() = 0; };
class Derived1 : public Base { public: void hello() { std::cout << "Derived1"; } };
class Derived2 : public Base { public: void hello() { std::cout << "Derived2"; } };
// The object making functions
Base* Maker1() { return new Derived1; }
Base* Maker2() { return new Derived2; }
int main()
{
// In C++98, without auto, it's worthwhile to typedef complicated types.
// The first one is a function type returning a pointer to Base...
typedef Base* MakerT();
// ... the second one is a map type projecting strings to such function pointers
typedef std::map<std::string, MakerT*> StrToMakerT;
/// The actual map projecting strings to maker function pointers
StrToMakerT strToMaker;
// Fill the map
strToMaker["D1"] = &Maker1;
strToMaker["D2"] = &Maker2;
// user input
std::string choice;
// as long as output works, input works, and the user didn't say "Q":
while (std::cout << "Please input 'D1' or 'D2' or 'Q' for quit: "
&& std::cin >> choice
&& choice != "Q")
{
// Prevent adding new entries to the map foir unknown strings
if (strToMaker.find(choice) != strToMaker.end())
{
// Simply look the function up again, the iterator type is too
// cumbersome to write in C++98
Base* b = (*strToMaker[choice])();
b->hello();
std::cout << '\n';
delete b;
}
else
{
std::cout << "Didn't find your choice, try again.\n";
}
}
std::cout << "Thank you, good bye\n";
}
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;
}
Let's say I have class Action
template<class T>
class Action {
public:
virtual ~Action() = default;
virtual void execute(T &object) const = 0;
};
which can be executed on some object of type T
Next, I have class Object
class Object {
public:
Object() : actions() {}
virtual ~Object() = default;
virtual const std::string getName() const = 0;
void addAction(const Action<Object> *action) {
actions.push_back(action);
}
void execute() {
for (auto &action : actions) {
action->execute(*this);
}
}
private:
std::vector<const Action<Object> *> actions;
};
which holds a vector of actions which can be executed at once.
Now, I have some concrete ObjectA
class ObjectA : public Object {
public:
const std::string getName() const override {
return "ObjectA";
}
};
and two concrete actions ActionA, ActionB
class ActionA : public Action<ObjectA> {
void execute(ObjectA &object) const override {
std::cout << "ActionA on " << object.getName() << std::endl;
}
};
class ActionB : public Action<ObjectA> {
void execute(ObjectA &object) const override {
std::cout << "ActionB on " << object.getName() << std::endl;
}
};
The usage is that I create an ObjectA, add both action to it and execute them.
int main() {
ObjectA object = ObjectA{};
object.addAction(reinterpret_cast<const Action<Object> *>(new ActionA()));
object.addAction(reinterpret_cast<const Action<Object> *>(new ActionB()));
// This is what I want to achieve instead of using reinterpret_cast
//object.addAction(new ActionA());
//object.addAction(new ActionB());
object.execute();
}
The output should be
ActionA on ObjectA
ActionB on ObjectA
The problem is that in order to compile it, I must use reinterpret_cast. The problem is probably the definition of std::vector<const Action<Object> *> actions; I would like to template this, so in ObjectA it is like std::vector<const Action<ObjectA> *> actions;
Is something like that possible?
Action<Object> and Action<ObjectA> are unrelated types in the C++ type system.
What more, Action<Object> permits itself to be called with more types than Action<ObjectA>. So ignoring the type system, an Action<ObjectA> cannot implement the contract that Action<Object> promises it can fulfill.
However, an Action<Object> can fulfill the promise that an Action<ObjectA> makes.
There are famous two kinds of OO-type relations; covariance and contravariance. Action<T> is cotravariant in T, Action<Base> can be used to fulfill the contract of Action<Derived>.
So an approach.
First, your Action<T> is a poorly written pointer-semantic version of std::function<void(T&)>. Use that instead.
You now have value semantics.
template<class T>
using Action=std::function<void(T&)>;
class Object {
public:
Object() = default;
virtual ~Object() = default;
virtual const std::string getName() const = 0;
void addAction(Action<Object> action) {
actions.emplace_back(std::move(action));
}
void execute() {
for (auto &action : actions) {
action(*this);
}
}
private:
std::vector<Action<Object>> actions;
};
ah, much nicer.
This doesn't, however, solve your problem.
auto ActionA = Action<ObjectA>{
[](ObjectA &object) {
std::cout << "ActionA on " << object.getName() << std::endl;
}
};
ActionA cannot be assigned to an Action<Object> because an Action<Object> can be passed a non-ObjectA and it must do something with it.
Your original code's override won't compile.
We have to decide if we want to pretend to be an Action<Object> what we should do if the types mismatch? Here is one option:
template<class T, class F>
auto only_when_dynamic_type_matches( F&& f ) {
if constexpr( std::is_pointer< T >{} ) {
return
[f=std::forward<F>(f)](auto* x)->void{
auto* t = dynamic_cast<T>(x);
if (!t) return
f(t);
};
} else {
return
[f=std::forward<F>(f)](auto&& x)->void{
auto* t = dynamic_cast<std::remove_reference_t<T>*>(std::addressof(x));
if (!t) return;
f(*t);
};
}
}
now we can write
auto ActionA = only_when_dynamic_type_matches<ObjectA&>([](auto&&object) {
std::cout << "ActionA on " << object.getName() << std::endl;
});
auto ActionB = only_when_dynamic_type_matches<ObjectA&>([](auto&&object) {
std::cout << "ActionB on " << object.getName() << std::endl;
});
then
int main() {
ObjectA object = ObjectA{};
object.addAction(ActionA);
object.addAction(ActionB);
object.execute();
}
Live example.
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.