1

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.

zeluisping
  • 213
  • 7
  • 17
  • 3
    Can you reduce it to a minimal example? There is a lot of code that is useless for the question itself. – skypjack Aug 15 '16 at 21:21
  • 1
    That's a lot of code involved to create something that's essentially C++ RTTI with `typeid` and `dynamic_cast`. – milleniumbug Aug 15 '16 at 21:24
  • @skypjack done, had forgotten to do it, my apologies! – zeluisping Aug 15 '16 at 23:13
  • @milleniumbug my goal was to avoid using `typeid`, I had done something like this before using `typeid`, I've been going over standard libraries and this was a way of using some of what is provided in ``. Now that you mention it, I should go back and redo what I did with `typeid`, pretty sure I'll find things to improve now! Thank you! – zeluisping Aug 15 '16 at 23:15
  • @Spencer I haven't figured out how to do it while at the same time preserving `T` – zeluisping Aug 15 '16 at 23:23
  • 1
    Your design tries to determine statically if the types match. But a virtual function cannot provide static type information that changes in its return value: the return value is of one static type (and possibly different dynamic type). I cannot tell from your description if dynamic type information/comparison is what you want or not. – Yakk - Adam Nevraumont Aug 16 '16 at 00:00
  • @Yakk with these experimental classes, the aim was in doing it statically, thus you have just given me the answer I needed. If I am doing it statically I can't possible be able to get it dynamically, only if I'd have information on the types dynamically (as the non-experimental classes do). Thank you! – zeluisping Aug 16 '16 at 01:19

1 Answers1

1

You seem to be trying to build something very similar to the Smalltalk hierarchy, where each class has a metaclass (which is itself an object--i.e., an instance of a class).

In Smalltalk this was handled with a structure like this1:

enter image description here

In this, solid lines depict inheritance, and dashed lines instances (i.e., A - - -> B means A is an instance of B). Grey lines are in the normal class hierarchy and black lines are in the metaclass hierarchy2.

Some of this is probably difficult to model in C++, particularly the relationship between Metaclass and Metaclass class, where the metaclass of Metaclass is itself an instance of Metaclass.

I'm not sure if that's enough detail to model the system accurately; if not, you may want to spend some time reading the book I've cited in footnote 1.


  1. Although I've redrawn it, this is essentially a copy of figure 16.5 from Smalltalk-80: The Language and its Implementation.
  2. I realize this notation is somewhat unusual--but it's what the original used, and I've attempted to preserve it reasonably accurately.
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • I surely will attempt to implement this scheme in C++! It'll be a nice challenge, I still do not know if it will fix my problem, however given the information provided and that a "not so straightforward" solution seems to exist for the problem (at least at the time of writing of this comment) it is an answer with a plausible working solution with a refractor of my code. Might defeat the purpose of the exercise I imposed on myself, but presents a new interesting exercise in the same field. Thank you! – zeluisping Aug 15 '16 at 23:20