Newer
Older
Import / applications / HighwayDash / ports / Framework / Object.h
//  BlockyFroggy
//  Copyright © 2017 John Ryland.
//  All rights reserved.
#pragma once
#ifndef OBJECT_H
#define OBJECT_H


#include "Common.h"
#include "EventService.h"
#include <memory>
#include <vector>
#include <string>
#include <map>


struct SimpleTypeInfo
{
  std::string m_typeName;
  std::string m_parentTypeName;
  std::vector<std::string> m_functions;
};


std::map<std::string, SimpleTypeInfo> &RegisteredTypes()
{
  static std::map<std::string, SimpleTypeInfo> types;
  return types;
}


struct MetaData
{
  void addFunction(const char* retType, const char* funcName, ... /* args types and names */)
  {
  }
  void addVariable(const char* type, const char* name)
  {
  }
};


// Base object model

struct BasePimpl : public EventObject<BasePimpl>
{
  virtual ~BasePimpl() {}
  virtual std::shared_ptr<BasePimpl> deepCopy() const {
    return std::make_shared<BasePimpl>(*this);
  }
  virtual std::string dynamicTypeName() const {
    return "RefObject";
  }
  virtual std::string typeInformation() const {
    return "RefObject";
  }
  std::weak_ptr<BasePimpl> m_parent;
  std::vector<std::shared_ptr<BasePimpl>> m_children;
};


// done - Parent/Child hierachy of these objects
// done - Perhaps has reference counting - weak/strong based on parent/child relationship
// done - Perhaps has vtbl - try to make like a C#/Cx object
// done - Can be used together with events
//! @todo
//   Unit tests for the above
//   Perhaps has static type info - introspection - reflection - etc
//     - a table of function pointers and names - scriptability
//     - deeper than rtti - dynamically bind to UI and dynamically create objects and call their functions from a UI
//   Perhaps has visitor for serialization / deserialization
//   Tie it together with the language bindings - lua / python / javascript
class RefObject
{
public:
  typedef RefObject Type;
  typedef RefObject BaseType;
  typedef void      ParentType;
  typedef BasePimpl Pimpl;
  struct TypeName
  {
    static constexpr const char *value = "RefObject";
  };

  Type deepCopy(bool sliced = false) const { return deepCopyTemplate<Type>(sliced); }
  std::string dynamicTypeName() const { return m_data->dynamicTypeName(); }

  RefObject(std::shared_ptr<Pimpl> ptr)
    : m_data(ptr)
  {
  }
  
  operator bool() const { return m_data.operator bool(); }
  RefObject getParent() { return m_data->m_parent.lock(); }
  std::vector<std::shared_ptr<BasePimpl>> getChildren() { return m_data->m_children; }

  void setParent(RefObject a_parent)
  {
    if (getParent())
      getParent().removeChild(*this);
    m_data->m_parent = a_parent.m_data;
    getParent().addChild(m_data);
  }

protected:
  RefObject() {}
  
  void addChild(std::shared_ptr<BasePimpl> a_child)
  {
    m_data->m_children.push_back(a_child);
  }

  void removeChild(RefObject a_child)
  {
    //! @todo implement
    // m_data->m_children.erase()
  }

  std::shared_ptr<Pimpl> m_data;

  template <typename Type>
  Type deepCopyTemplate(bool sliced) const
  {
    typename Type::BaseType newObj;
    newObj.m_data = m_data->deepCopy();
    // The kind of slicing this would provides isn't really the bad kind of slicing as it is in the context of
    // when we are making a copy - is is a sliced copy. But is giving the option for slicing useful? Maybe.
    //newObj.m_data = (sliced) ? std::make_shared<typename Type::Pimpl>(*(static_cast<typename Type::Pimpl*>(m_data.get()))) : m_data->deepCopy();
    return *static_cast<Type*>(&newObj);
  }

