Templated virtual member functions

M

Matthias

Hello,

templated virtual member functions are not allowed. Now I am searching
for a good workaround for this problem, but I can't find any.
Here's my problem:

class Base
{
template <typename T>
virtual void setParam(std::string s, const T& value);
};

clas Derived : public Base
{
template <typename T>
void setParam(std::string s, const T& value);
};

Base* b = new Derived;
b->setParam( "msg", 5 );

How can I make this work?

-Matthias
 
V

Victor Bazarov

Matthias said:
templated virtual member functions are not allowed. Now I am searching
for a good workaround for this problem, but I can't find any.
Here's my problem:

class Base
{
template <typename T>
virtual void setParam(std::string s, const T& value);
};

clas Derived : public Base
{
template <typename T>
void setParam(std::string s, const T& value);
};

Base* b = new Derived;
b->setParam( "msg", 5 );

How can I make this work?

By redesigning your classes. Why do you need it virtual? Why do you need
to derive from Base? Why do you need it a template? You're presenting us
with a "solution" to an unknown problem. We don't know what you're trying
to accomplish. If you explained your design intentions, somebody might be
able to recommend something.

V
 
E

Earl Purple

class Base
{
template <typename T>
virtual void setParam(std::string s, const T& value);
};

and what size do you propose the v-table should be?
clas Derived : public Base
{
template <typename T>
void setParam(std::string s, const T& value);
};

Base* b = new Derived;
b->setParam( "msg", 5 );

How can I make this work?

You could ask how come ostream classes can stream in lots of different
types but yet streaming is a non-virtual function.

Where is the polymorphism? What will happen in different versions of
Base?

With ostream they all do something common - effectively convert the
type to a string then have different ways of writing the string.
 
B

benben

Matthias said:
Hello,

templated virtual member functions are not allowed. Now I am searching
for a good workaround for this problem, but I can't find any.
Here's my problem:

class Base
{
template <typename T>
virtual void setParam(std::string s, const T& value);
};

clas Derived : public Base
{
template <typename T>
void setParam(std::string s, const T& value);
};

Base* b = new Derived;
b->setParam( "msg", 5 );

How can I make this work?

-Matthias

What you want can be done with a bit of a trick. The idea is to build up
runtime information by compile time.

#include <typeinfo> // RTTI
#include <memory> // auto_ptr

class objectwrapper_base // runtime support for type query
{
public:
virtual const void* query_object(std::type_info) const = 0;
virtual ~objectwrapper_base(){}
};

template <typename T> // compile time type information build up
class objectwrapper: public objectwrapper_base
{
const T* ptr;

public:
objectwrapper(const T& t){ptr = &t;}

const void* query_object(std::type_info ti) const{
if (ti == typeid(T))
return static_cast<const void*>(ptr);

return 0;
}
};

class envelop // a simple class to wrap the above up into one
{
std::auto_ptr<
objectwrapper_base> wrapper;

public:
template <typename T>
envelop(const T& obj):
wrapper(new objectwrapper<T>(obj))
{}

template <typename T> // just a wrapper function
const T* query_object(void) const{
return static_cast<const T*>(
wrapper->query_object(typeid(T)));
}
};


// and for your problem, we just use class envelop
// to achieve the template effect

class Base
{
public:
virtual void setParam(std::string s, envelop elp);
};

class Derived : public Base
{
public:
void setParam(std::string s, envelop elp);
};


For production code you may consider boost::any to be a much polished
alternative.

Regards,
Ben
 
M

Matthias

I am sorry for the very late reply.

My problem: Basically I have different implementations of so called
"effect files". I should be able to set parameters of an arbitrary type
in them (via the setParam function). For example one implementation has
a Python backend. The python backend can take almost any c++ object and
passes it to the python side (I wrap my application with swig if that
helps you). Other implementations of effect files should also be able
to the same. That's why I wanted to put this into an interface-like
base class so that I can set params to an effect file, no matter which
implementation is working behind the scenes. Creating lots of overloads
for every possible type is not an option here.
 
M

Matthias

This looks like a compile time version of dynamic_cast.
The problem here is that I need to pass elp into another templated
function. This means I would need to do something like this in the
Derived implementation:

