Newer
Older
Import / research / ui / toolkit / include / Object.h
#pragma once
#include <list>
#include <typeinfo>
#include <cstdlib>
#include <memory>
#include "Common.h"
#include "Property.h"
#ifdef __GNUG__
# include <cxxabi.h>
#endif
BEGIN_NAMESPACE


/*


     Example Object Tree
     -------------------


            Window
              |
             HBox
              |
         ------------
         |          |
        VBox     RenderView
         |
  ----------------------
  |         |           |
Button_1 Button_2  Button_3


This object hierarchy is determined at runtime by the way in which objects are created and parented to each other.
To first create the tree requires objects to have parents and children. To inspect the tree it is useful for objects
to have a name. It is then possible to print a representation of the tree. To introspect deeper in to each object
requires knowledge of what type each object is and be able to query the reflected exposed members. This requires
object types to have meta data or type information that can be retrieved using a base object pointer.


     Example Type Hierarchy
     ----------------------


            Object
              |
        -------------
        |           |
   Derived_Ex     Widget
                    |
              --------------
              |             |
           Check_Box     Button


This is the class hierarchy which is static. It can be useful to find from a collection of objects the objects
which implement a certain interface for example. C++ provides dynamic_cast which can be used to test if a object
is or inherits from the given type. C# has a nicer type system with various features like Type t = object.GetType(),
object is T, Type t = typeof(obj), var Ts = list.OfType<T>(), Type t = typeof(obj); printf("%s", t);

First thing to notice is that "Type" is a thing, in C++ we could make a Type class and have Type instances.
A Type object ideally just wraps a type identifier which could be an int, and from that int it is possible to
look up more info such as the name for printing. The Type class could have a cast operator to convert to a const char *.

typeof can be implemented as a template function. GetType can be a member of Object.

Requires a Map of typeId -> String for looking up the name.
Should probably have a reverse Map to lookup the typeId from a String, so this could be a BiMap.

And another Map to look up the full reflection information from a typeId.


Use Cases
---------

- Serialization/De-serialization
  - from disk/file
  - through a network - replication
  - passing arguments for implementing RPC

- Dynamic content creation / editing
  - display object tree in a tree view
  - manipulate properties of objects
  - create new instances etc

- Testing
  - query state
  - quick tweaking of constants while developing

- Detect state changes
  - Register a callback when something changes

*/


class TypeUtil
{
public:
  static String demangle(const char* name)
  {
#if defined(__GNUC__) || defined(__clang__)
    int status = -1; // some arbitrary value to eliminate the compiler warning
    std::unique_ptr<char, void(*)(void*)> res{ abi::__cxa_demangle(name, NULL, NULL, &status), std::free };
    return (status == 0) ? res.get() : name;
#else
    return name;
#endif
  }
};


template <class T>
class TypeInfo
{
public:
  // Non-RTTI approach using __FUNCTION__ extension to get de-mangled name
  static String prettyName()
  {
#if defined(__GNUC__) || defined(__clang__)
    String name = __PRETTY_FUNCTION__;
#else
    String name = __FUNCTION__;
#endif
    // fprintf(stderr, "Func: -%s-\n", name.c_str());
    size_t pos1 = name.find_first_of('<');
    if (pos1 != std::string::npos)
    {
      pos1++;
      size_t pos2 = pos1;
      int depth = 0;
      for (; pos2 < name.size(); pos2++)
      {
        if (name.c_str()[pos2] == '<')
          depth++;
        else if (name.c_str()[pos2] == '>') {
          depth--;
          if (depth < 0)
            break;
        }
      }
      name = name.substr(pos1, pos2-pos1);
    }
    // fprintf(stderr, "TypeInfo::name() returning: -%s-\n", name.c_str());
    return name;
  }

#ifndef NO_RTTI
  // RTTI Mangled Type Name
  static String rttiName()
  {
    return typeid(T).name();
  }

  // RTTI De-mangled Type Name
  static String rttiDemangledName()
  {
    return TypeUtil::demangle(rttiName().c_str());
  }
#endif
};


template <class T>
String typeName()
{
  return TypeInfo<T>::prettyName();
  //return TypeInfo<T>::rttiName();
  //return TypeInfo<T>::rttiDemangledName();
}