  template <typename T, typename RetT, typename ...Args>
  RetT forward(RetT (T::*f)(Args...), Args... args) const
  {
    T* tptr = reinterpret_cast<T*>(m_data.get()); //! @todo static_cast might be safe here
    //if (tptr)
      return (*tptr.*f)(args...);
    //return RetT();
  }

  static struct TypeRegistrar
  {
    TypeRegistrar()
    {
      RegisteredTypes().emplace("RefObject", SimpleTypeInfo{ "RefObject", "void" });
    }
  } s_typeRegistrator;

  static void registerTypeFactories();
};


// This line is required
RefObject::TypeRegistrar RefObject::s_typeRegistrator;


// Root object of object tree - all unparented objects are parented to g_root
static std::shared_ptr<BasePimpl> getRootObject()
{
  static std::shared_ptr<BasePimpl> s_root = std::make_shared<BasePimpl>();
  return s_root;
}


//
// Based on: https://stackoverflow.com/questions/35666631/compile-time-literal-string-as-template-argument
//

template <char ... C>
struct TypeString
{
  static constexpr const char value[sizeof...(C)+1] = { C...,'\0' };
};

template <char ... C>
constexpr const char TypeString<C...>::value[sizeof...(C)+1];

#if 1

// Might not be 100% supported on all compilers, not standardized in C++ yet
// See below for an alternative fallback way
template <typename CharT, CharT... Cs>
constexpr TypeString<Cs...> operator ""_create()
{
    return {};
}

#define STRING_AS_TYPE(str)     decltype(str ## _create)

#else

// Alternative approach which should work on all compilers based on:
//  https://github.com/irrequietus/typestring/blob/master/typestring.hh
// however uses a MPL 2.0 license

template<int N, int M>
constexpr char tygrab(char const(&c)[M]) noexcept
{ return c[N < M ? N : M-1]; }

template<char... X>
auto typoke(TypeString<X...>) // as is...
  -> TypeString<X...>;

template<char... X, char... Y>
auto typoke(TypeString<X...>, TypeString<'\0'>, TypeString<Y>...)
  -> TypeString<X...>;

template<char A, char... X, char... Y>
auto typoke(TypeString<X...>, TypeString<A>, TypeString<Y>...)
  -> decltype(typoke(TypeString<X...,A>(), TypeString<Y>()...));

template<char... C>
auto typeek(TypeString<C...>)
  -> decltype(typoke(TypeString<C>()...));

#define TYPESTRING16(n,x) \
        tygrab<0x##n##0>(x), tygrab<0x##n##1>(x) \
      , tygrab<0x##n##2>(x), tygrab<0x##n##3>(x) \
      , tygrab<0x##n##4>(x), tygrab<0x##n##5>(x) \
      , tygrab<0x##n##6>(x), tygrab<0x##n##7>(x) \
      , tygrab<0x##n##8>(x), tygrab<0x##n##9>(x) \
      , tygrab<0x##n##A>(x), tygrab<0x##n##B>(x) \
      , tygrab<0x##n##C>(x), tygrab<0x##n##D>(x) \
      , tygrab<0x##n##E>(x), tygrab<0x##n##F>(x)

#define STRING_AS_TYPE(x) \
    decltype(typeek(TypeString<TYPESTRING16(0,x), TYPESTRING16(1,x)>()))

#endif


//template <typename T>
//struct PimplT;


template <typename T, typename ParentT, typename NameT>
class BaseObject : public ParentT
{
private:
  BaseObject() {}
public:
  friend class                                RefObject;
  //friend struct                               PimplT<T>;
  typedef BaseObject<T,ParentT,NameT>         BaseType;
  struct                                      Pimpl;
  friend struct                               Pimpl;
  typedef ParentT                             ParentType;
  typedef T                                   Type;
  typedef NameT                               TypeName;

  T deepCopy(bool sliced = false) const /* overloaded */ { return ParentT::template deepCopyTemplate<T>(sliced); }

