Is a type-safe callback mechanism possible?

A

Adam Nielsen

Hi everyone,

I'm trying to use type-safe code with templates, but I'm a bit stuck as
to how you can call into a type-specific template when all you have is a
pointer to its base class.

The code below demonstrates the problem, along with the only solution I
have come up with - instead of doing your processing outside the
template class, move it inside the template class. Unfortunately this
means the class must be aware of a whole bunch of things it might be
used for, which isn't really ideal.

I'm hoping someone here might know how I could create a type-safe
callback mechanism, so instead of putting all my code inside the
template class, I can just create a new callback-class and have the
templated code call that instead. Being able to override a virtual
template function would do the job nicely, but apparently that's not
allowed :-(

See below for an example, with more detail in the comments.

Thanks,
Adam.

----

#include <string>
#include <iostream>
#include <typeinfo>

class CDatabase
{
public:
// This function adds some data to a database in a
// type-specific way.
template <typename T>
void add(const T& tData) {
// Add tData into the database, depending on what type it is.
std::cout << "Adding some data of type " << typeid(T).name()
<< std::endl;
}
};

// This is the generic base-class for a database field.
class IDatabaseField
{
public:
// See below for why this is required
virtual void addSelfToDatabase(CDatabase *pDB) = 0;
};

// Since database fields can be different types, we need different
// classes to reflect that.
template <typename T>
class TDatabaseField: public IDatabaseField {
private:
T tData;
public:
void setData(const T& tNewData) {
this->tData = tNewData;
}
T getData(void) {
return this->tData;
}

// Again, see below for why this is required
virtual void addSelfToDatabase(CDatabase *pDB) {
// Call a type-specific function in CDatabase depending on
// what type T is in this instance.
pDB->add(this->tData);
}
};

// Now say someone calls this function to add a field into the database.
void addNextFieldToDatabase(IDatabaseField *pField)
{
CDatabase db;

// Here we can't call pField->getData() because we don't know what
// type the data is, so when we call CDatabase::add() we don't know
// what type of variable to pass along.
//
//db->add(???);

// However if we get the class to do the work inside an overridden
// function, that solves the problem.
pField->addSelfToDatabase(&db);

// Of course, every time we want to do something like this (add a
// value to the database, verify the value, export the value, etc.)
// we need to create a new function like addSelfToDatabase.
// Suddenly our database field needs to have one function for every
// kind of processing we will ever need to do.

// There must be a way to use some sort of callback interface so
// that only one generic function is required, with custom callbacks
// being used to do the specific work for that situation.
}

// Code to make this example compile and do something
int main(void)
{
// Normally these would be in an std::vector<IDatabaseField *>
TDatabaseField<int> numericField;
TDatabaseField<std::string> stringField;

addNextFieldToDatabase(&numericField);
addNextFieldToDatabase(&stringField);

return 0;
}
 
M

Michael DOUBEZ

Adam said:
I'm trying to use type-safe code with templates, but I'm a bit stuck as
to how you can call into a type-specific template when all you have is a
pointer to its base class.

Templates live only at compile time while dynamic polymorphism is at
runtime, your are stuck unless you put some restrictions on the types
(visitor or multimethods) or create some dependencies.
The code below demonstrates the problem, along with the only solution I
have come up with - instead of doing your processing outside the
template class, move it inside the template class. Unfortunately this
means the class must be aware of a whole bunch of things it might be
used for, which isn't really ideal.

You can also externalize the functions into another class which has
polymorphic behavior. This does lessen somewhat the coupling and
provides better separation of concern.
I'm hoping someone here might know how I could create a type-safe
callback mechanism, so instead of putting all my code inside the
template class, I can just create a new callback-class and have the
templated code call that instead. Being able to override a virtual
template function would do the job nicely, but apparently that's not
allowed :-(

There are some type erasure techniques that can emulate it somewhat but
not for extensible methods.

At one point, you will have to define the operations between a database
and a field or the datatypes you want to work with.

[snip code]
 
S

SG

I'm trying to use type-safe code with templates, but I'm a bit stuck as
to how you can call into a type-specific template when all you have is a
pointer to its base class.

The code below demonstrates the problem, along with the only solution

I havn't checked your code but from the description above I think the
little library "Function" is what could be very helpful in your case.
It's part of the Boost, TR1, and the future C++ standard library.

See
http://www.boost.org/doc/libs/1_38_0/doc/html/function.html

It's possible to have a function<void()> object that calles some
member function (void X::foo(), for example) on an object 'o' of some
arbritrary class 'X' via bind1st(mem_fun(&X::foo),&o). See
http://www.boost.org/doc/libs/1_38_0/doc/html/function/tutorial.html#id3171589

Cheers!
SG
 
A

abhay.burli

On Apr 20, 12:44 pm, Adam Nielsen <[email protected]>
wrote:
[...]
Templates are afterall a compile-time entity. You need reflection
support from the language runtime if one is to combine dynamic-
polymorphism and templates-style static-type checking.

Besides, when i wanted to write a callback, i had referred to a
whitepaper now available here:-
http://www.newty.de/jakubik/callback.html#Source Code

It has excellent metrics listed with various callback techniques and i
think you may be interested in it.

HTH,
 
A

Adam Nielsen

I'm trying to use type-safe code with templates, but I'm a bit stuck
Templates live only at compile time while dynamic polymorphism is at
runtime, your are stuck unless you put some restrictions on the types
(visitor or multimethods) or create some dependencies.

I think you should be able to work all this out at compile time though.
Your virtual function (overridden in the template class) would call a
member in the callback class, with the same template type as was used to
instantiate the template class. In other words, whenever the compiler
instantiates the template, it would immediately know what type is
required to instantiate the callback class.

I think the problem there is that you can't override a template
function, so you can't create a new type of callback class. One way
around it would be to create overloaded functions, one for every data
type you might use (and override all those as required), but that kind
of defeats the purpose of templates in the first place...
You can also externalize the functions into another class which has
polymorphic behavior. This does lessen somewhat the coupling and
provides better separation of concern.

I'm not sure sure what you mean by this, but I think moving all the
potential callbacks into another class would still mean users of my code
would need to alter my code every time they wanted to use a callback.
There are some type erasure techniques that can emulate it somewhat but
not for extensible methods.

At one point, you will have to define the operations between a database
and a field or the datatypes you want to work with.

That's a shame, but I guess you can only work within what the language
provides.

In this particular case I have now implemented it in such a way as to
avoid altering the main field class, instead using a bunch of
dynamic_cast statements at runtime to figure out what type the field is
and calling a template function accordingly. A bit manual I suppose,
but it seems to work well enough.

Thanks for your suggestions!

Cheers,
Adam.
 
A

Adam Nielsen

Besides, when i wanted to write a callback, i had referred to a
whitepaper now available here:-
http://www.newty.de/jakubik/callback.html#Source Code

It has excellent metrics listed with various callback techniques and i
think you may be interested in it.

They're interesting (if a bit difficult to read) but they seem to avoid
the main issue for me, which is how you use a user-supplied template
function as a callback.

"You can't" seems to be the general answer ;-)

Cheers,
Adam.
 
A

Adam Nielsen

I havn't checked your code but from the description above I think the
little library "Function" is what could be very helpful in your case.
It's part of the Boost, TR1, and the future C++ standard library.

See
http://www.boost.org/doc/libs/1_38_0/doc/html/function.html

It's possible to have a function<void()> object that calles some
member function (void X::foo(), for example) on an object 'o' of some
arbritrary class 'X' via bind1st(mem_fun(&X::foo),&o). See
http://www.boost.org/doc/libs/1_38_0/doc/html/function/tutorial.html#id3171589

Thanks for the suggestion, but the problem is that when I would call the
bind() function, I don't know what type I need. I would need to be able
to bind to a template function without specifying the template type,
which isn't allowed.

So unfortunately binding won't work, since the exact function that will
get called is unknown at bind() time!

Cheers,
Adam.
 
M

Michael DOUBEZ

Adam said:
I think you should be able to work all this out at compile time though.
Your virtual function (overridden in the template class) would call a
member in the callback class, with the same template type as was used to
instantiate the template class. In other words, whenever the compiler
instantiates the template, it would immediately know what type is
required to instantiate the callback class.

Which creates a heavy dependency (TDatabaseField must know CDatabase).
I think the problem there is that you can't override a template
function, so you can't create a new type of callback class. One way
around it would be to create overloaded functions, one for every data
type you might use (and override all those as required), but that kind
of defeats the purpose of templates in the first place...

Which would require limiting the number or the kind of types, if you do,
there are a number of techniques to automatize that.
I'm not sure sure what you mean by this, but I think moving all the
potential callbacks into another class would still mean users of my code
would need to alter my code every time they wanted to use a callback.

Yes, unless you let them provide an abstract factory for the callbacks
but that means defining the possible operations.

In which case, in your example, it might be easier to let CDatabase call
addSelfToDatabase in a overloaded add(IDatabaseField&) member function:

class CDatabase
{
//as in your example ...

void add(const IDatabaseField& field)
{
field.addSelfToDatabase(*this);
}
};

Renaming "IDatabaseField" to "ISelfInsertable" might help :)
That's a shame, but I guess you can only work within what the language
provides.

In this particular case I have now implemented it in such a way as to
avoid altering the main field class, instead using a bunch of
dynamic_cast statements at runtime to figure out what type the field is
and calling a template function accordingly. A bit manual I suppose,
but it seems to work well enough.

If you can use dynamic_cast, this means you have put restrictions on the
types supported.
 

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,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top