Search code examples
c++templatesshared-ptr

Different templated class in a set using boost::shared_ptr


I have some design problems, I thought one of you might have some clue to help me.

I tried to summarize my problem to this simple example :

I have two different class DerivedOne and DerivedTwo which inherit from the same Base class and share the definition of a method.
I have a set of shared_ptr pointing to client, which have one object from two different class DerivedOne and DerivedTwo.
Because I don't want to use a Base* pointer to hold this 2 different class, I tried to make the client class template.

But I have 2 different class, and I can't hold them in the same set.
I thought shared_ptr could hold an object pointer without specifying the template argument, but I was wrong, or I don't know how to do.

The other solution I see, is to separate those 2 different client in 2 different set

Thanks in advance for any suggestions.

Here is the code :

#include <iostream>
#include <set>
#include <boost/shared_ptr.hpp>

class Base
{
    public:
        virtual void test() = 0;
};

class Derived_one
    : public Base
{
    public:
        void test() {
            std::cout << "Derived One" << std::endl;
        }            
};

class Derived_two
    : public Base
{
    public:
        void test() {
            std::cout << "Derived Two" << std::endl;
        }
};

template <class temp_arg>
class Client
{
    public:        
        int test(){
            obj_.test();
        }

    protected:
        temp_arg obj_;

};

typedef Client<Derived_one> ClientOne;
typedef Client<Derived_two> ClientTwo;    

/// Here I don't want to specify any of the two previously declared client :
//typedef boost::shared_ptr<Client> Client_ptr;
typedef boost::shared_ptr<ClientOne> Client_ptr;

int main(int, const char**)
{
    std::set<Client_ptr> Clients_;
    Client_ptr client_(new ClientOne());

    Clients_.insert(client_);
    client_->test();

    return 0;
}

If you want to give a look to the real code :
https://github.com/gravitezero/Node/tree/experimental/src
Client correspond to connection
The Base class is message
The two derived class are peply and request.


Solution

  • The boost shared template pointers wont perform any magic that you can't do without regular pointers (namely putting two different types into the same collection). Whenever using a template you must explicitly declare the template arguments, because alls a template does is generate duplicate code for you at compile time. So when you use a template, the compiler simply duplicates the template file as many times as you have declared it with the template arguments. No template arguments, no duplication, no template. At least this is my understanding, I'm sure the C++ Police will correct me if I'm wrong.

    So the way to put two different types into the same collection is to mask the value being placed in the collection. The most basic way of doing this (and bad way) is to make your collection of void pointers and then cast on insert and extraction depending on the context. This is dangerous, because then you will be accessing memory through the pointer without checking that the pointer points to the correct amount/format of memory (this is type checking).

    So the real way to do this is as pmr suggested. Make your collection a collection of base type pointers and use dynamic cast to cast between the types when inserting and extracting from the list. A dynamic cast will check at run-time to make sure that the type you are casting from and to are compatible. IE if you cast from Derived1 to Base, and then later from Base to Derived1 this is okay. If you cast from Derived1 to base, and then later from base to Derived2 on a pointer pointing to the same memory, this will fail at run-time, because Derived2 and Derived1 are not compatible.

    This explanation is a little long, because I had the same design trouble a few months ago, until I did some reading.