Home > Software design >  store shared_ptr to container with base class
store shared_ptr to container with base class

Time:06-02

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?

CodePudding user response:

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.

CodePudding user response:

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