setParam(std::string s, envelop elp)
{
createPythonObject( elp.query_object() );

This won't compile because I have to do something like
createPythonObject( elp.query_object<Type>() ); The createPythonObject
template has lots of different specializations depending on the type of
the class that was wrapped by elp.
 
B

benben

Matthias said:
This looks like a compile time version of dynamic_cast.
The problem here is that I need to pass elp into another templated
function. This means I would need to do something like this in the
Derived implementation:

setParam(std::string s, envelop elp)
{
createPythonObject( elp.query_object() );

This won't compile because I have to do something like
createPythonObject( elp.query_object<Type>() ); The createPythonObject
template has lots of different specializations depending on the type of
the class that was wrapped by elp.

Ok, here's the trick: bring every essential operation up to
objectwrapper_base class. For example:

class objectwrapper_base // runtime support for type query
{
public:
virtual const void* query_object(std::type_info) const = 0;
virtual ~objectwrapper_base(){}

// the following member function is newly added:
virtual void createPythonObject(void);
};

Now in the corresponding objectwrapper template, implement the member:

template <typename T> // compile time type information build up
class objectwrapper: public objectwrapper_base
{
const T* ptr;

public:
objectwrapper(const T& t){ptr = &t;}

const void* query_object(std::type_info ti) const{
if (ti == typeid(T))
return static_cast<const void*>(ptr);

return 0;
}

// newly implemented function:
void createPythonObject(void){
::createPythonObject(*ptr);
}
};

Finally, you need to wrap these in the envelop class:

class envelop // a simple class to wrap the above up into one
{
std::auto_ptr<
objectwrapper_base> wrapper;

public:
template <typename T>
envelop(const T& obj):
wrapper(new objectwrapper<T>(obj))
{}

template <typename T> // just a wrapper function
const T* query_object(void) const{
return static_cast<const T*>(
wrapper->query_object(typeid(T)));
}

// newly added function
void createPythonObject(void){
wrapper->createPythonObject();
}
};

Now all you need in SetParam is hence:

setParam(std::string s, envelop elp){
// createPythonObject( elp.query_object() );
elp.createPythonObject();
// ...
}

Regards,
Ben
 
M

Matthias

Hmmm, I see a problem with this approach. If I understand your
proposal properly, I go like this:

class Base
{
virtual
void setParam(string s, envelop elp) = 0;
};

class Derived1
{
void setParam(string s, envelop elp)
{
elp.createPythonObject();
}
};

class Derived2
{
void setParam(string s, envelop elp)
{
elp.doSomethingElse();
}
};

This means, whenever I add/remove/change a derived class, my elp class
also needs to change. This seems to violate encapsulation (derived
classes can use methods from other derived classes) and makes it
impossible to use libraries (think if envelop is compiled into one
lib, somebody wants to create another derived class but can't modify
envelop anymore...)

I can't find a good solution to this problem. Maybe it's not easily
possible in C++. All I want to tell it is to have a base class which
requires derived classes to implement a certain template.

I can see the problem about the vtable, but maybe there are workarounds
like benben posted. Maybe I am thinking weird, but sometimes I have the
slight feeling C++ doesn't adapt well to human patterns of thinking and
ideas of implementation.

-Matthias
 
B

benben

Matthias said:
Hmmm, I see a problem with this approach. If I understand your
proposal properly, I go like this:

class Base
{
virtual
void setParam(string s, envelop elp) = 0;
};

class Derived1

I think you mean class Derived1: public Base
{
void setParam(string s, envelop elp)
{
elp.createPythonObject();
}
};

class Derived2

// :public Base
{
void setParam(string s, envelop elp)
{
elp.doSomethingElse();
}
};

Basically, yes.
This means, whenever I add/remove/change a derived class, my elp class
also needs to change. This seems to violate encapsulation (derived
classes can use methods from other derived classes) and makes it
impossible to use libraries (think if envelop is compiled into one
lib, somebody wants to create another derived class but can't modify
envelop anymore...)

I can't find a good solution to this problem. Maybe it's not easily
possible in C++. All I want to tell it is to have a base class which
requires derived classes to implement a certain template.

You had the solution -- the envelop class. In fact, it is very similar
to what a script interpreter will do with variables.

Perhaps I gave you a wrong example of how to use the envelop class,
perhaps you are trying to abstract at the wrong spot. A supposed way of
using an envelop through casting:

if (int* pi = elp.retrieve<int>())
createPythonObject<int>(*pi);
else if (string* ps = elp.retrieve<string>())
createPythonObject<string>(*ps);
// etc

Note the finite set of types you can deal with. But I suspect how much
createPythonObject template can deal with any type.
I can see the problem about the vtable, but maybe there are workarounds
like benben posted. Maybe I am thinking weird, but sometimes I have the
slight feeling C++ doesn't adapt well to human patterns of thinking and
ideas of implementation.

Perhaps you should just tell us why you want to do sub-typing and
subsequently have to resolve to using virtual functions. It seems that
you problem can very simply overcome by not using virtual functions at all.
-Matthias

Ben
 
M

Matthias

First of all, thanks for looking into this even more.

I see that I can query for each type manually, but then I could just go
without the envelop class and write a virtual function for every type.
createPythonObject can deal with almost any type that I use in my
program, including complex classes.

I'll try to re-explain what I am trying to achieve:

There's a so called effect-file which you can simply think of as a
parameter collection for now. It will take parameters of different
types and do something with them. Now there is probably more than one
effect-file implementation. Each implementation does different things
with the parameters passed to it. For example I mentioned the python
implementation which creates python objects of the passed variables.
There might be another implementation creating Perl objects or even a
C++ implementation which stores the variables into std::maps (one for
each type).
The caller should not have to care which implementation he is using,
that's why there's an abstract base class.
Like

void DoSomething(Base* b)
{
b->SetParameter("bla", 11);
}

Base* b = new Derived1;
DoSomething(b);
delete b;

b = new Derived2;
DoSomething(b);
delete b;

The user should be able to call setParameter on b for a whole bunch of
types. If a type is not supported by the underlying implementation then
the compiler should barf, otherwise it should compile.

Does this make sense?

I thought about replacing the virtual functions somehow with templates,
but didn't come up with anything that makes sense.

Thanks again for your kind help!

-Matthias
 
B

benben

void DoSomething(Base* b)
{
b->SetParameter("bla", 11);
}

Ok, I roughly see what your problem is. I still don't get why you need
to use an abstract base, wouldn't the following just do the trick?

template <typename EffectFileImplT>
void DoSomething(EffectFileImplT& ef)
{
ef.SetParameter("bla", 11); // non-virtual call
}
Base* b = new Derived1;
DoSomething(b);
delete b;

b = new Derived2;
DoSomething(b);
delete b;

Effect1 ef1; // non-polymorphic type
DoSometing(ef1);

Effect2 ef2; // non-polymorphic type
DoSomething(ef2);


Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,226
Members
46,815
Latest member
treekmostly22

Latest Threads

Top