// TODO: I don't really like things being nullable, but this is one way to do it
// Currently it is set up to runtime assert if it is accessed without being set
// Preferable would be to move that to compile-time if it is possible. Is that possible?
// Could the type change if it is set, and the different type can static_assert if it
// is then accessed without being set? Or perhaps enforce that you check it before
// accessing it? Eg: hasValue returns an object that has the accessor.
// Perhaps not, the idea is that you can pass these between functions, so from a given
// function it isn't possible to know if it was set or not. The given function that
// accepts a nullable though could be templated to have two versions, one where the
// nullable was set and one where it wasn't - it being set is a trait
template <class T>
class Nullable
{
public:
  bool hasValue() const { return m_hasBeenSet; }
  T value() const { assert(m_hasBeenSet); return m_value; }
  T& operator()() { assert(m_hasBeenSet); return m_value; }
  Nullable<T>& operator=(const T& a_other) { m_hasBeenSet = true; m_value = a_other; return *this; }
private:
  bool m_hasBeenSet = false;
  T m_value;
};


template <class T, bool inited = false>
class Nullable2
{
public:
  bool hasValue() const { return inited; }
  T value() const { static_assert(inited, "needs to be assigned to first"); return m_value; }
  T& operator()() { static_assert(inited, "needs to be assigned to first"); return m_value; }
  Nullable2<T, true>& operator=(const T& a_other) { m_value = a_other; return *((Nullable2<T,true>*)this); }
protected://ivate:
  friend class Nullable2<T, false>;
  friend class Nullable2<T, true>;
  T m_value;
};


// TODO: incomplete - need to add populating the type names
//       basic idea for making Type as a class so can pass Types around
class Type
{
public:
  Type(int a_typeId) : m_typeId(a_typeId) {}

  bool isValid() const
  {
    return getTypeIdMap().count(m_typeId) != 0;
  }
  
  const String& asString() const
  {
    return getTypeIdMap()[m_typeId];
  }

  const char* operator()() const
  {
    return asString().c_str();
  }

  bool operator!=(const Type& a_other) const { return m_typeId != a_other.m_typeId; }
  bool operator==(const Type& a_other) const { return m_typeId == a_other.m_typeId; }

  // This can't be called more than once for a given type, best way will be to make a bimap to ensure this
  template <class T>
  static void registerType();

private:
  static Map<int, String>& getTypeIdMap()
  {
    static Map<int, String> typeIdMap;
    return typeIdMap;
  }

  static Map<String, int>& getTypeNameMap()
  {
    static Map<String, int> typeNameMap;
    return typeNameMap;
  }

  int m_typeId;
};


// TODO: implement for basic types like  int, float, String etc, then an enable_if for Object types, and
//       another for structs and classes. Needs to automaticaaly populate the typeNameMap too.
template <typename T>
Type typeOf(const T& obj)
{
}


struct Member
{
  void*  offset;
  String typeName; // TODO: convert to a typeId
  String memberName;
  String description;
};


class Object;
class ReflectionDataInterface : public InterfaceBase
{
public:
  virtual int getTypeId() const = 0;
  virtual std::string getType() const = 0;
  virtual size_t getSize() const = 0;
  virtual Object* create(Object* a_parent = 0, const char* a_name = 0) const = 0;
  virtual void destroy(Object* o) const = 0;
  virtual const Map<String,Member>& getMembersMap() const = 0; // TODO: Property should have proper type and the map can inform it
  virtual Map<String,Member>& getMembersMapNonConst() const = 0; // TODO: Hide this?
};




class MemberVisitor
{
public:
  MemberVisitor(Map<String,Member>& a_membersMap) : m_membersMap(a_membersMap) { }
  template <typename T>
  void operator()(const char* name, T* t, const char* desc) { m_membersMap[name] = { (void*)t, typeName<T>(), name, desc }; }
private:
  Map<String,Member>& m_membersMap;
};


// Mix-in shim between the inheritance hierarchy of object types to provide introspection and reflection of the hierarchy
//   TODO: is it possible to force needing to use this to inherit from Object and then subsequently from sub-classes?
template <typename ParentType, typename ThisType>
class Inherit : public ParentType
{
public:
  // Convenience type aliases
  typedef ParentType                   ParentT;
  typedef Inherit<ParentType,ThisType> BaseT;
  typedef ThisType                     ThisT;

  // Pass-through forwarding constructor
  template <typename ...Ts>
  Inherit(Ts... args) : ParentT(args...) {}

  // This looks identical to in Object, but I believe that re-implementing here
  // will cause a new static variable in each instance this template is expanded for new types
  static int staticTypeId();
  int virtualTypeId() override  { return ThisT::staticTypeId(); }
  
  Type getType() { return Type(virtualTypeId()); }

