Pure virtual member function has to process a templated buffer - how to? - c++

I have a base class PixelBuffer which has a virtual function to copy the content of the PixelBuffer to a templated Buffer object. PixelBuffer may have several inherited implementations like OpenGLPixelBuffer/DXPixelBuffer which implement a pure virtual copyToBuffer function (pseudo code):
class PixelBuffer
{
public:
virtual void copyToBuffer(? buffer) = 0; // how to declare this?
};
The PixelBuffer has an internal data type which can be of different base types (int, uint, float, char, ...).
More important is the data type of the Buffer I want to copy to.
The buffer class (may) look something like this:
template <typename T>
class Buffer
{
public:
Buffer() : m_data(), m_size(0) {}
Buffer(std::size_t buffsize) : m_data(new std::vector<T>(buffsize)), m_size(buffsize) { }
std::shared_ptr<std::vector<T>> data() { return m_data; }
std::size_t size() { return m_size; }
void allocate(std::size_t buffsize) {
m_data = std::shared_ptr<std::vector<T>>(new std::vector<T>(buffsize));
m_size = buffsize;
}
private:
std::shared_ptr<std::vector<T>> m_data;
std::size_t m_size;
};
typedef Buffer<float> FloatBuffer;
typedef Buffer<char> ByteBuffer;
I am searching for a way to do something like this:
PixelBuffer* pbObj = ...;
FloatBuffer dest;
pbObj->copyToBuffer(dest); // does allocation and copying
Since it is not possible to template the PixelBuffer member functions, I don't know how to solve this right now. I know I need many implementations for copying the different buffer data types, but I don't know where to implement them and how this could be done without adding a bunch of functions to PixelBuffer (for each type of Buffer).
Maybe this could be done using the visitor pattern or with policies?
Edit:
To answer a comment: Yes, it should be possible to copy from any PixelBuffer type to any Buffer type. However, only those types for which the copying is implemented should be supported!

