Pasha
Pasha

Reputation: 89

store shared_ptr to container with base class

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?

Upvotes: 0

Views: 97

Answers (2)

fabian
fabian

Reputation: 82461

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))
    );
}

Upvotes: 1

Drew Dormann
Drew Dormann

Reputation: 63830

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.

Upvotes: 2

Related Questions