Is a type-safe callback mechanism possible?
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;
}