I'm still not exactly certain what you require, but if you want to be able to pass various specializations of Buffer then you need unique methods rather than a single virtual method:
virtual void copyToBuffer(Buffer<char> & buffer) = 0;
virtual void copyToBuffer(Buffer<float> & buffer) = 0;
You would probably then override these in the subclass, and probably throw an exception for any cases where the copy operation wasn't supported. Your question suggests you don't want to do this, although I feel it's the best solution if you have a limited set of buffer specialisations that you need to deal with.
If you want only a single method, you need to abstract out functionality common to each specialization of Buffer into a class which each specialisation extends, and take a reference to that instead:
virtual void copyToBuffer(BufferBase & buffer) = 0;
template <typename T>
class Buffer : public BufferBase
{
// ...
The implementations of the copyToBuffer method are then going to be limited to the functionality provided in BufferBase, in this case, so I doubt this solution would prove workable for you.
On the other hand, if you want to only have a single method which accepts a specific type of buffer and the type differs from subclass to subclass, then the method shouldn't be declared in the base class at all - you should simply declare the method with the appropriate argument type in each of the subclasses. Probably the cleanest solution in this case is to parameterize the PixelBuffer class according to the element type of the buffer it supports:
template <typename E>
class PixelBuffer : public PixelBufferBase
{
public:
virtual void copyToBuffer(Buffer<E> &) = 0;
}
Your example then becomes:
PixelBufferBase* pbObj = ...;
PixelBuffer<float> *fpbObj = dynamic_cast<PixelBuffer<float> *>(pbObj);
FloatBuffer dest;
fpbObj->copyToBuffer(dest); // does allocation and copying
(You can probably use a static_cast instead for performance, at the cost of certain safety).
You don't actually need a policy-based design; you would get a benefit from policies only if the copyToBuffer implementation could be shared between different PixelBuffer subclasses (which sounds unlikely) and in any case you'd still need to parameterise the PixelBuffer type.
I also don't believe the visitor patterns helps you with this problem - it requires multiple functions to deal with multiple PixelBuffer/Buffer types; you might move the overloaded method around to a different class, but you don't avoid the need for it.

I have met the same problem. I used a void * as the type and convert the pointer to the correct Buffer in the implements.
It's not beautiful, but It works and has no penalty on the performance.
But if your PixelBuffer will copy to more than one type of Buffer with only one copyToBuffer function. You will have to add another argument to distinct different type of buffers:
enum class BufferType
{
INT,
CHAR,
FLOAT,
//...
};
class PixelBuffer
{
public:
//....
virtual void copyToBuffer(void* buffer, BufferType buffertype) = 0;
}
virtual void DXPixelBuffer::copyToBuffer(void* buffer, BufferType buffertype)
{
switch(buffertype){
case BufferType::FLOAT:
Buffer<float>* pbuffer = static_cast<Buffer<float>*>(buffer);
//...
break;
//...
And you can use a traits class and a wrap function to make the call to copyToBuffer more safe.
template<typename T> struct BufferTraits;
template<> struct BufferTraits<float>{
static constexpr BufferType Type = BufferType::FLOAT;
};
template<> struct BufferTraits<char> {
static constexpr BufferType Type = BufferType::CHAR;
};
class PixelBuffer
{
public:
template<typename T> void PixelBuffer::copyToBuffer(Buffer<T>& buffer){this->copyToBuffer(reinterpret_cast<void *>(&buffer), BufferTraits<T>::Type);}
//...
}
Your example will remain the same:)
PixelBuffer* pbObj = ...;
FloatBuffer dest;
pbObj->copyToBuffer(dest);

Related

Specifying void* parameter in derived function

I would like to do something that probably is not possible in Cpp, but I could not find a post about this specifically.
I want to have a derived class specify the type of a void* parameter on a virtual function.
I have a base class called interface with a send function.
// pure virtual
class Interface{
virtual bool Send(const void*)=0;
};
struct Packet{
DataType data;
};
class SpecificInterface{
bool Send(const DataType*);
}
Is there a way to make something like this work? The intent is that SpecificInterface::Send implements Interface::Send. Allowing SpecificInterface to not be a pure virtual while restricting the void* to a specific packet type.
Otherwise I know I could take a void* parameter and static_cast it into the Packet* type; however, I do not want others to send a pointer type that cannot be cast to Packet*.
Let me know if this is not clear
When you want to override a virtual function, the number of arguments and the types of the arguments must exactly match the declaration in the base class. You'll have to use:
class SpecificInterface{
bool Send(const void* ptr)
{
cont DataType* dataTypePtr = static_cast<const DataType*>(ptr);
// Now use dataTypePtr any way you wish
}
};
Please note that use of such code is dangerous. If ptr does not really point to a DataType object, your program will have undefined behavior.
#RSahu is correct, of course. You could still use a virtual method to do about the same thing:
class Interface {
virtual bool send(const void*) = 0;
};
struct Packet {
DataType data;
};
class SpecificInterface {
bool send(cont void*) override {
send(static_cast<DataType*>(data));
}
bool send(cont DataType*); // code which actually does something
};
However - I recommend against your whole approach to begin with - it is massively unsafe, since the validity of the type is never checked! It's a source of many potential bugs. More often than not, you can avoid doing this. Here are a few things you might try instead:
std::any - a class which doesn't offer you compile-time type safety, but at least checks types at run-time. You would have a send(const std::any& data) virtual function, and inside it you would call std::any_cast<DataType>(data) to get a DataType or std::any_cast<DataType>(&data) to get a DataType *.
Probably even better - the Curiously-recurring template pattern (CRTP):
template <typename T>
class Interface {
virtual bool send(T*) = 0;
};
class SpecificInterface : Interface<DataType> {
bool send(cont DataType*) override;
}

C++ Reference to vector of derived types

I have numerous objects implementing an interface called ExposesCommands.
class ExposesCommands
{
virtual bool get_command_results(std::string command, std::vector<std::string> &results) = 0;
};
typedef std::unique_ptr<ExposesCommands> ExposesCommands_ptr;
Commands are exposed via a template class:
template <typename T>
class ExposedCommands : public ExposesCommands
{
private:
static std::map<const char*, std::string T::*, cmp_str> exposed_cmds;
public:
virtual bool get_command_results(std::string command, std::vector<std::string> &results);
}
Now, I am trying to add sub-commands. Sub-commands will link to sub-objects that implement the commands. The way I would like to add them is like so:
template <typename T>
class ExposedCommands : public ExposesCommands
{
private:
static std::map<const char*, std::string T::*, cmp_str> exposed_cmds;
static std::map<const char*, std::vector<ExposesCommands_ptr> T::*, cmp_str> exposed_sub_cmds;
public:
virtual bool get_command_results(std::string command, std::vector<std::string> &results) {
auto &it = exposed_cmds.find(command.c_str());
if (it != exposed_cmds.cend()) {
auto x = std::bind(it->second, std::placeholders::_1);
std::string data = x(*((T*)this));
if (data != "") {
results.push_back(data);
}
return true;
}
// else check if in exposed_sub_cmds.
// if so, iterate through vector, call get_command_results
// on remainder of command name for each sub object, adding
// its result to the vector of results.
// return true
//
return false;
}
}
I have objects like this implementing the interface (building of the maps is not shown here):
class ObjectA : public ExposesCommands<ObjectA>
{
public:
std::string cmd_x; // command X
std::string cmd_y; // command Y
}
typedef std::unique_ptr<ObjectA> ObjectA_ptr;
class ObjectB
{
public:
std::string cmd_z; // command Z
std::vector<ObjectA_ptr> my_as; // 'ObjectA' sub commands
}
Unfortunately, this doesn't work because I can't assign a &std::vector<ObjectA_ptr> to a std::vector<ExposesCommands_ptr> T::*.
Is there any way to get around this? Or a better approach to this problem?
To summarize your problem: You have a base class and some derived classes
class Base {
public:
virtual ~Base();
};
class Derived1 : public Base;
class Derived2 : public Base;
You need to store a collection of pointers (for ownership management you chose to use std::unique_ptr, which seems wise) to Derived1 objects in a way that it can be used by code that doesn't know Derived1 exists, and only wants to use properties of Base, but also do not want to lose the property that this specific collection of Base objects actually contains Derived1 objects only. This is a kind of type erasure, as the runtime behaciour of the collection should not depend on whether it stores Base, Derived1 or Derived2 objects (or even a mixture of it, so that property gets erased), yet at compile time, you don't want to write all those ugly downcasts (and you want the compiler to verify you only downcast objects from a container you statically know that it does only contain Derived1 objects). Be aware that if you are going to store pointers to Derived1 in std::unique_ptr<Base>, it is absolutely necessary that Base has a virtual destructor.
I don't know any ready-made solution for this off-hand (it couldn't find something skimming over the Boost libraries tagged Container, too), but I can show you how reach that goal yourself. You need a template, to get different compile-time types (just as std::vector is a template), which internally stores the data in a fixed type. So something like this:
typedef std::unique_ptr<Base> Base_ptr;
template <typename T>
class BaseVector {
public:
const std::vector<Base_ptr> &
as_baseclass_vector() const
{
return backing_;
}
private:
std::vector<Base_ptr> backing_;
};
Note that as_baseclass_vector does return a const reference to the raw vector, because the result must not be used to insert objects of the wrong type (e.g. pointers to Derived2 objects) into a CommandVector instantiated for Derived1. This is only half the way to go, the other half is sadly reimplementing the standard library container concept on this vector-wrapper, along this:
template<typename T>
void CommandVector::push_back(std::unique_ptr<T> obj)
{
backing_.push_back(std::move(obj));
}
or, more importantly and interestingly:
template<typename T>
const T* BaseVector::operator[](size_t index) const
{
return static_cast<T*>(backing_[index]);
}
Note that this operator[] does not return a reference-to-unique_ptr, as it could only return a reference to a unique_ptr<Base>, because that is what is stored in the backing vector. If it created a tempory unique_ptr to T, it would have to remove the ownership from the vector - you definitely would not want that! The result has been declared const, because it returns a copy instead of the usual reference, and modifying the return value (which is now forbidden) does not modify the object in the vector, opposed to what users expect. You would have to reimplement all the other methods (iterators would get quite interesting, but probably can be based on boost::transform_iterator) yourself if you chose to go this route.

How to check if a void* is to a pointer on a valid instance of an object type?

I'm searching for the most common and robust way to check if a void* can be convert in a given C++ object type. You can see below some information about the context.
When I define a C API for a DLL, I often use void* to hide the C++ object I use behind (something like below)
typedef void* Instance_t;
int createInstance(Instance_t* pInst);
int processing(Instance_t inst, uint8_t* pBuf, size_t bufLength);
When I createInstance the code cast a pointer like that:
int createInstance(Instance_t* pInst)
{
MyClass* ptr = new MyClass();
*pInst = (void*)(ptr);
//.... etc
return 0;
}
But the question is how can we later in all the other C function we define, check if the void* value we receive is a valid MyClass*. I think we can't as none of the C++ casting operator is really type safe is that case (even dynamic_cast).
for now my best solution is to use a C cast (or a reinterpret_cast) and if everything is ok with a call to a IsValid function define with MyClass.
Have you a better way to do that check ?
You can't do that, unless you (say) allocate all your MyClass instances from a memory pool, and check the address you get passed is a pointer into to that memory pool. Or maintain a list of valid instances.
However, if you need to pass round an opaque pointer just make the client use
struct MyClass;
typedef struct MyClass *Instance_t;
This will compile cleanly and give you a reasonable amount of peace of mind. As long as you're only using the pointer, the compiler is happy. It's when it dereferences it, the compiler needs to know what the pointer actually points to.
I don't think you can do this, and I don't think you should be doing this. A void * is just a pointer to some location in memory. Almost by definition, there's no way to know what it points to.
But why are you typecasting everything to void *'s, why not use protected and private methods on your class if you want to prevent users from fiddling with the internals of your class?
There is no way to check that an untyped pointer points to a valid object of any particular type. If you use void*, you are throwing away type checking. Instead, you could declare the type in the C header, and use a pointer to that (incomplete) type rather than void*.
struct Instance;
int createInstance(struct Instance** pInst);
int processing(struct Instance* inst, uint8_t* pBuf, size_t bufLength);
Then in C++, you can define and use the class.
// Probably better to use "struct" rather than "class" in case
// some compilers complain about mixing class keys.
struct Instance {
// Whatever
};
int createInstance(Instance** pInst) {
*pInst = new Instance;
// and so on
}
There's no way to determine if a void* points to a valid C++ class. Unless you've got RTTI enabled then there's no metadata associated with a class, and even then there are plenty of cases in C++ where a void* isn't pointing to a class. For example:
int x=10;
void *ptr = &x;
Here ptr is pointing to a raw value. There's no RTTI associated with the integer so how could you query it to determine anything>
When I need to export some objects from my CPP lib to C code, I do:
typedef void * OBJ1;
typedef void * OBJ2;
OBJ1 createObj1();
OBJ2 createObj2();
void doObj1(OBJ1 obj);
So in do function I exactly knew what object to expect
The short answer: it can be difficult.
The issues are numerous, but basically boil down to the very low-level nature of operations accessible in C and C++ (note: accessible, but not necessarily legal). The very role of void* is that any pointer can be coerced to it, but should you be using another type anyway abuse of reinterpret_cast could still lead to troubles.
A simple solution is to use tagging. Essentially, put a type id in the pointer so that you can always know the original type of the object. It's onerous (as each and every type need be modified), but otherwise easy to deploy.
typedef enum {
SP_ATag,
SP_BTag,
SP_CTag,
...
} SP_Tag_t;
// External Tag
typedef struct {
SP_Tag_t tag;
void* p;
} SP_Any_t;
// Internal Tag
struct A {
SP_Tag_t tag;
...;
};
...
typedef union {
A* a;
B* b;
C* c;
} SP_Any_t;
Then, instead of using void*, you use SP_Any_t.
Advantages:
lightweight
external solution does not require modifying existing classes
internal solution should be binary compatible with existing C code
Disadvantages:
single place to declare all types
corrupting the tag is easy (whether accidental or intentional)
A more involved solution, which can be a good debug help, is to introduce a per type registry. The downside is that you need to instrument existing types for it to work, still it's easy enough, and that it involves much more runtime overhead. But hey: it works!
template <typename> class Registrable;
//
// class Registry
//
class Registry {
public:
template <typename> friend class Registrable;
template <typename T>
static T* Cast(void*);
private:
struct TagType {};
using Key = std::pair<TagType const*, void*>;
using Store = std::set<Key>;
template <typename T>
static void Register(Registrable<T>* t);
template <typename T>
static void Unregister(Registrable<T>* t);
static Store& Get();
}; // class Registry
template <typename T>
T* Registry::Cast(void* const pointer) {
TagType const* const tag = &Registrable<T>::Tag;
if (Get().count(std::make_pair(tag, pointer)) == 0) { return nullptr; }
return static_cast<T*>(reinterpret_cast<Registrable<T>*>(pointer));
}
template <typename T>
void Registry::Register(Registrable<T>* t) {
TagType const* const tag = &T::Tag;
void* const pointer = reinterpret_cast<void*>(t);
Get().insert(std::make_pair(tag, pointer));
}
template <typename T>
void Registry::Unregister(Registrable<T>* t) {
TagType const* const tag = &T::Tag;
void* const pointer = reinterpret_cast<void*>(t);
Get().erase(std::make_pair(tag, pointer));
}
Registry::Store& Registry::Get() { static Store S; return S; }
//
// class Registrable
//
template <typename T>
class Registrable {
public:
static Registry::TagType const Tag;
Registrable();
~Registrable();
Registrable(Registrable&&) = default;
Registrable& operator=(Registrable&&) = default;
Registrable(Registrable const&) = default;
Registrable& operator=(Registrable const&) = default;
}; // class Registrable
template <typename T> Registry::TagType const Registrable<T>::Tag;
template <typename T>
Registrable<T>::Registrable() { Registry::Register(this); }
template <typename T>
Registrable<T>::~Registrable() { try { Registry::Register(this); } catch(...) {} }
do not use pointers but handles
if the memory is on the dll side do not pass pointers but handles to
your objects only
if the memory for your objects is allocated by the application and passed to the dll keep a table of those pointers and treat them as handles also
this works for a single dll. of course it does not work if pointers from 1.dll are passed to 2.dll via application. in this case you are on thinnest possible ice anyway.
I have a solution with some limitations that uses RTTI...
if your instances all derive from a virtual base class, then you can reinterpret cast to that base class safely, and then dynamic cast to your other class...
class Object
{
virtual ~Object() {}
};
class A : public Object
{
static bool IsOfThisClass(void *data)
{
return dynamic_cast<A*>((Object*)data) != 0;
}
}
calling A::IsOfThisClass(someData) will return true if someData is of class A.
it's not the kind of hack you want to expose to a user, since it only works if void* points to a class derived from Object, but it can be a useful building block in controlled situations.

Container implementation for complex type

I'm trying to come up with a container wrapper which stores data of the following types: bool, int, double, std::string. In addition I have a complex type which I need to store in the container. Let's call it Foo. For the sake of simplicity we'll say that Foo contains a list of ints.
My container class currently wraps an ugly and complex container type which I get from a c api. When I'm finish manipulating the data in the container, I need to copy it back to the api. It uses unions and linked lists. It is possible that I can copy this data into, for example, a std::list, but this might cause performance issues which present themselves at a later date. Therefore, my container class is not dependant on how data is actually stored in memory.
Here's a quick idea of how my container looks:
template <class T>
class Cont
{
public:
Cont(ISetter<T>* setter)
: _setter(setter)
{
}
void sillyFunction(T t)
{
(*_setter)(t,0);
}
private:
...
ISetter<T>* _setter;
};
So I use a helper setter class which handles the nitty gritty of the memory. I have a number of these class but the ISetter will give you an idea of what I'm doing.
In order to deal with the Foo type, which is also stored by the c api in a rather bizarre way, I have arrived at the following setter. Again, this is just a rough example.
class IFoo
{
public:
virtual int getMember() = 0;
};
class Foo2: public IFoo
{
public:
virtual int getMember(){ return 1;} // dummy
};
template<typename T> class ISetter{};
template<> class ISetter<IFoo*>
{
public:
virtual void operator()(IFoo* value, int index) = 0;
};
template<typename T> class Setter{};
template<> class Setter2<Foo2*>: public ISetter<IFoo*>
{
public:
virtual void operator()(IFoo* value, int index)
{
_list[index] = dynamic_cast<Foo2*>(value);
}
private:
std::vector<Foo2*> _list;
};
So I handle my Foo as an interface called IFoo. The Setter2 implementation deals with the setting in memory of my list of Foos. Setter1, missing below, deals with the ugly c api memory.
Here's an idea of these class in practice:
Foo2* f = new Foo2();
ISetter<IFoo*>* setter = new Setter2<Foo2*>();
Cont<IFoo*>* container = new Cont<IFoo*>(setter);
container->sillyFunction(f);
When dealing with ints, for example, I do something like this instead:
int i = 10;
ISetter<int>* setter = new Setter1<int>();
Cont<int>* container = new Cont<int>(setter);
container->sillyFunction(i);
So, my question is if you think this is a good approach and what improvements you might recommend.
I use shared pointers instead of raw pointers.
I would create a single simple Foo wrapper class which can look up members data from the C API, and present it as a coherent class. No need for messing about with interfaces, virtual functions or inheritance for that. Just a single class will do.
So for each "Foo"-entry in the C API, you create a single Foo wrapper.
Then you have simple, well-behaved type representing individual instances of the data stored in your C library.
Now just take that and put it in a std::vector.
struct Foo {
Foo(<handle-or-pointer-to-library-data>);
// member functions for retrieving member data from the C API
};
std::vector<int>
std::vector<bool>
std::vector<std::string>
std::vector<Foo>
As I understand your problem, that would be a simple and efficient solution.
I would change it a little. Consider to remove all this Setter virtual-ism from your code. One of goal to introduce Templates were to have alternative to virtual-ism:
template <class T, class Setter>
class Cont
{
public:
Cont(Setter setter = Setter())
: _setter(setter)
{
}
void sillyFunction(T t)
{
_setter(t,0);
}
private:
...
Setter _setter;
};
And its simple usage:
template <class IType, class Type>
class Setter2_Virtual
{
public:
void operator()(IType* value, int index)
{
_list[index] = dynamic_cast<Type*>(value);
}
private:
std::vector<Type*> _list;
};
Cont<IFoo*, Setter2_Virtual<IFoo, Foo2> > container;
container.sillyFunction(f);
I concentrated on Setters - but maybe you can do the same with IFoo/Foo stuff as well.
Just an idea - you do not obliged to use it after all.

What is an appropriate interface for dealing with meta-aspects of classes?

I'm looking for some advice of what would be an appropriate interface for dealing with aspects about classes (that deal with classes), but which are not part of the actual class they are dealing with (meta-aspects). This needs some explanation...
In my specific example I need to implement a custom RTTI system that is a bit more complex than the one offered by C++ (I won't go into why I need that). My base object is FooBase and each child class of this base is associated a FooTypeInfo object.
// Given a base pointer that holds a derived type,
// I need to be able to find the actual type of the
// derived object I'm holding.
FooBase* base = new FooDerived;
// The obvious approach is to use virtual functions...
const FooTypeInfo& info = base->typeinfo();
Using virtual functions to deal with the run-time type of the object doesn't feel right to me. I tend to think of the run-time type of an object as something that goes beyond the scope of the class, and as such it should not be part of its explicit interface. The following interface makes me feel a lot more comfortable...
FooBase* base = new FooDerived;
const FooTypeInfo& info = foo::typeinfo(base);
However, even though the interface is not part of the class, the implementation would still have to use virtual functions, in order for this to work:
class FooBase
{
protected:
virtual const FooTypeInfo& typeinfo() const = 0;
friend const FooTypeInfo& ::foo::typeinfo(const FooBase*);
};
namespace foo
{
const FooTypeInfo& typeinfo(const FooBase* ptr) {
return ptr->typeinfo();
}
}
Do you think I should use this second interface (that feels more appropriate to me) and deal with the slightly more complex implementation, or shoud I just go with the first interface?
#Seth Carnegie
This is a difficult problem if you don't even want derived classes to know about being part of the RTTI ... because you can't really do anything in the FooBase constructor that depends on the runtime type of the class being instantiated (for the same reason you can't call virtual methods in a ctor or dtor).
FooBase is the common base of the hierarchy. I also have a separate CppFoo<> class template that reduces the amount of boilerplate and makes the definition of types easier. There's another PythonFoo class that work with Python derived objects.
template<typename FooClass>
class CppFoo : public FooBase
{
private:
const FooTypeInfo& typeinfo() const {
return ::foo::typeinfo<FooClass>();
}
};
class SpecificFoo : public CppFoo<SpecificFoo>
{
// The class can now be implemented agnostic of the
// RTTI system that works behind the scenes.
};
A few more details about how the system works can be found here:
► https://stackoverflow.com/a/8979111/627005
You can tie dynamic type with static type via typeid keyword and use returned std::type_info objects as means of identification. Furthermore, if you apply typeid on a separate class created specially for the purpose, it will be totally non-intrusive for the classes you are interesed in, althought their names still have to be known in advance. It is important that typeid is applied on a type which supports dynamic polymorphism - it has to have some virtual function.
Here is example:
#include <typeinfo>
#include <cstdio>
class Base;
class Derived;
template <typename T> class sensor { virtual ~sensor(); };
extern const std::type_info& base = typeid(sensor<Base>);
extern const std::type_info& derived = typeid(sensor<Derived>);
template <const std::type_info* Type> struct type
{
static const char* name;
static void stuff();
};
template <const std::type_info* Type> const char* type<Type>::name = Type->name();
template<> void type<&base>::stuff()
{
std::puts("I know about Base");
}
template<> void type<&derived>::stuff()
{
std::puts("I know about Derived");
}
int main()
{
std::puts(type<&base>::name);
type<&base>::stuff();
std::puts(type<&derived>::name);
type<&derived>::stuff();
}
Needless to say, since std::type_info are proper objects and they are unique and ordered, you can manage them in a collection and thus erase type queried from the interface:
template <typename T> struct sensor {virtual ~sensor() {}};
struct type
{
const std::type_info& info;
template <typename T>
explicit type(sensor<T> t) : info(typeid(t))
{};
};
bool operator<(const type& lh, const type& rh)
{
return lh.info.before(rh.info);
}
int main()
{
std::set<type> t;
t.insert(type(sensor<Base>()));
t.insert(type(sensor<Derived>()));
for (std::set<type>::iterator i = t.begin(); i != t.end(); ++i)
std::puts(i->info.name());
}
Of course you can mix and match both, as you see fit.
Two limitations:
there is no actual introspection here . You can add it to template struct sensor via clever metaprogramming, it's very wide subject (and mind bending, sometimes).
names of all types you want to support have to be known in advance.
One possible variation is adding RTTI "framework hook" such as static const sensor<Myclass> rtti_MyClass; to implementation files where class names are already known and let the constructor do the work. They would also have to be complete types at this point to enable introspection in sensor.