  // The typename of this type - not the derived type the data actually is (determined using virtual)
  static std::string staticTypeName()
  {
    // return typeid(T).name(); // Not available if compile with -fno-rtti
    return TypeName::value;
  }

  static std::string staticParentTypeName()
  {
    return ParentT::TypeName::value;
  }

  template<typename... Args >
  static Type create(Args&&... args)
  {
    RefObject obj(std::make_shared<Pimpl>());
    Type o = *static_cast<Type*>(&obj); //! @todo dodgy if implementors of RefClasses start adding member variables etc
    o.setParent(getRootObject());
    o.init(std::forward<Args>(args)...);
    return o;
  }

  template<typename... Args >
  static Type createChild(RefObject parent, Args&&... args)
  {
    RefObject obj(std::make_shared<Pimpl>());
    Type o = *static_cast<Type*>(&obj); //! @todo dodgy if implementors of RefClasses start adding member variables etc
    o.setParent(parent);
    o.init(std::forward<Args>(args)...);
    return o;
  }

/*
  using BaseObjectTypeInformationItem = GenericFactoryItem<TypeInformation>;
  static std::vector<TypeInformation> getTypeInformation()
  {
  }
*/

protected:
  static MetaData getMeta();
  static struct TypeRegistrar
  {
    TypeRegistrar()
    {
      m_metaData = getMeta();

      // TypeInformation typeInfo;
      // BaseObject<int, RefObject, int>::BaseObjectTypeInformationItem g_register_testName("blah", typeInfo);
      RegisteredTypes().emplace(TypeName::value, SimpleTypeInfo{ TypeName::value, ParentType::TypeName::value });
    }
    template <class R, class F, class ...Ts>
    TypeRegistrar& func(const char* a_name, R (F::*a_func)( Ts...) ) {
      RegisteredTypes()[TypeName::value].m_functions.emplace_back(a_name);
      return *this;
    }
    MetaData m_metaData;
  } s_typeRegistrator;
};


// These 2 lines are required
template <typename T, typename ParentT, typename NameT>
typename BaseObject<T,ParentT,NameT>::TypeRegistrar BaseObject<T,ParentT,NameT>::s_typeRegistrator;


struct RefObjectFactory
{
  template <typename T, typename... Args>
  static T create(Args&&... args)
  {
    return T::create(std::forward<Args>(args)...);
  }
};


template <typename T>
struct PimplT : public T::ParentType::Pimpl
{
  typedef struct T::Type::Pimpl   BasePimplType;
  typedef struct PimplT<T>        ParentType;  // Parent type WRT to the class subclassing this class
  virtual std::shared_ptr<RefObject::Pimpl> deepCopy() const override
  {
    return std::make_shared<BasePimplType>(*static_cast<const BasePimplType*>(this));
  }
  virtual std::string dynamicTypeName() const override
  {
    return T::staticTypeName();
  }
};