  // TODO: This is better if do like this in Object too, but only if can enforce using Inherit everywhere
  // otherwise it is dangerous by giving incorrect results, being the type name up to where Inherit was last used.
  //static std::string typeName() { return typeName<ThisT>(); }

  // Returns true if this ThisT is descended from T, otherwise returns false
  template <typename T>
  static bool staticInherits() { return _inherits((T*)0); }

  // Returns true if this ThisT is a T or descended from T, otherwise returns false
  template <typename T>
  static bool staticIs() { return _is((T*)0); }
  
  //template <typename T>
  //bool is()              { return virtualTypeId() == T::staticTypeId() || ParentT::template is<T>(); }
  
  bool isCheck(int Tid) override       { return staticTypeId() == Tid || ParentT::isCheck(Tid); }

  bool inheritsCheck(int Tid) override { return ParentT::isCheck(Tid); } // staticInherits(); }

  // Callback to add this type in to the inheritance map for use by reflection
  static void registerInheritance(Map<String,String>& inheritanceMap)
  {
    //ParentT::registerInheritance(inheritanceMap);  // Expectation is ParentT is a registered object type
    inheritanceMap[typeName<ThisT>()] = typeName<ParentT>();
  }

  // TODO: include callable members - turn args in to a struct as input type
  static void registerMembers(Map<String,Member>& memberMap)
  {
    //ParentT::registerMembers(memberMap);  // visit calls parent::visit for us
    MemberVisitor v(memberMap);
    ThisT::visit(v);
  }

private:
  template<typename T>
  static bool _inherits(T*)          { return ParentT::template staticInherits<T>(); }
  static bool _inherits(ParentT*)    { return true; }

  template<typename T>
  static bool _is(T*)                { return ParentT::template staticIs<T>(); }
  static bool _is(ThisT*)            { return true; }
};


class NullObject
{
public:
	virtual ~NullObject() {}

  virtual int virtualTypeId() { return -1; }

  template <typename T>
  static bool staticInherits() { return false; }
  
  template <typename T>
  static bool staticIs() { return false; }

  template <typename T>
  static void visit(T& v) { }

  virtual bool isCheck(int Tid) { return false; }
  virtual bool inheritsCheck(int Tid) { return false; }
};


class Object : public Inherit<NullObject, Object>
{
public:
  // a_parent is pointer so it can be null
	Object(Object* a_parent = nullptr, const char* a_name = nullptr);
	virtual ~Object();

  //static String typeName()      { return typeName<ThisT>(); }
  //String typeName() const       { return typeName<decltype(*this)>(); }

  template <typename T>
  typename std::enable_if<std::is_base_of<Object, T>::value, bool>::type
  inherits()                    { return inheritsCheck(T::staticTypeId()); }
  
  template <typename T>
  typename std::enable_if<std::is_base_of<Object, T>::value, bool>::type
  is()                          { return isCheck(T::staticTypeId()); }

  String name() const           { return m_name; }   // TODO: Make a property
  Object* parent() const        { return m_parent; } // TODO: Make a property

  template <typename T>
  T* parent() const;

  // TODO: Enumerators with functors - so you don't need to copy to another list to do this
  //       idea is that you can iterate the children and find the next one which matches the criteria
  template <typename T>
  std::list<T*> children() const;

  // Reflection related functions
  template <typename T>
  static void registerObjectType();

  static const Map<String,ReflectionDataInterface*>& objectTypes()              { return getReflectionDataMap(); }

  static const ReflectionDataInterface* getReflectionData(const char* typeName) { return objectTypes().at(typeName); }
  //const ReflectionDataInterface* getReflectionData() const                      { return objectTypes().at(typeName()); }
  //template <typename T>
  //static const ReflectionDataInterface* getReflectionData()                     {  return objectTypes().at(typeName<T>()); }
  
  static const Map<String,String>& getInheritanceMap() { return getInheritanceMapNonConst(); }

  // Prints out all the reflection information
  static void dumpReflectionInformation();

  // TODO: move from object to type system
  static int getNewTypeId() { static int s_typeId = 0; return s_typeId++; }

protected:
	void         addChild(Object* a_object); // Perhaps a_object should be reference instead if possible
  virtual void childAdded(Object* a_object);

private:
  String             m_name;

