user987280
user987280

Reputation: 1608

sharing template parameters across multiple class member functions

Sorry if this question is a bit confusing. I'm really asking for learning purposes and to see if there is a way that this would even be possible.

In the class below, I have to specify a template parameter for CreateObject() and GetObject():

class ObjectManager
{
public:
    template <class T>
    void CreateObject(int id)
    {
        //Store pointer to newly created object in a map
    }

    template <class T>
    T* GetObject(int id)
    {
        //Find object using id and return a T* to it
    }
};

CreateObject() uses the template parameter to create the correct type of object and then stores a pointer to it in a map. GetObject() uses the template parameter to return a pointer of the desired type. For some objects, I want GetObject() to return a pointer to the actual object type. For other objects, I want GetObject() to return a pointer to a parent type.

As of right now, any time I call GetObject(), I need to specify the type:

SomeType* pST = m_objectManager.GetObject<SomeType>(id);
AnotherType* pAT = m_objectManager.GetObject<AnotherType>(id);

My goal would be to specify the actual object type and the desired return type as template parameters in my CreateObject() function:

class ObjectManager
{
public:
    template <class T, class DesiredReturnType>
    void CreateObject(int id)
    {
        //Store pointer to newly created object of type T in a map
        //The DesiredReturnType would be used by GetObject() to return the correct type of pointer
    }

    //This isn't possible but would be ideal.
    DesiredReturnType* GetObject(int id)
    {
        //Return a DesiredReturnType* to the object
    }
};

m_objectManager.CreateObject<SomeType, ParentOfSomeType>(id);

//Now I don't need to specify a template argument when calling GetObject().
//CreateObject() will create a new SomeType.
//GetObject() will return a ParentOfSomeType* which is really pointing to a SomeType object
ParentOfSomeType* pPST = m_objectManager.GetObject(id);

Since every object has a different type and a different desired return type, I wouldn't be able to use class template parameters. The type and desired return return type would always be changing depending on what type of objects I was creating.

Would something like this be possible? Is there some kind of design pattern which would help in a situation like this?

EDIT:

The reason for the design choice is as follows. I will be calling another function which has different behavior depending on whether is receives a Parent* or a Child*.

By taking out the template argument, I thought that I might be able to do something like this:

for(int id = 0; id < 10; id++)
MyFunction(m_objectManager.GetObject(id));

It probably doesn't change the fact that it's a bad decision choice, but I'm mostly asking out of curiosity. :)

Upvotes: 2

Views: 837

Answers (3)

Evgeny Panasyuk
Evgeny Panasyuk

Reputation: 9199

Try visitor pattern. Live example at Ideone.com

struct ConcreteVisitor: IVisitor
{
    virtual void visit(int)
    {
        cout << "visiting int" << endl;
    }
    virtual void visit(double)
    {
        cout << "visiting double" << endl;
    }
    virtual void visit(SomeClass&)
    {
        cout << "visiting SomeClass" << endl;
    }
    // ..
};
int main(int argc,char *argv[])
{
    ObjectManager manager;
    ConcreteVisitor visitor;

    manager.create_object<int>(1);
    manager.create_object<double>(2);
    manager.create_object<SomeClass>(3);

    manager.apply(1,visitor);
    manager.apply(2,visitor);
    manager.apply(3,visitor);
    return 0;
}

Output is:

SomeClass::SomeClass()
visiting int
visiting double
visiting SomeClass
SomeClass::~SomeClass()

Full code:

#include <boost/shared_ptr.hpp>
#include <boost/ptr_container/ptr_map.hpp>
#include <iostream>
#include <ostream>
#include <map>

using namespace std;
using namespace boost;
typedef int ID;

struct SomeClass
{
    SomeClass()
    {
        cout << "SomeClass::SomeClass()" << endl;
    }
    ~SomeClass()
    {
        cout << "SomeClass::~SomeClass()" << endl;
    }
};

struct IVisitor
{
    virtual void visit(int)=0;
    virtual void visit(double)=0;
    virtual void visit(SomeClass&)=0;
    // ..
};