#define ref(cls) \
  cls : public BaseObject< cls, RefObject, STRING_AS_TYPE(#cls) >

#define ref_inherit(cls,par) \
  cls : public BaseObject< cls, par, STRING_AS_TYPE(#cls) >


template <typename T, typename PropertyName>
class Property : public BaseObject<Property<T,PropertyName>,RefObject,STRING_AS_TYPE("Property<T,PropertyName>")>
{
public:
  // Forwarding public functions
  void init()          { return RefObject::forward(&Pimpl::zeroInit); }
  void init(T a_value) { return RefObject::forward(&Pimpl::valueInit, a_value); }
  T get()              { return RefObject::forward(&Pimpl::get); }
  void set(T a_value)  { return RefObject::forward(&Pimpl::set, a_value); }

  // Convienence operators
  //! @todo check that these don't hinder or create implicit conflicts with making referenced copies properly
  operator T()                                      { return get(); }
  Property<T,PropertyName>& operator=(T a_property) { set(a_property); return *this; }

  //! @todo be able to make this private or protected (it can be hidden inside a cpp file, so why can't it be protected?
  struct Pimpl : public PimplT<Property<T,PropertyName>>
  {
    // Actual implementation details
    virtual void zeroInit()           { }
    virtual void valueInit(T a_value) { set(a_value); }
    virtual T get()                   { return m_value; }
    virtual void set(T a_value)       { m_value = a_value; }
    T m_value;
  };
};


typedef Property<const char*, STRING_AS_TYPE("String")>  StringPropertyBase;


class ref_inherit(RefString, StringPropertyBase)
{
public:
  // Convienence operators
  operator std::string()                         { return std::string(StringPropertyBase::get()); }
  RefString& operator=(const std::string& a_str) { set(a_str.c_str()); return *this; }

  //! @todo be able to make this private or protected (it can be hidden inside a cpp file, so why can't it be protected?
  struct Pimpl final : public StringPropertyBase::Pimpl
  {
    // Actual implementation details
    ~Pimpl() override
    {
      delete m_value;
    }
    void zeroInit() override
    {
      m_value = nullptr;
    }
    void set(const char* a_str) override
    {
      size_t len = strlen(a_str);
      m_value = (char*)memcpy(new char[len], a_str, len);
    }
  };
};


// Variant is made a ref class so that it can nest itself inside objects, like DOM or JSON objects
// But lacks arrays without an array kind of object type. No KVP map/dict either like with JSON.
// Need something else for that.
//! @todo perhaps this needs more thinking through and testing
//        - Perhaps either just single value or full JSON style support
//        - variant without nested objects, and then a value class with support for variants and object model
class ref(RefVariant)
{
public:
  enum Type
  {
    Invalid,
    Void,
    Bool,
    Char,
    String,
    Int,
    Float,
    Object
  };

  union Value
  {
    char        m_void;
    bool        m_bool;
    uint8_t     m_char;
    RefString   m_str;
    int64_t     m_int;
    double      m_flt;
    RefObject   m_object;
  };

  Type  m_type;
  Value m_value;
};



#define DECLARE_OBJECT_TYPE(name) \
  struct name : public EventObject<name> \
  { \
    private: \
      typedef name _this_type; \
      static const char* _this_name() { return #name; } \
    public: \
      name() { \
        Reset(); \
      } \
      typedef struct { \
        template <class V> \
        static void Visit(_this_type* o, V& v) { \
        }

#define DECLARE_MEMBER(type, name, defaultValue) \
      } blah##name; \
      \
      type name; \
      \
      typedef struct { \
        template <class V> \
        static void Visit(_this_type* o, V& v) { \
          blah##name::Visit(o, v); \
          v.Visit(#name, o->name, defaultValue); \
        }

#define BEGIN_MEMBER_FUNCTION(returnType, name, params) \
      } blah##name; \
      returnType name params \
      {

#define END_MEMBER_FUNCTION(name) \
      } \
      typedef struct { \
        template <class V> \
        static void Visit(_this_type* o, V& v) { \
          blah##name::Visit(o, v); \
        }

#define END_OBJECT_TYPE(name) \
      } last; \
      void Reset() \
      { \
        ResetVisitor r; \
        last::Visit(this, r); \
      } \
      template <class V> \
      void Visit(V& v) \
      { \
        v.Enter(_this_name()); \
        last::Visit(this, v); \
        v.Exit(_this_name()); \
      } \
      static void someFunc() { } \
  }; \
  ObjectFactoryItem g_registerObject_##name(#name, &name::someFunc);


typedef void (*ObjectFunc)();
using ObjectFactoryItem = GenericFactoryItem<ObjectFunc>;


class ResetVisitor
{
public:
  ResetVisitor() {}

  void Enter(const char* a_scope) {}
  void Exit(const char* a_scope) {}

  template <typename T1, typename T2>
  void Visit(const char* a_memberName, T1& a_value, const T2& a_defaultValue)
  {
    a_value = a_defaultValue;
  }
};


#endif // OBJECT_H