  // Could the hierarchy exist outside of the object in a more compact form?
  // Some kind of compact tree not based on pointers? 64bit pointers are large, indexes can be smaller,
  // if can assume less than 65536 objects under a root object, then 16bit indexes would be smaller.
  // So could have a list of pointers, and then a 8bit child count, and n 16bit indexes.
  // If the list is configured in flat lists, the indexes can be inferred, so just need the child counts
  // There is then quite a bit of computation to then work back out the indexes, but a simpler idea is just having a base index and count
  // then the child indexes are inferred to be sequential from the base index. So just need a count and base index. In 32bits could do 16.16 or 8.24
  // Perhaps parent can also be removed if based on context you might know it because you follow a child down to get to the object you want the parent of
  // so it may exist in the stack and no need to store it in the object. But not 100% sure. Looking at the code, probably do need the parent.
  // Could store it as a parent index.
  // If aim for 24bit indexes, could do 1 byte for which root, 3 bytes for parent index, 1 byte for child count, and 3 bytes for child base index
  // So 64 bits for parent and children info inside the object. Outside the object requires a flat array of object pointers with each root object.
  // This array will develop holes in it, so may need to implement free lists in them by co-opting the object pointers to store indexes to the
  // next free item and how many children are in the free block (its length)
  // Another idea - m_children represents a group of siblings. The siblings could contain a m_nextSibling pointer which forms a ring, so starting
  // at any one of them it is possible to go through all of them.
  // This scheme does away with a list, but there are 3 pointers in the object, parent, firstChild, nextSibling.
  // The redundancy here is that all the siblings contain a common parent pointer
  // The last child in the chain doesn't need to link back to the first, it could contain a null_ptr, so possibly the sibling and parent pointers
  // could be the same pointer if have a way to know at the last one, but it would be expensive if many children to retrieve the parent pointer.
  //
  Object*            m_parent;
	std::list<Object*> m_children;  // Vector instead? - most of the time we are iterating them, not adding/removing them often
                                  // order is important, and there is possibility to be removing from middle of the list
  static Map<String,String>& getInheritanceMapNonConst() { static Map<String,String> map; return map; }
  static Map<String,ReflectionDataInterface*>& getReflectionDataMap();
};


/*
#define START_MEMBERS \
           typedef struct { \
             template <typename T> static inline void visit(T&) { }

#define REFLECT_MEMBER_BASE(name, desc) \
           } detail_##name; \
           template <typename RetT, typename ...Ts> \
           static RetT static_##name(ThisT* obj, Ts... args) { return obj->name(args...); } \
           template <typename RetT, typename ...Ts> \
           static auto pFunc_##name(RetT (ThisT::*t)(Ts... args)) -> RetT(*)(ThisT*, Ts...) { RetT (*pFunc)(ThisT*, Ts...) = static_##name; return pFunc; } \
           typedef struct { \
             template <typename T> \
             static inline void visit(T& v) { detail_##name::visit(v); \

// Using "Ryland's Device"
#define REFLECT_MEMBER(name, desc) \
           REFLECT_MEMBER_BASE(name, desc) \
               v(#name, &((ThisT*)nullptr)->name, desc); \
             }

#define REFLECT_MEMBER_FUNCTION(name, desc) \
           REFLECT_MEMBER_BASE(name, desc) \
               v(#name, pFunc_##name(&ThisT::name), desc); \
             }

#define ADD_MEMBER(type, name, desc) \
           } detail_##name; \
           type name; \
           typedef struct { \
             template <typename T> \
             static inline void visit(T& v) { detail_##name::visit(v); \
               v(#name, &((ThisT*)nullptr)->name, desc); \
             }

#define END_MEMBERS \
           } detail_last; \
           template <typename T> static void visit(T& v) { detail_last::visit(v); }
*/



template <typename T, T> struct MemberProxy;

// Member functions
template <typename T, typename R, typename ...Args, R (T::*mf)(Args...)>
struct MemberProxy<R (T::*)(Args...), mf>
{
  static R call(T & obj, Args &&... args)
  {
    return (obj.*mf)(std::forward<Args>(args)...);
  }
  static auto get() -> R(*)(T &, Args &&...)
  {
    return &call;
  }
};

// Member variables
template <typename T, typename R, R T::*mv>
struct MemberProxy<R T::*, mv>
{
  static R* get()
  {
    return &(((T*)nullptr)->*mv);
  }
};


// TODO: https://stackoverflow.com/questions/9779105/generic-member-function-pointer-as-a-template-parameter
//  On the SO article, mention the improved version above and usage with:
//    MemberProxy<decltype(&ThisT::name),&ThisT::name>::get()
//  which works for a member variable or member function.
//  Before posting, first verify that this can work as expected

