I have written a set of two classes to help with polymorphism, more specifically in cases where we'd have a collection of pointers to a base class where we store derived classes.
There are two implementations, one in current use and an experimental version. The current implementation works at runtime having a static instance of Type
per class, which keeps track of it's parent and children Type
(s); an Object
class is used to serve as the base class for all other classes (including a macro for creating the necessary static and virtual methods for Type
retrieval.
The experimental implementation (the one relevant here) consists of a templated Type<T>
and the same structure for Object
adapted to the new Type<T>
. The new Type<T>
uses <type_traits>
for all it's operations.
Next follows the sources for the experimental classes:
Type.h
#ifndef QUIDOR_EXPERIMENTAL_TYPE_H
#define QUIDOR_EXPERIMENTAL_TYPE_H
// std
#include <type_traits>
namespace quidor {
namespace experimental {
template<class T> class Type {
public:
typedef T class_type;
public:
template<class O> // is<O>
bool operator ==(const Type<O> &) const {
return std::is_same<class_type, O>();
}
// more operators like the previous one
Type() = default;
~Type() = default;
};
}
}
#endif //QUIDOR_EXPERIMENTAL_TYPE_H
Everything works, no problems there, the problem arises when we start going into Object
and it's derivatives.
Object.h
#ifndef QUIDOR_EXPERIMENTAL_OBJECT_H
#define QUIDOR_EXPERIMENTAL_OBJECT_H
// quidor
#include "Type.h"
#define QuidorObjectMeta(class) \
public: \
static constexpr const char * className() { \
return #class; \
} \
\
static const quidor::experimental::Type<class> classType() { \
return quidor::experimental::Type<class>(); \
} \
\
virtual const quidor::experimental::Type<class> getClassType() const { \
return class::classType(); \
} \
private:
//\ObjectMeta(class)
namespace quidor {
namespace experimental {
class Object {
public:
static const Type<Object> classType() {
return Type<Object>();
}
virtual const Type<Object> getClassType() const {
return Object::classType();
}
Object() = default;
virtual ~Object() = default;
private:
};
}
}
#endif //QUIDOR_EXPERIMENTAL_OBJECT_H
The problem lies in just one function:
virtual const Type<Object> getClassType() const
A derived class, Derived
, would have this virtual method defined as (following ObjectMeta
):
virtual const Type<Derived> getClassType() const
Now this is obviously not legal as the return type of getClassType()
cannot change from what it is declared with in Object
.
The existence of that virtual method is required as it represents how we can get the real type of an instance from, for example, a variable of type Object *
, being this the desired functionality:
#include <quidor/experimental/Object.h>
#include <cassert>
class Derived : public quidor::experimental::Object {
QuidorObjectMeta(::Derived, quidor::experimental::Object);
Derived() = default;
virtual ~Derived() = default;
};
int main(int argc, char ** argv) {
using quidor::experimental;
Object * o = new Derived();
assert(o->getClassType() == Derived::classType()); // true
delete o;
return 0;
}
The only solution I have "found" would be to create a non-templated base class for Type<T>
and return that instead, the problem arises when this base class cannot know the T
of it's underlying Type<T>
; nor can this base class have a virtual method, as this method would have to be templated to receive the other type (O
in Type<T>
's code), and templated virtual methods are not legal.
So I have come here to ask for a solution to this problem, if there is any, if not and why and how it'd work, or not. Thank you in advance.