// 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