#define START_MEMBERS \
           template <typename T> \
           static void visit(T& v) \
           { \
             ParentT::visit(v);

#define REFLECT_MEMBER(name, desc) \
             v(#name, MemberProxy<decltype(&ThisT::name),&ThisT::name>::get(), desc);

#define REFLECT_MEMBER_FUNCTION(name, desc) \
             REFLECT_MEMBER(name, desc)

#define ADD_MEMBER(type, name, desc)
             
#define END_MEMBERS \
           }


// TODO : perhaps see if instead of in a map, this data is static data in each object type (except create/destroy etc)
template <class T>
class ReflectionData : public ReflectionDataInterface
{
public:
  /* Add this to ReflectionMap and ensure uniquely added - single instance for given type */
  // ReflectionData() : m_typeName("uninited"), m_typeId(g_typeId++) { }
  ReflectionData(int a_typeId, std::string a_typeName) : m_typeName(a_typeName), m_typeId(a_typeId) { }
  ~ReflectionData()                {}
  int getTypeId() const override         { return m_typeId; }
  std::string getType() const override   { return m_typeName; } // inheritance info ?
  size_t getSize() const override        { return sizeof(T); }
  Object* create(Object* a_parent = 0, const char* a_name = 0) const override { return new T(a_parent, a_name); }
  void destroy(Object* o) const override { delete o; }
  const Map<String,Member>& getMembersMap() const override { return getMembersMapNonConst(); }
  Map<String,Member>& getMembersMapNonConst() const override {
    static Map<String,Member> map;
    return map;
  }
private:
  std::string m_typeName;
  int m_typeId = -1;
};


template <typename T>
void Object::registerObjectType()
{
  String typeStr = typeName<T>();
  fprintf(stderr, "Registering object type: %s\n", typeStr.c_str());
  if (getReflectionDataMap().count(typeStr)) {
    fprintf(stderr, "Error, registering an object type (%s) more than once\n", typeStr.c_str());
  } else {
    getReflectionDataMap()[typeStr] = new ReflectionData<T>(T::staticTypeId(), typeStr);
    T::registerMembers(getReflectionDataMap()[typeStr]->getMembersMapNonConst());
    T::registerInheritance(getInheritanceMapNonConst());
  }
}





// TODO: Potentially if compile without RTTI then this might be able to be reimplemented using
// the reflection and introspection features that will be added to Object - eg: isA, but
// current road block is can't do virtual templates - need to polymorphically call on a_obj
// to query for a given type if it is supported. Perhaps another way might be if T can be
// captured in to a non-templated object in some way. function objects like what std::bind
// capture might be doing something like this, so there might be a way
template <typename T>
T* ObjectPtrCast(Object* a_obj)
{
#ifndef NO_RTTI
  return dynamic_cast<T*>(a_obj);
#else
  //printf("Attempting ObjectPtrCast\n");
  //printf("Attempting cast dest type and id: %s  %i\n", typeName<T>().c_str(), T::staticTypeId());
  if (!a_obj) {
    printf("null object in ObjectPtrCast\n");
    return 0;
  }
  // virtual templates are not allowed
  if (a_obj->is<T>())
    return static_cast<T*>(a_obj);
  int typeId = a_obj->virtualTypeId();
  const char* name = a_obj->name().c_str();
  if (typeId != 0)
    printf("Failed cast. Source object (%s) id: %i, Dest type and id: %s  %i\n", (name) ? name : "null", typeId, typeName<T>().c_str(), T::staticTypeId());
  return 0;
#endif
}


/*
template <typename T>
T& ObjectCast(Object& a_obj)
{
  // virtual templates are not allowed
  if (a_obj.is<T>())
    return reinterpret_cast<T&>(a_obj);
  return 0;
}
*/


template <typename ParentType, typename ThisType>
int Inherit<ParentType, ThisType>::staticTypeId()
{
  static int s_typeId = Object::getNewTypeId();
  return s_typeId;
}


template <class T>
void Type::registerType()
{
  String name = typeName<T>();
  if (getTypeNameMap().count(name))
  {
    fprintf(stderr, "type already registered: %s\n", name.c_str());
    return;
  }
  int newId = Object::getNewTypeId();
  getTypeIdMap().insert(newId, name);
  getTypeNameMap().insert(name, newId);
}


template <typename T>
T* Object::parent() const
{
  return ObjectPtrCast<T>(m_parent);
}


template <typename T>
std::list<T*> Object::children() const
{
  std::list<T*> kids;
  for (Object* obj : m_children) {
    T* kid = ObjectPtrCast<T>(obj);
    if (kid)
      kids.push_back(kid);
  }
  return kids;
}


END_NAMESPACE