struct ConcreteVisitor: IVisitor
{
    virtual void visit(int)
    {
        cout << "visiting int" << endl;
    }
    virtual void visit(double)
    {
        cout << "visiting double" << endl;
    }
    virtual void visit(SomeClass&)
    {
        cout << "visiting SomeClass" << endl;
    }
    // ..
};

struct ITypeStorage
{
    virtual void apply_visitor(void *obj,IVisitor &visitor)=0;
};

template<typename ObjectType> struct TypeStorage: ITypeStorage
{
    virtual void apply_visitor(void *obj,IVisitor &visitor)
    {
        visitor.visit( *(static_cast<ObjectType *>(obj)) );
    }
};

class ObjectManager
{
    map<ID,boost::shared_ptr<void> > objects;
    ptr_map<ID,ITypeStorage> types;
public:
    template <class T>
    void create_object(ID id)
    {
        objects[id].reset(new T());//shared_ptr will use right deleter
        types.insert(id,new TypeStorage<T>());
    }
    void apply(ID id,IVisitor &visitor)
    {
        types.find(id)->second->apply_visitor(objects[id].get(),visitor);
    }
};

int main(int argc,char *argv[])
{
    ObjectManager manager;
    ConcreteVisitor visitor;

    manager.create_object<int>(1);
    manager.create_object<double>(2);
    manager.create_object<SomeClass>(3);

    manager.apply(1,visitor);
    manager.apply(2,visitor);
    manager.apply(3,visitor);
    return 0;
}

Upvotes: 0

PiotrNycz
PiotrNycz

Reputation: 24392

What about class template:

class ObjectManager
{
public:
   /// reference to some internal object manager 
   template <class T, class DesiredReturnType>
   class ObjectRef {
   public:
     ObjectRef(ObjectManager& manager) {}
     DesiredReturnType* GetObject()
     {
     }
   };
    template <class T, class DesiredReturnType>
    ObjectRef<T,DesiredReturnType> CreateObject(int id)
    {
    }
};

auto ref = m_objectManager.CreateObject<SomeType, ParentOfSomeType>(id);

//Now I don't need to specify a template argument when calling GetObject().
//CreateObject() will create a new SomeType.
//GetObject() will return a ParentOfSomeType* which is really pointing to a SomeType object
ParentOfSomeType* pPST = ref.GetObject(); // no need to specify id...

Upvotes: 0

Paul Groke
Paul Groke

Reputation: 6447

A function in C++ can only have one return-type and that type must be known at compile-time. A function template can have a return type that's dependent on it's template argument(s). Since C++ is statically typed that dependency must be resolved at compile-time. That means you cannot look up the desired return-type from a map at runtime. You can however derive it from the template argument(s).

EDIT: To clarify: when you "use" a function template like FunctionTemplate<Type>() or FunctionTemplate(parameterThatsUsedToDeriveTheTemplateArguments), the function template is instantiated. That means a normal function with the "name" FunctionTemplate<ARG1, ARG2, ...> will be created - the so called "specialization" of the template for the arguments ARG1, ARG2 etc. And this function is just like a normal function, which means it also can have only one fixed return type that must be known at compile time. /EDIT

In other words: GetObject has to have at least one template argument that it uses to derive the return-type. One thing that could work - depending on how you want to use this - would be to encode the desired return-type in the type of the id parameter.

E.g. something like

template <class T>
struct ObjectId {
    typedef T ReturnType;
    ObjectId(int id) : m_id(id) {}
    int m_id;
};

class ObjectManager {
    ...
    template <class T, class ID> // ID type will be deduced
    void CreateObject(ID id) {
        ...
    }

    template <class ID> // ID type will be deduced
    typename ID::ReturnType* GetObject(ID id) {
        ...
    }
};

...
ObjectManager obman;
auto const idFoo = ObjectId<Foo>(1);
auto const idBar = ObjectId<BarBase>(2);

obman.CreateObject<Foo>(idFoo);
obman.CreateObject<Bar>(idBar);
Foo* foo = obman.GetObject(idFoo);
BarBase* bar = obman.GetObject(idBar);

Upvotes: 1

Related Questions