mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
fahhh
This commit is contained in:
@@ -0,0 +1,597 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "reflection/type.h"
|
||||
#include "security/securitycontext.h"
|
||||
#include "reflection/member.h"
|
||||
#include "reflection/Type.h"
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
#include <boost/type_traits.hpp>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Reflection
|
||||
{
|
||||
class Callback;
|
||||
|
||||
// Base class that describes a Callback
|
||||
class RBXBaseClass CallbackDescriptor : public MemberDescriptor
|
||||
{
|
||||
public:
|
||||
typedef Callback ConstMember;
|
||||
typedef Callback Member;
|
||||
|
||||
protected:
|
||||
SignatureDescriptor signature;
|
||||
bool async;
|
||||
|
||||
CallbackDescriptor(ClassDescriptor& classDescriptor, const char* name, Descriptor::Attributes attributes, Security::Permissions security, bool async);
|
||||
public:
|
||||
inline const SignatureDescriptor& getSignature() const { return signature; }
|
||||
bool isAsync() const { return async; }
|
||||
};
|
||||
|
||||
class SyncCallbackDescriptor : public CallbackDescriptor
|
||||
{
|
||||
protected:
|
||||
SyncCallbackDescriptor(ClassDescriptor& classDescriptor, const char* name, Descriptor::Attributes attributes, Security::Permissions security);
|
||||
public:
|
||||
typedef boost::function<shared_ptr<Reflection::Tuple>(shared_ptr<const Reflection::Tuple> args)> GenericFunction;
|
||||
|
||||
// the preferred way to set a generic function:
|
||||
virtual void setGenericCallback(DescribedBase* object, shared_ptr<GenericFunction> function) const = 0;
|
||||
virtual void clearCallback(DescribedBase* object) const = 0;
|
||||
|
||||
// use this function if you don't have a shared_ptr:
|
||||
void setGenericCallbackHelper(DescribedBase* object, const GenericFunction& function) const;
|
||||
};
|
||||
|
||||
class AsyncCallbackDescriptor : public CallbackDescriptor
|
||||
{
|
||||
public:
|
||||
typedef boost::function<void(shared_ptr<const Reflection::Tuple>)> ResumeFunction;
|
||||
typedef boost::function<void(std::string)> ErrorFunction;
|
||||
typedef boost::function<void(shared_ptr<const Reflection::Tuple> args, ResumeFunction resumeFunction, ErrorFunction errorFunction)> GenericFunction;
|
||||
|
||||
// the preferred way to set a generic function:
|
||||
virtual void setGenericCallback(DescribedBase* object, shared_ptr<GenericFunction> function) const = 0;
|
||||
virtual void clearCallback(DescribedBase* object) const = 0;
|
||||
|
||||
// use this function if you don't have a shared_ptr:
|
||||
void setGenericCallbackHelper(DescribedBase* object, const GenericFunction& function) const;
|
||||
|
||||
protected:
|
||||
AsyncCallbackDescriptor(ClassDescriptor& classDescriptor, const char* name, Descriptor::Attributes attributes, Security::Permissions security);
|
||||
|
||||
static void callGenericImpl(shared_ptr<AsyncCallbackDescriptor::GenericFunction> function, shared_ptr<Tuple> args,
|
||||
AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction);
|
||||
|
||||
template <typename Class, typename Function, typename Value>
|
||||
void setGenericCallbackImpl(DescribedBase* object, Function Class::*member, void (Class::*onChanged)(const Function&), const Value& value) const
|
||||
{
|
||||
Class* c = static_cast<Class*>(object);
|
||||
|
||||
Function oldValue = c->*member;
|
||||
c->*member = value;
|
||||
if (onChanged)
|
||||
(c->*onChanged)(oldValue);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A light-weight convenience class that associates a CallbackDescriptor
|
||||
// with a described object to create a "Callback"
|
||||
class Callback
|
||||
{
|
||||
const CallbackDescriptor* descriptor;
|
||||
DescribedBase* instance;
|
||||
public:
|
||||
inline Callback(const CallbackDescriptor& descriptor, DescribedBase* instance)
|
||||
:descriptor(&descriptor),instance(instance)
|
||||
{}
|
||||
|
||||
inline Callback(const Callback& other)
|
||||
:descriptor(other.descriptor),instance(other.instance)
|
||||
{}
|
||||
|
||||
inline Callback& operator =(const Callback& other)
|
||||
{
|
||||
this->descriptor = other.descriptor;
|
||||
this->instance = other.instance;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const RBX::Name& getName() const {
|
||||
return descriptor->name;
|
||||
}
|
||||
|
||||
inline DescribedBase* getInstance() const
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
inline const CallbackDescriptor& getDescriptor() const
|
||||
{
|
||||
return *descriptor;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Base class of typed CallbackDescriptors
|
||||
template<typename Signature>
|
||||
class SyncCallbackDesc : public SyncCallbackDescriptor
|
||||
{
|
||||
protected:
|
||||
typedef typename boost::function<Signature> Function;
|
||||
typedef typename boost::function_traits<Signature>::result_type result_type;
|
||||
|
||||
template<typename Result>
|
||||
static typename boost::enable_if<boost::is_void<Result>, void>::type
|
||||
callGeneric(shared_ptr<SyncCallbackDescriptor::GenericFunction> function, shared_ptr<Tuple> args)
|
||||
{
|
||||
(*function)(args);
|
||||
}
|
||||
|
||||
template<typename Result>
|
||||
static typename boost::disable_if<boost::is_same<shared_ptr<const Tuple>, Result>, Result>::type
|
||||
convertResult(shared_ptr<Reflection::Tuple> result)
|
||||
{
|
||||
// Extract the first value in the Tuple and return it as the result. Ignore other vales
|
||||
if (result->values.size() == 0)
|
||||
throw std::runtime_error("Callback did not return a value");
|
||||
return result->values[0].convert<Result>();
|
||||
}
|
||||
|
||||
template<typename Result>
|
||||
static typename boost::enable_if<boost::is_same<shared_ptr<const Tuple>, Result>, Result>::type
|
||||
convertResult(shared_ptr<Reflection::Tuple> result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename Result>
|
||||
static typename boost::disable_if<boost::is_void<Result>, Result>::type
|
||||
callGeneric(shared_ptr<SyncCallbackDescriptor::GenericFunction> function, shared_ptr<Tuple> args)
|
||||
{
|
||||
shared_ptr<Reflection::Tuple> result = (*function)(args);
|
||||
return convertResult<Result>(result);
|
||||
}
|
||||
|
||||
class RBXInterface ISetter
|
||||
{
|
||||
public:
|
||||
virtual ~ISetter() {}
|
||||
virtual void setCallback(DescribedBase* object, const Function& value) const = 0;
|
||||
};
|
||||
boost::scoped_ptr<ISetter> setter;
|
||||
|
||||
SyncCallbackDesc(ClassDescriptor& classDescriptor, const char* name, Descriptor::Attributes attributes, Security::Permissions security):
|
||||
SyncCallbackDescriptor(classDescriptor, name, attributes, security)
|
||||
{}
|
||||
public:
|
||||
void setCallback(DescribedBase* object, const Function& value) const
|
||||
{
|
||||
setter->setCallback(object, value);
|
||||
}
|
||||
void clearCallback(DescribedBase* object) const
|
||||
{
|
||||
setter->setCallback(object, Function());
|
||||
}
|
||||
};
|
||||
|
||||
// Specialized class that implements generic bindings and sets the signature
|
||||
template <typename Signature, int arity>
|
||||
class SyncCallbackDescImpl;
|
||||
|
||||
template<typename Signature>
|
||||
class SyncCallbackDescImpl<Signature, 0> : public SyncCallbackDesc<Signature>
|
||||
{
|
||||
typedef typename SyncCallbackDesc<Signature>::result_type result_type;
|
||||
static result_type callGeneric(shared_ptr<SyncCallbackDescriptor::GenericFunction> function)
|
||||
{
|
||||
shared_ptr<Reflection::Tuple> args(new Tuple());
|
||||
return SyncCallbackDesc<Signature>::template callGeneric<result_type>(function, args);
|
||||
}
|
||||
|
||||
protected:
|
||||
SyncCallbackDescImpl(ClassDescriptor& classDescriptor, const char* name, Descriptor::Attributes attributes, Security::Permissions security)
|
||||
:SyncCallbackDesc<Signature>(classDescriptor, name, attributes, security)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::function_traits<Signature>::arity == 0));
|
||||
this->signature.resultType = &Type::singleton<result_type>();
|
||||
}
|
||||
public:
|
||||
virtual void setGenericCallback(DescribedBase* object, shared_ptr<SyncCallbackDescriptor::GenericFunction> function) const
|
||||
{
|
||||
this->setCallback(object, boost::bind(callGeneric, function));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class SyncCallbackDescImpl<Signature, 1> : public SyncCallbackDesc<Signature>
|
||||
{
|
||||
typedef typename SyncCallbackDesc<Signature>::result_type result_type;
|
||||
static result_type callGeneric(shared_ptr<SyncCallbackDescriptor::GenericFunction> function,
|
||||
// TODO: Use const ref for args and bind with boost::cref?
|
||||
typename boost::function_traits<Signature>::arg1_type arg1)
|
||||
{
|
||||
shared_ptr<Reflection::Tuple> args(new Tuple());
|
||||
args->values.push_back(arg1);
|
||||
return SyncCallbackDesc<Signature>::template callGeneric<result_type>(function, args);
|
||||
}
|
||||
|
||||
protected:
|
||||
SyncCallbackDescImpl(ClassDescriptor& classDescriptor, const char* name, const char* arg1name, Descriptor::Attributes attributes, Security::Permissions security)
|
||||
:SyncCallbackDesc<Signature>(classDescriptor, name, attributes, security)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::function_traits<Signature>::arity == 1));
|
||||
this->signature.resultType = &Type::singleton<result_type>();
|
||||
this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton<typename boost::function_traits<Signature>::arg1_type>());
|
||||
}
|
||||
public:
|
||||
virtual void setGenericCallback(DescribedBase* object, shared_ptr<SyncCallbackDescriptor::GenericFunction> function) const
|
||||
{
|
||||
this->setCallback(object, boost::bind(callGeneric, function, _1));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class SyncCallbackDescImpl<Signature, 2> : public SyncCallbackDesc<Signature>
|
||||
{
|
||||
typedef typename SyncCallbackDesc<Signature>::result_type result_type;
|
||||
static result_type callGeneric(shared_ptr<SyncCallbackDescriptor::GenericFunction> function,
|
||||
typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2)
|
||||
{
|
||||
shared_ptr<Reflection::Tuple> args(new Tuple());
|
||||
args->values.push_back(arg1);
|
||||
args->values.push_back(arg2);
|
||||
return SyncCallbackDesc<Signature>::template callGeneric<result_type>(function, args);
|
||||
}
|
||||
|
||||
protected:
|
||||
SyncCallbackDescImpl(ClassDescriptor& classDescriptor, const char* name, const char* arg1name, const char* arg2name, Descriptor::Attributes attributes, Security::Permissions security)
|
||||
:SyncCallbackDesc<Signature>(classDescriptor, name, attributes, security)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::function_traits<Signature>::arity == 2));
|
||||
this->signature.resultType = &Type::singleton<result_type>();
|
||||
this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton<typename boost::function_traits<Signature>::arg1_type>());
|
||||
this->signature.addArgument(RBX::Name::declare(arg2name), Type::singleton<typename boost::function_traits<Signature>::arg2_type>());
|
||||
}
|
||||
public:
|
||||
virtual void setGenericCallback(DescribedBase* object, shared_ptr<SyncCallbackDescriptor::GenericFunction> function) const
|
||||
{
|
||||
this->setCallback(object, boost::bind(callGeneric, function, _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class SyncCallbackDescImpl<Signature, 3> : public SyncCallbackDesc<Signature>
|
||||
{
|
||||
typedef typename SyncCallbackDesc<Signature>::result_type result_type;
|
||||
static result_type callGeneric(shared_ptr<SyncCallbackDescriptor::GenericFunction> function,
|
||||
typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3)
|
||||
{
|
||||
shared_ptr<Reflection::Tuple> args(new Tuple());
|
||||
args->values.push_back(arg1);
|
||||
args->values.push_back(arg2);
|
||||
args->values.push_back(arg3);
|
||||
return SyncCallbackDesc<Signature>::template callGeneric<result_type>(function, args);
|
||||
}
|
||||
|
||||
protected:
|
||||
SyncCallbackDescImpl(ClassDescriptor& classDescriptor, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, Descriptor::Attributes attributes, Security::Permissions security)
|
||||
:SyncCallbackDesc<Signature>(classDescriptor, name, attributes, security)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::function_traits<Signature>::arity == 3));
|
||||
this->signature.resultType = &Type::singleton<result_type>();
|
||||
this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton<typename boost::function_traits<Signature>::arg1_type>());
|
||||
this->signature.addArgument(RBX::Name::declare(arg2name), Type::singleton<typename boost::function_traits<Signature>::arg2_type>());
|
||||
this->signature.addArgument(RBX::Name::declare(arg3name), Type::singleton<typename boost::function_traits<Signature>::arg3_type>());
|
||||
}
|
||||
public:
|
||||
virtual void setGenericCallback(DescribedBase* object, shared_ptr<SyncCallbackDescriptor::GenericFunction> function) const
|
||||
{
|
||||
this->setCallback(object, boost::bind(callGeneric, function, _1, _2, _3));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class SyncCallbackDescImpl<Signature, 4> : public SyncCallbackDesc<Signature>
|
||||
{
|
||||
typedef typename SyncCallbackDesc<Signature>::result_type result_type;
|
||||
static result_type callGeneric(shared_ptr<SyncCallbackDescriptor::GenericFunction> function,
|
||||
typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4)
|
||||
{
|
||||
shared_ptr<Reflection::Tuple> args(new Tuple());
|
||||
args->values.push_back(arg1);
|
||||
args->values.push_back(arg2);
|
||||
args->values.push_back(arg3);
|
||||
args->values.push_back(arg4);
|
||||
return SyncCallbackDesc<Signature>::template callGeneric<result_type>(function, args);
|
||||
}
|
||||
|
||||
protected:
|
||||
SyncCallbackDescImpl(ClassDescriptor& classDescriptor, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, Descriptor::Attributes attributes, Security::Permissions security)
|
||||
:SyncCallbackDesc<Signature>(classDescriptor, name, attributes, security)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::function_traits<Signature>::arity == 4));
|
||||
this->signature.resultType = &Type::singleton<result_type>();
|
||||
this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton<typename boost::function_traits<Signature>::arg1_type>());
|
||||
this->signature.addArgument(RBX::Name::declare(arg2name), Type::singleton<typename boost::function_traits<Signature>::arg2_type>());
|
||||
this->signature.addArgument(RBX::Name::declare(arg3name), Type::singleton<typename boost::function_traits<Signature>::arg3_type>());
|
||||
this->signature.addArgument(RBX::Name::declare(arg4name), Type::singleton<typename boost::function_traits<Signature>::arg4_type>());
|
||||
}
|
||||
public:
|
||||
virtual void setGenericCallback(DescribedBase* object, shared_ptr<SyncCallbackDescriptor::GenericFunction> function) const
|
||||
{
|
||||
this->setCallback(object, boost::bind(callGeneric, function, _1, _2, _3, _4));
|
||||
}
|
||||
};
|
||||
|
||||
// The fully functional descriptor that binds to class members
|
||||
template<typename Signature>
|
||||
class BoundCallbackDesc : public SyncCallbackDescImpl<Signature, boost::function_traits<Signature>::arity>
|
||||
{
|
||||
typedef typename boost::function<Signature> Function;
|
||||
|
||||
template<class Class>
|
||||
class Setter : public SyncCallbackDesc<Signature>::ISetter
|
||||
{
|
||||
typedef void (Class::*OnChanged)();
|
||||
Function Class::*member;
|
||||
OnChanged onChanged;
|
||||
public:
|
||||
Setter(Function Class::*member, OnChanged onChanged = NULL):member(member),onChanged(onChanged) {}
|
||||
|
||||
virtual void setCallback(DescribedBase* object, const Function& value) const
|
||||
{
|
||||
Class* c = static_cast<Class*>(object);
|
||||
c->*member = value;
|
||||
if (onChanged)
|
||||
(c->*onChanged)();
|
||||
}
|
||||
};
|
||||
public:
|
||||
template<class Class>
|
||||
BoundCallbackDesc(const char* name, Function Class::*member, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
:SyncCallbackDescImpl<Signature, 0>(Class::classDescriptor(), name, attributes, security)
|
||||
{
|
||||
this->setter.reset(new Setter<Class>(member));
|
||||
}
|
||||
|
||||
template<class Class>
|
||||
BoundCallbackDesc(const char* name, Function Class::*member, void (Class::*onChanged)(), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
:SyncCallbackDescImpl<Signature, 0>(Class::classDescriptor(), name, attributes, security)
|
||||
{
|
||||
this->setter.reset(new Setter<Class>(member, onChanged));
|
||||
}
|
||||
|
||||
template<class Class>
|
||||
BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
:SyncCallbackDescImpl<Signature, 1>(Class::classDescriptor(), name, arg1name, attributes, security)
|
||||
{
|
||||
this->setter.reset(new Setter<Class>(member));
|
||||
}
|
||||
|
||||
template<class Class>
|
||||
BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, void (Class::*onChanged)(), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
:SyncCallbackDescImpl<Signature, 1>(Class::classDescriptor(), name, arg1name, attributes, security)
|
||||
{
|
||||
this->setter.reset(new Setter<Class>(member, onChanged));
|
||||
}
|
||||
|
||||
template<class Class>
|
||||
BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
:SyncCallbackDescImpl<Signature, 2>(Class::classDescriptor(), name, arg1name, arg2name, attributes, security)
|
||||
{
|
||||
this->setter.reset(new Setter<Class>(member));
|
||||
}
|
||||
|
||||
template<class Class>
|
||||
BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, void (Class::*onChanged)(), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
:SyncCallbackDescImpl<Signature, 2>(Class::classDescriptor(), name, arg1name, arg2name, attributes, security)
|
||||
{
|
||||
this->setter.reset(new Setter<Class>(member, onChanged));
|
||||
}
|
||||
|
||||
template<class Class>
|
||||
BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, const char* arg3name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
:SyncCallbackDescImpl<Signature, 3>(Class::classDescriptor(), name, arg1name, arg2name, arg3name, attributes, security)
|
||||
{
|
||||
this->setter.reset(new Setter<Class>(member));
|
||||
}
|
||||
|
||||
template<class Class>
|
||||
BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, const char* arg3name, void (Class::*onChanged)(), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
:SyncCallbackDescImpl<Signature, 3>(Class::classDescriptor(), name, arg1name, arg2name, arg3name, attributes, security)
|
||||
{
|
||||
this->setter.reset(new Setter<Class>(member, onChanged));
|
||||
}
|
||||
|
||||
template<class Class>
|
||||
BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
:SyncCallbackDescImpl<Signature, 4>(Class::classDescriptor(), name, arg1name, arg2name, arg3name, arg4name, attributes, security)
|
||||
{
|
||||
this->setter.reset(new Setter<Class>(member));
|
||||
}
|
||||
|
||||
template<class Class>
|
||||
BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, void (Class::*onChanged)(), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
:SyncCallbackDescImpl<Signature, 4>(Class::classDescriptor(), name, arg1name, arg2name, arg3name, arg4name, attributes, security)
|
||||
{
|
||||
this->setter.reset(new Setter<Class>(member, onChanged));
|
||||
}
|
||||
};
|
||||
|
||||
template <class Class, typename Signature, int arity = boost::function_traits<Signature>::arity>
|
||||
class BoundAsyncCallbackDesc;
|
||||
|
||||
template <class Class, typename Signature>
|
||||
class BoundAsyncCallbackDesc<Class, Signature, 0> : public AsyncCallbackDescriptor
|
||||
{
|
||||
typedef boost::function<void(AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction)> Function;
|
||||
|
||||
static void callGeneric(shared_ptr<AsyncCallbackDescriptor::GenericFunction> function,
|
||||
AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction)
|
||||
{
|
||||
shared_ptr<Reflection::Tuple> args(new Tuple());
|
||||
callGenericImpl(function, args, resumeFunction, errorFunction);
|
||||
}
|
||||
|
||||
void declareSignature()
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::function_traits<Signature>::arity == 0));
|
||||
this->signature.resultType = &Type::singleton<typename boost::function_traits<Signature>::result_type>();
|
||||
}
|
||||
|
||||
Function Class::*member;
|
||||
void (Class::*onChanged)(const Function&);
|
||||
|
||||
public:
|
||||
BoundAsyncCallbackDesc(const char* name, Function Class::*member, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
: AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security)
|
||||
, member(member)
|
||||
, onChanged(NULL)
|
||||
{
|
||||
declareSignature();
|
||||
}
|
||||
|
||||
BoundAsyncCallbackDesc(const char* name, Function Class::*member, void (Class::*onChanged)(const Function&), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
: AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security)
|
||||
, member(member)
|
||||
, onChanged(onChanged)
|
||||
{
|
||||
declareSignature();
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void setGenericCallback(DescribedBase* object, shared_ptr<AsyncCallbackDescriptor::GenericFunction> function) const
|
||||
{
|
||||
setGenericCallbackImpl(object, member, onChanged, boost::bind(callGeneric, function, _1, _2));
|
||||
}
|
||||
|
||||
virtual void clearCallback(DescribedBase* object) const
|
||||
{
|
||||
setGenericCallbackImpl(object, member, onChanged, Function());
|
||||
}
|
||||
};
|
||||
|
||||
template <class Class, typename Signature>
|
||||
class BoundAsyncCallbackDesc<Class, Signature, 1> : public AsyncCallbackDescriptor
|
||||
{
|
||||
typedef typename boost::function_traits<Signature>::arg1_type Arg1;
|
||||
typedef boost::function<void(Arg1, AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction)> Function;
|
||||
|
||||
static void callGeneric(shared_ptr<AsyncCallbackDescriptor::GenericFunction> function,
|
||||
Arg1 arg1,
|
||||
AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction)
|
||||
{
|
||||
shared_ptr<Reflection::Tuple> args(new Tuple());
|
||||
args->values.push_back(arg1);
|
||||
callGenericImpl(function, args, resumeFunction, errorFunction);
|
||||
}
|
||||
|
||||
void declareSignature(const char* arg1name)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::function_traits<Signature>::arity == 1));
|
||||
this->signature.resultType = &Type::singleton<typename boost::function_traits<Signature>::result_type>();
|
||||
this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton<typename boost::function_traits<Signature>::arg1_type>());
|
||||
}
|
||||
|
||||
Function Class::*member;
|
||||
void (Class::*onChanged)(const Function&);
|
||||
|
||||
public:
|
||||
BoundAsyncCallbackDesc(const char* name, Function Class::*member, const char* arg1name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
: AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security)
|
||||
, member(member)
|
||||
, onChanged(NULL)
|
||||
{
|
||||
declareSignature(arg1name);
|
||||
}
|
||||
|
||||
BoundAsyncCallbackDesc(const char* name, Function Class::*member, const char* arg1name, void (Class::*onChanged)(const Function&), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
: AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security)
|
||||
, member(member)
|
||||
, onChanged(onChanged)
|
||||
{
|
||||
declareSignature(arg1name);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void setGenericCallback(DescribedBase* object, shared_ptr<AsyncCallbackDescriptor::GenericFunction> function) const
|
||||
{
|
||||
setGenericCallbackImpl(object, member, onChanged, boost::bind(callGeneric, function, _1, _2, _3));
|
||||
}
|
||||
|
||||
virtual void clearCallback(DescribedBase* object) const
|
||||
{
|
||||
setGenericCallbackImpl(object, member, onChanged, Function());
|
||||
}
|
||||
};
|
||||
|
||||
template <class Class, typename Signature>
|
||||
class BoundAsyncCallbackDesc<Class, Signature, 2> : public AsyncCallbackDescriptor
|
||||
{
|
||||
typedef typename boost::function_traits<Signature>::arg1_type Arg1;
|
||||
typedef typename boost::function_traits<Signature>::arg2_type Arg2;
|
||||
typedef boost::function<void(Arg1, Arg2, AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction)> Function;
|
||||
|
||||
static void callGeneric(shared_ptr<AsyncCallbackDescriptor::GenericFunction> function,
|
||||
Arg1 arg1,
|
||||
Arg2 arg2,
|
||||
AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction)
|
||||
{
|
||||
shared_ptr<Reflection::Tuple> args(new Tuple());
|
||||
args->values.push_back(arg1);
|
||||
args->values.push_back(arg2);
|
||||
callGenericImpl(function, args, resumeFunction, errorFunction);
|
||||
}
|
||||
|
||||
void declareSignature(const char* arg1name, const char* arg2name)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::function_traits<Signature>::arity == 2));
|
||||
this->signature.resultType = &Type::singleton<typename boost::function_traits<Signature>::result_type>();
|
||||
this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton<typename boost::function_traits<Signature>::arg1_type>());
|
||||
this->signature.addArgument(RBX::Name::declare(arg2name), Type::singleton<typename boost::function_traits<Signature>::arg2_type>());
|
||||
}
|
||||
|
||||
Function Class::*member;
|
||||
void (Class::*onChanged)(const Function&);
|
||||
|
||||
public:
|
||||
BoundAsyncCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
: AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security)
|
||||
, member(member)
|
||||
, onChanged(NULL)
|
||||
{
|
||||
declareSignature(arg1name, arg2name);
|
||||
}
|
||||
|
||||
BoundAsyncCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, void (Class::*onChanged)(const Function&), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
: AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security)
|
||||
, member(member)
|
||||
, onChanged(onChanged)
|
||||
{
|
||||
declareSignature(arg1name, arg2name);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void setGenericCallback(DescribedBase* object, shared_ptr<AsyncCallbackDescriptor::GenericFunction> function) const
|
||||
{
|
||||
setGenericCallbackImpl(object, member, onChanged, boost::bind(callGeneric, function, _1, _2, _3, _4));
|
||||
}
|
||||
|
||||
virtual void clearCallback(DescribedBase* object) const
|
||||
{
|
||||
setGenericCallbackImpl(object, member, onChanged, Function());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/Name.h"
|
||||
#include "boost/utility.hpp"
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Reflection
|
||||
{
|
||||
class Descriptor : public boost::noncopyable
|
||||
{
|
||||
|
||||
static void checkLockedDown()
|
||||
{
|
||||
// If this following assertion fails then you need to put your class
|
||||
// into FactoryRegistrator::FactoryRegistrator() or somewhere else.
|
||||
// Failure of this test is so severe that we want to catch it in production, too.
|
||||
if (lockedDown)
|
||||
RBXCRASH();
|
||||
}
|
||||
public:
|
||||
struct Attributes
|
||||
{
|
||||
bool isDeprecated;
|
||||
const Descriptor* preferred; // used if isDeprecated
|
||||
Attributes()
|
||||
:isDeprecated(false)
|
||||
,preferred(NULL)
|
||||
{}
|
||||
static Attributes deprecated(const Descriptor& preferred)
|
||||
{
|
||||
Attributes result;
|
||||
result.isDeprecated = true;
|
||||
result.preferred = &preferred;
|
||||
return result;
|
||||
}
|
||||
static Attributes deprecated()
|
||||
{
|
||||
Attributes result;
|
||||
result.isDeprecated = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static bool lockedDown; // After the first instance of a described class is created we cannot modify the reflection database
|
||||
|
||||
const RBX::Name& name;
|
||||
scoped_ptr<bool> isReplicable;
|
||||
scoped_ptr<bool> isOutdated;
|
||||
const Attributes attributes;
|
||||
|
||||
Descriptor(const char* name, Attributes attributes)
|
||||
:name(RBX::Name::declare(name))
|
||||
,attributes(attributes)
|
||||
,isReplicable(new bool(false))
|
||||
,isOutdated(new bool(false))
|
||||
{
|
||||
checkLockedDown();
|
||||
RBXASSERT(!this->name.empty());
|
||||
}
|
||||
Descriptor(const RBX::Name& name, Attributes attributes)
|
||||
:name(name)
|
||||
,attributes(attributes)
|
||||
,isReplicable(new bool(false))
|
||||
,isOutdated(new bool(false))
|
||||
{
|
||||
checkLockedDown();
|
||||
RBXASSERT(!this->name.empty());
|
||||
}
|
||||
virtual ~Descriptor() {}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
#pragma once
|
||||
|
||||
#include "reflection/Type.h"
|
||||
#include "util/utilities.h"
|
||||
#include "util/math.h"
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/thread/once.hpp>
|
||||
|
||||
namespace RBX {
|
||||
|
||||
|
||||
namespace Reflection {
|
||||
|
||||
class EnumDescriptor : public Type
|
||||
{
|
||||
public:
|
||||
static std::vector< const EnumDescriptor* >::const_iterator enumsBegin();
|
||||
static std::vector< const EnumDescriptor* >::const_iterator enumsEnd();
|
||||
static size_t allEnumSize() {return allEnums().size();}
|
||||
|
||||
class Item : public Descriptor
|
||||
{
|
||||
public:
|
||||
const EnumDescriptor& owner;
|
||||
const int value; // value of the enum
|
||||
const size_t index; // place in ordered enum values (0<=index<enumCount)
|
||||
Item(const char* name, Descriptor::Attributes attributes, int value, size_t index, const EnumDescriptor& owner)
|
||||
:Descriptor(name, attributes)
|
||||
,value(value)
|
||||
,index(index)
|
||||
,owner(owner)
|
||||
{
|
||||
}
|
||||
bool convertToValue(Variant& value) const
|
||||
{
|
||||
return owner.convertToValue(index, value);
|
||||
}
|
||||
bool convertToString(std::string& value) const
|
||||
{
|
||||
return owner.convertToString(index, value);
|
||||
}
|
||||
};
|
||||
|
||||
#if 0
|
||||
typedef boost::unordered_map<const RBX::Name*, const EnumDescriptor*> EnumNameTable;
|
||||
#else
|
||||
typedef std::map<const RBX::Name*, const EnumDescriptor*> EnumNameTable;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static EnumNameTable& allEnumsNameLookup();
|
||||
static std::vector<const EnumDescriptor*>& allEnums();
|
||||
|
||||
static bool equalValue(const Item* item, int intValue)
|
||||
{
|
||||
return item->value == intValue;
|
||||
}
|
||||
|
||||
static int count;
|
||||
protected:
|
||||
std::vector< const Item* > allItems;
|
||||
size_t enumCount;
|
||||
size_t enumCountMSB;
|
||||
EnumDescriptor(const char* typeName);
|
||||
~EnumDescriptor();
|
||||
public:
|
||||
size_t getEnumCount() const { return enumCount; }
|
||||
size_t getEnumCountMSB() const { return enumCountMSB; }
|
||||
std::vector< const Item* >::const_iterator begin() const {
|
||||
return allItems.begin();
|
||||
}
|
||||
std::vector< const Item* >::const_iterator end() const {
|
||||
return allItems.end();
|
||||
}
|
||||
static const EnumDescriptor* lookupDescriptor(const RBX::Name& name) {
|
||||
EnumNameTable::const_iterator iter = allEnumsNameLookup().find(&name);
|
||||
if (iter!=allEnumsNameLookup().end())
|
||||
return iter->second;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
static const EnumDescriptor* lookupDescriptor(const Type& type) {
|
||||
if (type.isEnum)
|
||||
return static_cast<const EnumDescriptor*>(&type);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool isValue(int intValue) const {
|
||||
return std::find_if(allItems.begin(), allItems.end(), boost::bind(&equalValue, _1, intValue)) != allItems.end();
|
||||
}
|
||||
virtual const Item* lookup(const char* text) const = 0;
|
||||
virtual const Item* lookup(const Variant& value) const = 0;
|
||||
virtual bool convertToValue(size_t index, Variant& value) const = 0;
|
||||
virtual bool convertToString(size_t index, std::string& value) const = 0;
|
||||
};
|
||||
|
||||
// A thread-safe singleton!
|
||||
template<typename T>
|
||||
class Singleton : boost::noncopyable
|
||||
{
|
||||
static T& doGetSingleton()
|
||||
{
|
||||
static T s;
|
||||
return s;
|
||||
}
|
||||
static void initSingleton()
|
||||
{
|
||||
doGetSingleton();
|
||||
}
|
||||
public:
|
||||
static T& singleton()
|
||||
{
|
||||
static boost::once_flag flag = BOOST_ONCE_INIT;
|
||||
boost::call_once(&initSingleton, flag);
|
||||
return doGetSingleton();
|
||||
};
|
||||
};
|
||||
|
||||
template<typename Enum> class EnumRegistrar;
|
||||
|
||||
template<typename Enum>
|
||||
class EnumDesc : public EnumDescriptor
|
||||
{
|
||||
public:
|
||||
friend class Singleton<const EnumDesc<Enum> >;
|
||||
static const EnumDesc& singleton()
|
||||
{
|
||||
return Singleton<const EnumDesc<Enum> >::singleton();
|
||||
}
|
||||
|
||||
private:
|
||||
// You must implement the following constructor for each EnumDesc that you define
|
||||
EnumDesc();
|
||||
~EnumDesc()
|
||||
{
|
||||
// Force linking of EnumRegistrar<Enum>, which will force clients
|
||||
// of this library to define EnumRegistrar<Enum>::registrar in
|
||||
// their startup code.
|
||||
|
||||
|
||||
Reflection::EnumRegistrar<Enum>::registrar.dummy();
|
||||
|
||||
std::for_each(allItems.begin(), allItems.end(), &del_fun<const Item>);
|
||||
}
|
||||
|
||||
std::map<const RBX::Name*, Enum> nameToEnum;
|
||||
std::map<const RBX::Name*, Enum> nameToEnumLegacy;
|
||||
std::vector< const RBX::Name* > enumToName; // maps enum to Name (there may be gaps)
|
||||
|
||||
std::vector< std::string > enumToString; // maps enum to String (there may be gaps)
|
||||
std::vector< const Item* > enumToItem; // maps enum to Item (there may be gaps)
|
||||
|
||||
std::vector< Enum > intToEnum; // maps legacy values to proper enum
|
||||
std::vector< Enum > indexToEnum;
|
||||
std::vector< size_t > enumToIndex; // maps enum to Index (there may be gaps)
|
||||
|
||||
|
||||
// Used in constructor
|
||||
void addPair(Enum value, const char* name, Descriptor::Attributes attributes = Descriptor::Attributes())
|
||||
{
|
||||
RBXASSERT_VERY_FAST(value >= 0);
|
||||
// No spaces in enums:
|
||||
RBXASSERT_VERY_FAST(std::string(name).find(' ') == std::string::npos);
|
||||
// No no CamelCase in enums:
|
||||
RBXASSERT_VERY_FAST(!isCamel(name));
|
||||
|
||||
const Item* item = new Item(name, attributes, value, enumCount, *this);
|
||||
|
||||
allItems.push_back(item);
|
||||
|
||||
if (intToEnum.size()<=(size_t)value)
|
||||
intToEnum.resize(value+1, (Enum)-1);
|
||||
intToEnum[value] = value;
|
||||
|
||||
RBXASSERT(value>=0);
|
||||
|
||||
if (enumToIndex.size()<=(size_t)value)
|
||||
enumToIndex.resize(value+1, -1);
|
||||
enumToIndex[value] = enumCount;
|
||||
indexToEnum.push_back(value);
|
||||
|
||||
if (enumToName.size()<=(size_t)value)
|
||||
enumToName.resize(value+1, &RBX::Name::getNullName());
|
||||
enumToName[value] = &item->name;
|
||||
|
||||
if (enumToString.size()<=(size_t)value)
|
||||
enumToString.resize(value+1);
|
||||
enumToString[value] = name;
|
||||
|
||||
if (enumToItem.size()<=(size_t)value)
|
||||
enumToItem.resize(value+1);
|
||||
enumToItem[value] = item;
|
||||
|
||||
nameToEnum[&item->name] = value;
|
||||
|
||||
enumCount++;
|
||||
enumCountMSB = Math::computeMSB(enumCount);
|
||||
}
|
||||
void addLegacy(int oldValue, const char* name, Enum value)
|
||||
{
|
||||
RBXASSERT_VERY_FAST(value >= 0);
|
||||
|
||||
if (intToEnum.size()<=(size_t)oldValue)
|
||||
intToEnum.resize(oldValue+1, (Enum)-1);
|
||||
intToEnum[oldValue] = value;
|
||||
nameToEnumLegacy[&RBX::Name::declare(name)] = value;
|
||||
}
|
||||
void addLegacyName(const char* name, Enum value)
|
||||
{
|
||||
nameToEnumLegacy[&RBX::Name::declare(name)] = value;
|
||||
}
|
||||
public:
|
||||
const RBX::Name& convertToName(const Enum& value) const
|
||||
{
|
||||
RBXASSERT(value>=0);
|
||||
RBXASSERT(value<enumToItem.size());
|
||||
if (value<0)
|
||||
return RBX::Name::getNullName();
|
||||
if ((size_t)value>=enumToName.size())
|
||||
return RBX::Name::getNullName();
|
||||
|
||||
return *enumToName[value];
|
||||
}
|
||||
std::string convertToString(const Enum& value) const
|
||||
{
|
||||
RBXASSERT(value>=0);
|
||||
RBXASSERT((size_t)value<enumToItem.size());
|
||||
if (value<0)
|
||||
return "";
|
||||
if ((size_t)value>=enumToString.size())
|
||||
return "";
|
||||
|
||||
return enumToString[value];
|
||||
}
|
||||
const Item* convertToItem(const Enum& value) const
|
||||
{
|
||||
RBXASSERT(value>=0);
|
||||
RBXASSERT((size_t)value<enumToItem.size());
|
||||
if (value<0)
|
||||
return NULL;
|
||||
if ((size_t)value>=enumToItem.size())
|
||||
return NULL;
|
||||
|
||||
return enumToItem[value];
|
||||
}
|
||||
|
||||
bool mapIntValue(int intValue, Enum& value) const
|
||||
{
|
||||
if (intValue < 0)
|
||||
return false;
|
||||
|
||||
if ((size_t)intValue >= intToEnum.size())
|
||||
return false;
|
||||
|
||||
value = intToEnum[intValue];
|
||||
if ((int)value == -1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool convertToValue(const RBX::Name& name, Enum& value) const
|
||||
{
|
||||
typename std::map<const RBX::Name*, Enum>::const_iterator iter = nameToEnum.find(&name);
|
||||
if (iter!=nameToEnum.end()) {
|
||||
value = iter->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
iter = nameToEnumLegacy.find(&name);
|
||||
if (iter!=nameToEnumLegacy.end()) {
|
||||
value = iter->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/*implement*/ const Item* lookup(const char* text) const
|
||||
{
|
||||
Enum e;
|
||||
if (convertToValue(RBX::Name::lookup(text), e))
|
||||
return convertToItem(e);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
/*implement*/ const Item* lookup(const Variant& value) const
|
||||
{
|
||||
return convertToItem(value.cast<Enum>());
|
||||
}
|
||||
|
||||
/*implement*/ bool convertToValue(size_t index, Variant& value) const
|
||||
{
|
||||
Enum enumValue;
|
||||
bool result = convertToValue(index, enumValue);
|
||||
value = enumValue;
|
||||
return result;
|
||||
}
|
||||
/*implement*/ bool convertToString(size_t index, std::string& stringValue) const
|
||||
{
|
||||
Enum enumValue;
|
||||
if(convertToValue(index, enumValue)){
|
||||
stringValue = convertToString(enumValue);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool convertToValue(const char* text, Enum& value) const
|
||||
{
|
||||
return convertToValue(RBX::Name::lookup(text), value);
|
||||
}
|
||||
size_t convertToIndex(Enum value) const
|
||||
{
|
||||
RBXASSERT(value>=0);
|
||||
if ((size_t)value<enumToIndex.size())
|
||||
return enumToIndex[value];
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
bool convertToValue(size_t index, Enum& value) const
|
||||
{
|
||||
if (index<enumCount) {
|
||||
value = indexToEnum[index];
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Helper macro
|
||||
// GCC does not generate the registrar variable defination & fails at Link Time. Force Construct by passing in an dummy arg to ctor. That works. WEIRD huh?
|
||||
#define RBX_REGISTER_ENUM(Enum) namespace RBX { namespace Reflection { \
|
||||
template<> \
|
||||
const Type& Type::getSingleton<Enum>() \
|
||||
{ \
|
||||
return EnumDesc<Enum>::singleton(); \
|
||||
} \
|
||||
template<> EnumRegistrar<Enum> EnumRegistrar<Enum>::registrar(0); \
|
||||
template<> TypeRegistrar<Enum> TypeRegistrar<Enum>::registrar(0); \
|
||||
}}
|
||||
|
||||
// This class is intended to prevent clients of the library
|
||||
// from forgetting to initialize the enum descriptor
|
||||
template<typename Enum>
|
||||
class EnumRegistrar : boost::noncopyable
|
||||
{
|
||||
int x;
|
||||
|
||||
//// GCC does not generate the registrar variable defination & fails at Link Time. Force Construct by passing in an dummy arg to ctor. That works. WEIRD huh?
|
||||
EnumRegistrar(int i):x(i)
|
||||
{
|
||||
// This call registers the enum descriptor
|
||||
// in the reflection database
|
||||
EnumDesc<Enum>::singleton();
|
||||
}
|
||||
public:
|
||||
void dummy()
|
||||
{
|
||||
x++;
|
||||
}
|
||||
|
||||
// The instantiation of this static member must be in a unit
|
||||
// that is initialized in the main thread before any objects
|
||||
// are created. Otherwise the reflection database
|
||||
// can change at runtime, which would be a disaster
|
||||
static EnumRegistrar registrar;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "reflection/type.h"
|
||||
#include "security/securitycontext.h"
|
||||
#include "reflection/member.h"
|
||||
#include "util/G3DCore.h"
|
||||
#include "util/Region3.h"
|
||||
#include "util/Region3Int16.h"
|
||||
|
||||
struct lua_State;
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Reflection
|
||||
{
|
||||
class Function;
|
||||
class EnumDescriptor;
|
||||
|
||||
// Base that describes a Function
|
||||
class RBXBaseClass FunctionDescriptor : public MemberDescriptor
|
||||
{
|
||||
public:
|
||||
enum Kind
|
||||
{
|
||||
Kind_Default,
|
||||
Kind_Custom
|
||||
};
|
||||
|
||||
class RBXInterface Arguments
|
||||
{
|
||||
public:
|
||||
Variant returnValue;
|
||||
|
||||
virtual size_t size() const = 0;
|
||||
// Place the value for the requested parameter in "value".
|
||||
//
|
||||
// index: 1-based index into the argument list
|
||||
// returns: true if the index contains a valid argument
|
||||
// value: the value to set. the function returns false then value is unchanged
|
||||
virtual bool getVariant(int index, Variant& value) const = 0;
|
||||
virtual bool getBool(int index, bool& value) const = 0;
|
||||
virtual bool getLong(int index, long& value) const = 0;
|
||||
virtual bool getDouble(int index, double& value) const = 0;
|
||||
virtual bool getString(int index, std::string& value) const = 0;
|
||||
virtual bool getVector3int16(int index, Vector3int16& value) const = 0;
|
||||
virtual bool getRegion3int16(int index, Region3int16& value) const = 0;
|
||||
virtual bool getVector3(int index, Vector3& value) const = 0;
|
||||
virtual bool getRegion3(int index, Region3& value) const = 0;
|
||||
virtual bool getRect(int index, Rect2D& value) const = 0;
|
||||
virtual bool getObject(int index, shared_ptr<DescribedBase>& value) const = 0;
|
||||
virtual bool getEnum(int index, const EnumDescriptor& desc, int& value) const = 0;
|
||||
};
|
||||
typedef Function ConstMember;
|
||||
typedef Function Member;
|
||||
|
||||
protected:
|
||||
SignatureDescriptor signature;
|
||||
Kind kind;
|
||||
FunctionDescriptor(ClassDescriptor& classDescriptor, const char* name, Security::Permissions security, Attributes attributes);
|
||||
|
||||
public:
|
||||
const SignatureDescriptor& getSignature() const { return signature; }
|
||||
|
||||
Kind getKind() const { return kind; }
|
||||
|
||||
virtual int executeCustom(DescribedBase* instance, lua_State*) const { return 0; }
|
||||
|
||||
virtual void execute(DescribedBase* instance, Arguments& arguments) const = 0;
|
||||
};
|
||||
|
||||
|
||||
// A light-weight convenience class that associates a FunctionDescriptor
|
||||
// with a described object to create a "Function"
|
||||
class Function
|
||||
{
|
||||
protected:
|
||||
const FunctionDescriptor* descriptor;
|
||||
DescribedBase* instance;
|
||||
public:
|
||||
inline Function(const FunctionDescriptor& descriptor, DescribedBase* instance)
|
||||
:descriptor(&descriptor),instance(instance)
|
||||
{}
|
||||
|
||||
inline Function(const Function& other)
|
||||
:descriptor(other.descriptor),instance(other.instance)
|
||||
{}
|
||||
inline Function& operator =(const Function& other)
|
||||
{
|
||||
this->descriptor = other.descriptor;
|
||||
this->instance = other.instance;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const RBX::Name& getName() const {
|
||||
return descriptor->name;
|
||||
}
|
||||
|
||||
inline const FunctionDescriptor* getDescriptor() const {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
void execute(FunctionDescriptor::Arguments& arguments) const {
|
||||
return descriptor->execute(const_cast<DescribedBase*>(instance), arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "reflection/Property.h"
|
||||
#include "reflection/Function.h"
|
||||
#include "reflection/YieldFunction.h"
|
||||
#include "Reflection/Event.h"
|
||||
#include "reflection/Callback.h"
|
||||
|
||||
#include <vector>
|
||||
#include "boost/crc.hpp"
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Reflection
|
||||
{
|
||||
enum ReplicationLevel{
|
||||
NEVER_REPLICATE = 0, //Never replicate this object
|
||||
STANDARD_REPLICATE= 1, //Replicate to/from server according to standard rules
|
||||
PLAYER_REPLICATE = 2, //Replicate changes to/from the "player" who owns this object
|
||||
} ;
|
||||
|
||||
class ClassDescriptor
|
||||
: public Descriptor
|
||||
, public MemberDescriptorContainer<PropertyDescriptor>
|
||||
, public MemberDescriptorContainer<EventDescriptor>
|
||||
, public MemberDescriptorContainer<FunctionDescriptor>
|
||||
, public MemberDescriptorContainer<YieldFunctionDescriptor>
|
||||
, public MemberDescriptorContainer<CallbackDescriptor>
|
||||
{
|
||||
public:
|
||||
typedef std::vector<ClassDescriptor*> ClassDescriptors;
|
||||
|
||||
enum Functionality{
|
||||
PERSISTENT = 0x1 + 0x2 + 0x8 + 0x10, // isPublic, Replicate, canXmlWrite, isScriptable
|
||||
PERSISTENT_PLAYER = 0x1 + 0x4 + 0x8 + 0x10, // isPublic, ReplicatePlayer, canXmlWrite, isScriptable
|
||||
PERSISTENT_LOCAL = 0x1 + 0x0 + 0x8 + 0x10, // isPublic, canXmlWrite, isScriptable
|
||||
RUNTIME = 0x1 + 0x2 + 0x0 + 0x10, // isPublic, Replicate, isScriptable
|
||||
RUNTIME_PLAYER = 0x1 + 0x4 + 0x0 + 0x10, // isPublic, ReplicatePlayer, isScriptable
|
||||
RUNTIME_LOCAL = 0x1 + 0x0 + 0x0 + 0x10, // isPublic, isScriptable
|
||||
INTERNAL = 0x1 + 0x2 + 0x0 + 0x0, // isPublic, Replicate
|
||||
INTERNAL_PLAYER = 0x1 + 0x4 + 0x0 + 0x0, // isPublic, ReplicatePlayer,
|
||||
INTERNAL_LOCAL = 0x1 + 0x0 + 0x0 + 0x0, // isPublic
|
||||
PERSISTENT_HIDDEN = 0x1 + 0x2 + 0x8 + 0x0, // isPublic, Replicate, canXmlWrite
|
||||
PERSISTENT_LOCAL_INTERNAL = 0x1 + 0x0 + 0x8 + 0x0, // isPublic, canXmlWrite
|
||||
};
|
||||
|
||||
struct Attributes : public Descriptor::Attributes
|
||||
{
|
||||
Functionality flags;
|
||||
|
||||
Attributes(Functionality flags):flags(flags) {}
|
||||
static Attributes deprecated(Functionality flags, const ClassDescriptor* preferred);
|
||||
};
|
||||
|
||||
const Security::Permissions security;
|
||||
|
||||
private:
|
||||
ClassDescriptor();
|
||||
static ClassDescriptors& allClasses();
|
||||
|
||||
ClassDescriptors derivedClasses;
|
||||
ClassDescriptor* const base;
|
||||
const unsigned bReplicateType : 2;
|
||||
const unsigned bCanXmlWrite : 1;
|
||||
const unsigned bIsScriptable : 1;
|
||||
|
||||
static int count;
|
||||
|
||||
public:
|
||||
ClassDescriptor(ClassDescriptor& base, const char* name, Attributes attributes, Security::Permissions security);
|
||||
~ClassDescriptor() { count--; }
|
||||
|
||||
const ClassDescriptor* getBase() const { return base; }
|
||||
|
||||
bool isBaseOf(const ClassDescriptor& child) const;
|
||||
bool isA(const ClassDescriptor& test) const;
|
||||
|
||||
bool isBaseOf(const char* childName) const;
|
||||
bool isA(const char* testName) const;
|
||||
|
||||
inline ReplicationLevel getReplicationLevel() const { return (ReplicationLevel)bReplicateType; }
|
||||
inline bool isScriptCreatable() const { return bIsScriptable != 0; }
|
||||
inline bool isSerializable() const { return bCanXmlWrite != 0; }
|
||||
|
||||
// The root ClassDescriptor of all other Descriptors
|
||||
static ClassDescriptor& rootDescriptor()
|
||||
{
|
||||
static ClassDescriptor root;
|
||||
return root;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Class enumeration
|
||||
static ClassDescriptors::const_iterator all_begin() {
|
||||
return allClasses().begin();
|
||||
}
|
||||
static ClassDescriptors::const_iterator all_end() {
|
||||
return allClasses().end();
|
||||
}
|
||||
static size_t all_size()
|
||||
{
|
||||
return allClasses().size();
|
||||
}
|
||||
static unsigned int checksum();
|
||||
static unsigned int checksum(const PropertyDescriptor* t, boost::crc_32_type& result);
|
||||
static unsigned int checksum(const EventDescriptor* t, boost::crc_32_type& result);
|
||||
static unsigned int checksum(const ClassDescriptor* t, boost::crc_32_type& result);
|
||||
static unsigned int checksum(const Type* t, boost::crc_32_type& result);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Derived Class enumeration
|
||||
ClassDescriptors::const_iterator derivedClasses_begin() const {
|
||||
return derivedClasses.begin();
|
||||
}
|
||||
ClassDescriptors::const_iterator derivedClasses_end() const {
|
||||
return derivedClasses.end();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Convenience functions for enumerating and querying members of a Class
|
||||
PropertyDescriptor* findPropertyDescriptor(const char* name) const
|
||||
{
|
||||
return MemberDescriptorContainer<PropertyDescriptor>::findDescriptor(name);
|
||||
}
|
||||
FunctionDescriptor* findFunctionDescriptor(const char* name) const
|
||||
{
|
||||
return MemberDescriptorContainer<FunctionDescriptor>::findDescriptor(name);
|
||||
}
|
||||
YieldFunctionDescriptor* findYieldFunctionDescriptor(const char* name) const
|
||||
{
|
||||
return MemberDescriptorContainer<YieldFunctionDescriptor>::findDescriptor(name);
|
||||
}
|
||||
EventDescriptor* findEventDescriptor(const char* name) const
|
||||
{
|
||||
return MemberDescriptorContainer<EventDescriptor>::findDescriptor(name);
|
||||
}
|
||||
CallbackDescriptor* findCallbackDescriptor(const char* name) const
|
||||
{
|
||||
return MemberDescriptorContainer<CallbackDescriptor>::findDescriptor(name);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename MemberDescriptorContainer<T>::Collection::const_iterator begin() const {
|
||||
return MemberDescriptorContainer<T>::descriptors_begin();
|
||||
}
|
||||
template<class T>
|
||||
typename MemberDescriptorContainer<T>::Collection::const_iterator end() const {
|
||||
return MemberDescriptorContainer<T>::descriptors_end();
|
||||
}
|
||||
|
||||
bool operator==(const ClassDescriptor& other) const;
|
||||
bool operator!=(const ClassDescriptor& other) const;
|
||||
};
|
||||
|
||||
// Convenience typedefs:
|
||||
typedef MemberDescriptorContainer<PropertyDescriptor>::ConstIterator ConstPropertyIterator;
|
||||
typedef MemberDescriptorContainer<PropertyDescriptor>::Iterator PropertyIterator;
|
||||
typedef MemberDescriptorContainer<FunctionDescriptor>::ConstIterator FunctionIterator;
|
||||
typedef MemberDescriptorContainer<YieldFunctionDescriptor>::ConstIterator YieldFunctionIterator;
|
||||
typedef MemberDescriptorContainer<EventDescriptor>::ConstIterator ConstSignalIterator;
|
||||
typedef MemberDescriptorContainer<EventDescriptor>::Iterator SignalIterator;
|
||||
typedef MemberDescriptorContainer<CallbackDescriptor>::Iterator CallbackIterator;
|
||||
|
||||
// The base class of any class that supports Reflection
|
||||
class RBXBaseClass DescribedBase
|
||||
: public EventSource
|
||||
, public boost::enable_shared_from_this<DescribedBase>
|
||||
{
|
||||
protected:
|
||||
// Each instance has a reference to it's most-specific ClassDescriptor:
|
||||
const ClassDescriptor* descriptor;
|
||||
boost::scoped_ptr<std::string> xmlId;
|
||||
|
||||
public:
|
||||
// The ClassDescriptor for this base class
|
||||
static ClassDescriptor& classDescriptor()
|
||||
{
|
||||
return ClassDescriptor::rootDescriptor();
|
||||
}
|
||||
|
||||
DescribedBase()
|
||||
{
|
||||
Descriptor::lockedDown = true; // See Descriptor::checkLockedDown() for an explanation
|
||||
|
||||
// By default, each DescribedBase has a null ClassDescriptor
|
||||
this->descriptor = &classDescriptor();
|
||||
}
|
||||
|
||||
virtual ~DescribedBase()
|
||||
{
|
||||
}
|
||||
|
||||
inline const ClassDescriptor& getDescriptor() const { return *descriptor; };
|
||||
|
||||
template<class T>
|
||||
inline bool isA() const
|
||||
{
|
||||
return getDescriptor().isA(T::classDescriptor());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static inline bool isA(const DescribedBase* instance)
|
||||
{
|
||||
return instance ? instance->getDescriptor().isA(T::classDescriptor()) : false;
|
||||
}
|
||||
|
||||
// This function is slower than the others
|
||||
bool isA(std::string className)
|
||||
{
|
||||
return getDescriptor().isA(className.c_str());
|
||||
}
|
||||
|
||||
// Regular dynamic_casts are very slow. These faster versions uses our reflection framework to determine type then uses static_cast for speed.
|
||||
// Use these functions to replace dynamic_casts for classes that derives from DescribedCreatable or DescribedNonCreatable.
|
||||
template<class T>
|
||||
inline T* fastDynamicCast()
|
||||
{
|
||||
return (getDescriptor().isA(T::classDescriptor())) ? static_cast<T*>(this) : NULL;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline const T* fastDynamicCast() const
|
||||
{
|
||||
return (getDescriptor().isA(T::classDescriptor())) ? static_cast<const T*>(this) : NULL;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static inline T* fastDynamicCast(DescribedBase* instance)
|
||||
{
|
||||
return (instance && instance->getDescriptor().isA(T::classDescriptor())) ? static_cast<T*>(instance) : NULL;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static inline const T* fastDynamicCast(const DescribedBase* instance)
|
||||
{
|
||||
return (instance && instance->getDescriptor().isA(T::classDescriptor())) ? static_cast<const T*>(instance) : NULL;
|
||||
}
|
||||
|
||||
// This function replaces shared_dynamic_cast for classes that derives from DescribedCreatable or DescribedNonCreatable.
|
||||
template<class T, class U>
|
||||
static inline shared_ptr<T> fastSharedDynamicCast(const shared_ptr<U>& instance)
|
||||
{
|
||||
return isA<T>(instance.get()) ? shared_static_cast<T>(instance) : shared_ptr<T>();
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Convenience functions for getting members of a described object
|
||||
PropertyDescriptor* findPropertyDescriptor(const char* name)
|
||||
{
|
||||
return getDescriptor().MemberDescriptorContainer<PropertyDescriptor>::findDescriptor(name);
|
||||
}
|
||||
ConstPropertyIterator properties_begin() const {
|
||||
return getDescriptor().MemberDescriptorContainer<PropertyDescriptor>::members_begin(this);
|
||||
}
|
||||
ConstPropertyIterator properties_end() const {
|
||||
return getDescriptor().MemberDescriptorContainer<PropertyDescriptor>::members_end(this);
|
||||
}
|
||||
PropertyIterator properties_begin() {
|
||||
return getDescriptor().MemberDescriptorContainer<PropertyDescriptor>::members_begin(this);
|
||||
}
|
||||
PropertyIterator properties_end() {
|
||||
return getDescriptor().MemberDescriptorContainer<PropertyDescriptor>::members_end(this);
|
||||
}
|
||||
|
||||
FunctionDescriptor* findFunctionDescriptor(const char* name)
|
||||
{
|
||||
return getDescriptor().MemberDescriptorContainer<FunctionDescriptor>::findDescriptor(name);
|
||||
}
|
||||
FunctionIterator functions_begin() const {
|
||||
return getDescriptor().MemberDescriptorContainer<FunctionDescriptor>::members_begin(this);
|
||||
}
|
||||
FunctionIterator functions_end() const {
|
||||
return getDescriptor().MemberDescriptorContainer<FunctionDescriptor>::members_end(this);
|
||||
}
|
||||
|
||||
YieldFunctionDescriptor* findYieldFunctionDescriptor(const char* name) const
|
||||
{
|
||||
return getDescriptor().MemberDescriptorContainer<YieldFunctionDescriptor>::findDescriptor(name);
|
||||
}
|
||||
|
||||
YieldFunctionIterator yield_functions_begin() const {
|
||||
return getDescriptor().MemberDescriptorContainer<YieldFunctionDescriptor>::members_begin(this);
|
||||
}
|
||||
YieldFunctionIterator yield_functions_end() const {
|
||||
return getDescriptor().MemberDescriptorContainer<YieldFunctionDescriptor>::members_end(this);
|
||||
}
|
||||
|
||||
CallbackDescriptor* findCallbackDescriptor(const char* name)
|
||||
{
|
||||
return getDescriptor().MemberDescriptorContainer<CallbackDescriptor>::findDescriptor(name);
|
||||
}
|
||||
CallbackIterator callbacks_begin() {
|
||||
return getDescriptor().MemberDescriptorContainer<CallbackDescriptor>::members_begin(this);
|
||||
}
|
||||
CallbackIterator callbacks_end() {
|
||||
return getDescriptor().MemberDescriptorContainer<CallbackDescriptor>::members_end(this);
|
||||
}
|
||||
|
||||
EventDescriptor* findSignalDescriptor(const char* name) const
|
||||
{
|
||||
return getDescriptor().MemberDescriptorContainer<EventDescriptor>::findDescriptor(name);
|
||||
}
|
||||
|
||||
ConstSignalIterator signals_begin() const {
|
||||
return getDescriptor().MemberDescriptorContainer<EventDescriptor>::members_begin(this);
|
||||
}
|
||||
ConstSignalIterator signals_end() const {
|
||||
return getDescriptor().MemberDescriptorContainer<EventDescriptor>::members_end(this);
|
||||
}
|
||||
SignalIterator signals_begin() {
|
||||
return getDescriptor().MemberDescriptorContainer<EventDescriptor>::members_begin(this);
|
||||
}
|
||||
SignalIterator signals_end() {
|
||||
return getDescriptor().MemberDescriptorContainer<EventDescriptor>::members_end(this);
|
||||
}
|
||||
|
||||
const std::string* getXmlId() const {
|
||||
return xmlId.get();
|
||||
}
|
||||
|
||||
void setXmlId(const std::string& newId) {
|
||||
if (!xmlId)
|
||||
{
|
||||
xmlId.reset(new std::string(newId));
|
||||
}
|
||||
else
|
||||
{
|
||||
*xmlId = newId;
|
||||
}
|
||||
}
|
||||
|
||||
virtual const RBX::Name& getClassName() const = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,491 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "reflection/member.h"
|
||||
#include "reflection/enumconverter.h"
|
||||
#include "reflection/type.h"
|
||||
#include "v8xml/xmlelement.h"
|
||||
#include "V8Xml/Reference.h" // TODO: Reflection namespace should not know about V8Tree
|
||||
#include "boost/cast.hpp"
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Reflection
|
||||
{
|
||||
class ConstProperty;
|
||||
class Property;
|
||||
|
||||
typedef enum {
|
||||
READONLY,
|
||||
READWRITE
|
||||
} Mutability;
|
||||
|
||||
// Base that describes a Property
|
||||
class RBXBaseClass PropertyDescriptor : public MemberDescriptor
|
||||
{
|
||||
private:
|
||||
unsigned bIsPublic : 1;
|
||||
unsigned bIsEditable : 1;
|
||||
unsigned bCanReplicate : 1;
|
||||
unsigned bCanXmlRead : 1;
|
||||
unsigned bCanXmlWrite : 1;
|
||||
unsigned bIsScriptable : 1;
|
||||
unsigned bAlwaysClone : 1;
|
||||
|
||||
public:
|
||||
typedef ConstProperty ConstMember;
|
||||
typedef Property Member;
|
||||
|
||||
public:
|
||||
// Note: isPublic == PropertyUI shown, and Can BaseScript against. ToDO: possibly split?
|
||||
|
||||
enum Functionality {
|
||||
STANDARD = 1 + 2 + 4 + 8 + 16, // isPublic, canReplicate, canXmlRead , canXmlWrite, isScriptable
|
||||
NO_XML_WRITE = 1 + 2 + 4 + 0 + 16, // isPublic, canReplicate, canXmlRead , , isScriptable
|
||||
UI = 1 + 0 +(4)+ 0 + 16, // isPublic, (canXmlRead), isScriptable //Remove canXmlRead from UI
|
||||
SCRIPTING = 1 + 2 + 0 + 0 + 16, // isPublic, canReplicate, isScriptable
|
||||
STREAMING = 0 + 2 + 4 + 8 + 0, // canReplicate, canXmlRead , canXmlWrite,
|
||||
CLUSTER = 0 + 0 + 4 + 8 + 0, // canXmlRead , canXmlWrite,
|
||||
LEGACY = 0 + 0 + 4 + 0 + 0, // canXmlRead ,
|
||||
REPLICATE_ONLY = 0 + 2 + 0 + 0 + 0, // canReplicate
|
||||
LEGACY_SCRIPTING = 0 + 0 + 4 + 0 + 16, // canXmlRead , isScriptable
|
||||
HIDDEN_SCRIPTING = 0 + 0 + 0 + 0 + 16, // isScriptable
|
||||
PUBLIC_SERIALIZED = 1 + 0 + 4 + 8 + 0, // isPublic, canXmlRead , canXmlWrite,
|
||||
REPLICATE_CLONE = 0 + 2 + 0 + 0 + 0 + 32, // canReplicate alwaysClone
|
||||
STANDARD_NO_REPLICATE = 1 + 0 + 4 + 8 + 16, // isPublic, canXmlRead , canXmlWrite, isScriptable
|
||||
STANDARD_NO_SCRIPTING = 1 + 2 + 4 + 8 + 0, // isPublic, canReplicate, canXmlRead , canXmlWrite
|
||||
PUBLIC_REPLICATE = 1 + 2 + 0 + 0 + 0, // isPublic, canReplicate
|
||||
};
|
||||
|
||||
struct Attributes : public Descriptor::Attributes
|
||||
{
|
||||
Functionality flags;
|
||||
|
||||
Attributes():flags(STANDARD) {}
|
||||
Attributes(Functionality flags):flags(flags) {}
|
||||
static Attributes deprecated(const MemberDescriptor& preferred, Functionality flags = UI);
|
||||
static Attributes deprecated(Functionality flags = UI);
|
||||
};
|
||||
|
||||
const Type& type;
|
||||
const bool bIsEnum;
|
||||
|
||||
protected:
|
||||
PropertyDescriptor(ClassDescriptor& classDescriptor, const Type& type, const char* name, const char* category, Attributes attributes, Security::Permissions security, bool isEnum = false);
|
||||
|
||||
inline void checkFlags()
|
||||
{
|
||||
if (isWriteOnly())
|
||||
{
|
||||
bCanXmlWrite = 0;
|
||||
bCanReplicate = 0;
|
||||
}
|
||||
if (isReadOnly())
|
||||
{
|
||||
bCanXmlRead = 0;
|
||||
bCanReplicate = 0;
|
||||
}
|
||||
}
|
||||
public:
|
||||
inline bool isPublic() const { return bIsPublic != 0; }
|
||||
inline bool isScriptable() const { return bIsScriptable != 0; }
|
||||
|
||||
void setEditable(bool editable) { bIsEditable = editable ? 1 : 0; }
|
||||
inline bool isEditable() const { return bIsEditable != 0; }
|
||||
|
||||
virtual bool isReadOnly() const = 0;
|
||||
virtual bool isWriteOnly() const = 0;
|
||||
inline bool canXmlRead() const
|
||||
{
|
||||
RBXASSERT(bCanXmlRead == 0 || !isReadOnly());
|
||||
return bCanXmlRead != 0;
|
||||
}
|
||||
inline bool canXmlWrite() const
|
||||
{
|
||||
RBXASSERT(bCanXmlWrite == 0 || !isWriteOnly());
|
||||
return bCanXmlWrite != 0;
|
||||
}
|
||||
inline bool canReplicate() const
|
||||
{
|
||||
RBXASSERT(bCanReplicate == 0 || (!isReadOnly() && !isWriteOnly()));
|
||||
return bCanReplicate != 0;
|
||||
}
|
||||
inline bool alwaysClone() const
|
||||
{
|
||||
return bAlwaysClone != 0;
|
||||
}
|
||||
|
||||
bool operator==(const PropertyDescriptor& other) const {
|
||||
return this == &other;
|
||||
}
|
||||
bool operator!=(const PropertyDescriptor& other) const {
|
||||
return this != &other;
|
||||
}
|
||||
|
||||
virtual bool equalValues(const DescribedBase* a, const DescribedBase* b) const = 0;
|
||||
|
||||
virtual void getVariant(const DescribedBase* instance, Variant& value) const = 0;
|
||||
virtual void setVariant(DescribedBase* instance, const Variant& value) const = 0;
|
||||
virtual void copyValue(const DescribedBase* source, DescribedBase* destination) const = 0;
|
||||
|
||||
virtual int getDataSize(const DescribedBase* instance) const = 0;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// String conversion interface
|
||||
public:
|
||||
virtual bool hasStringValue() const = 0;
|
||||
virtual std::string getStringValue(const DescribedBase* instance) const {
|
||||
if (hasStringValue())
|
||||
debugAssertM(false, "you must implement getStringValue");
|
||||
else
|
||||
debugAssertM(false, "don't call getStringValue when hasStringValue()==false");
|
||||
return "";
|
||||
}
|
||||
virtual bool setStringValue(DescribedBase* instance, const std::string& text) const {
|
||||
if (hasStringValue())
|
||||
debugAssertM(false, "you must implement setStringValue");
|
||||
else
|
||||
debugAssertM(false, "don't call setStringValue when hasStringValue()==false");
|
||||
return false;
|
||||
}
|
||||
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Xml streaming interface
|
||||
public:
|
||||
XmlElement* write(const DescribedBase* instance, bool ignoreWriteProtection = false) const;
|
||||
virtual void read(DescribedBase* instance, const XmlElement* element, RBX::IReferenceBinder& binder) const;
|
||||
private:
|
||||
virtual void writeValue(const DescribedBase* instance, XmlElement* element) const = 0;
|
||||
virtual void readValue(DescribedBase* instance, const XmlElement* element, RBX::IReferenceBinder& binder) const = 0;
|
||||
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/
|
||||
|
||||
};
|
||||
|
||||
|
||||
template <typename V>
|
||||
class TypedPropertyDescriptor : public PropertyDescriptor
|
||||
{
|
||||
private:
|
||||
typedef PropertyDescriptor Super;
|
||||
|
||||
public:
|
||||
class RBXInterface GetSet
|
||||
{
|
||||
public:
|
||||
virtual bool isReadOnly() const = 0;
|
||||
virtual bool isWriteOnly() const = 0;
|
||||
virtual V getValue(const DescribedBase* object) const = 0;
|
||||
virtual void setValue(DescribedBase* object, const V& value) const = 0;
|
||||
};
|
||||
protected:
|
||||
std::auto_ptr<GetSet> getset;
|
||||
TypedPropertyDescriptor(ClassDescriptor& classDescriptor, const Type& type, const char* name, const char* category, std::auto_ptr<GetSet> getset, Attributes flags, Security::Permissions security)
|
||||
:PropertyDescriptor(classDescriptor, type, name, category, flags, security),getset(getset)
|
||||
{
|
||||
if (this->getset.get())
|
||||
this->checkFlags();
|
||||
}
|
||||
TypedPropertyDescriptor(ClassDescriptor& classDescriptor, const char* name, const char* category, std::auto_ptr<GetSet> getset, Attributes flags, Security::Permissions security)
|
||||
:PropertyDescriptor(classDescriptor, Type::singleton<V>(), name, category, flags, security),getset(getset)
|
||||
{
|
||||
if (this->getset.get())
|
||||
this->checkFlags();
|
||||
}
|
||||
public:
|
||||
/*implement*/ V get(const DescribedBase* instance) const
|
||||
{
|
||||
return getset->getValue(instance);
|
||||
}
|
||||
/*implement*/ void set(DescribedBase* instance, const V& value) const
|
||||
{
|
||||
getset->setValue(instance, value);
|
||||
}
|
||||
|
||||
/*implement*/ void getVariant(const DescribedBase* instance, Variant& value) const
|
||||
{
|
||||
value = getset->getValue(instance);
|
||||
}
|
||||
/*implement*/ void setVariant(DescribedBase* instance, const Variant& value) const
|
||||
{
|
||||
// TODO: This might be inefficient. How is the value stored in getset???
|
||||
getset->setValue(instance, value.get<V>());
|
||||
}
|
||||
/*implement*/ void copyValue(const DescribedBase* source, DescribedBase* destination) const
|
||||
{
|
||||
set(destination, get(source));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Variant interface
|
||||
public:
|
||||
virtual bool isReadOnly() const {
|
||||
return getset->isReadOnly();
|
||||
}
|
||||
|
||||
virtual bool isWriteOnly() const {
|
||||
return getset->isWriteOnly();
|
||||
}
|
||||
|
||||
V getValue(const DescribedBase* object) const {
|
||||
return getset->getValue(object);
|
||||
}
|
||||
|
||||
void setValue(DescribedBase* object, const V& value) const {
|
||||
getset->setValue(object, value);
|
||||
}
|
||||
|
||||
/*implement*/ bool equalValues(const DescribedBase* a, const DescribedBase* b) const {
|
||||
return getValue(a) == getValue(b);
|
||||
}
|
||||
|
||||
virtual int getDataSize(const DescribedBase* instance) const;
|
||||
|
||||
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/
|
||||
|
||||
virtual bool hasStringValue() const;
|
||||
virtual std::string getStringValue(const DescribedBase* instance) const;
|
||||
virtual bool setStringValue(DescribedBase* instance, const std::string& text) const;
|
||||
private:
|
||||
virtual void readValue(DescribedBase* instance, const XmlElement* element, IReferenceBinder& binder) const;
|
||||
virtual void writeValue(const DescribedBase* instance, XmlElement* element) const;
|
||||
};
|
||||
|
||||
// A light-weight convenience class that associates a PropertyDescriptor
|
||||
// with a described object to create a "Property"
|
||||
class ConstProperty
|
||||
{
|
||||
protected:
|
||||
const PropertyDescriptor* descriptor;
|
||||
const DescribedBase* instance;
|
||||
public:
|
||||
inline ConstProperty():descriptor(0),instance(0) {}
|
||||
inline ConstProperty(const PropertyDescriptor& descriptor, const DescribedBase* instance)
|
||||
:descriptor(&descriptor),instance(instance)
|
||||
{
|
||||
RBXASSERT(!instance || descriptor.isMemberOf(instance));
|
||||
}
|
||||
|
||||
inline ConstProperty(const ConstProperty& other)
|
||||
:descriptor(other.descriptor),instance(other.instance)
|
||||
{}
|
||||
|
||||
inline const DescribedBase* getInstance() const { return instance; }
|
||||
inline const PropertyDescriptor& getDescriptor() const { return *descriptor; }
|
||||
|
||||
inline ConstProperty& operator =(const ConstProperty& other)
|
||||
{
|
||||
this->descriptor = other.descriptor;
|
||||
this->instance = other.instance;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator ==(const ConstProperty& other) const
|
||||
{
|
||||
return (this->descriptor == other.descriptor) && (this->instance == other.instance);
|
||||
}
|
||||
|
||||
inline const RBX::Name& getName() const {
|
||||
return descriptor->name;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
inline bool isValueType() const
|
||||
{
|
||||
return descriptor->type==Type::singleton<V>();
|
||||
}
|
||||
template<typename V>
|
||||
inline V getValue() const
|
||||
{
|
||||
RBXASSERT(isValueType<V>());
|
||||
return static_cast<const TypedPropertyDescriptor<V>*>(descriptor)->getValue(instance);
|
||||
}
|
||||
|
||||
inline bool hasStringValue() const
|
||||
{
|
||||
return descriptor->hasStringValue();
|
||||
}
|
||||
inline std::string getStringValue() const
|
||||
{
|
||||
return descriptor->getStringValue(instance);
|
||||
}
|
||||
|
||||
inline XmlElement* write() const
|
||||
{
|
||||
return descriptor->write(instance);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A light-weight convenience class that associates a PropertyDescriptor
|
||||
// with a described object to create a "Property"
|
||||
class Property : public ConstProperty
|
||||
{
|
||||
public:
|
||||
inline Property(const PropertyDescriptor& descriptor, DescribedBase* instance)
|
||||
:ConstProperty(descriptor, instance)
|
||||
{}
|
||||
inline Property(const Property& other)
|
||||
:ConstProperty(*other.descriptor, other.instance)
|
||||
{}
|
||||
inline Property& operator =(const Property& other)
|
||||
{
|
||||
this->descriptor = other.descriptor;
|
||||
this->instance = other.instance;
|
||||
return *this;
|
||||
}
|
||||
inline bool operator ==(const Property& other) const
|
||||
{
|
||||
return this->descriptor==other.descriptor && this->instance==other.instance;
|
||||
}
|
||||
|
||||
inline bool operator !=(const Property& other) const
|
||||
{
|
||||
return this->descriptor!=other.descriptor || this->instance!=other.instance;
|
||||
}
|
||||
|
||||
DescribedBase* getInstance() const { return const_cast<DescribedBase*>(instance); }
|
||||
|
||||
template<typename V>
|
||||
inline void setValue(const V& value)
|
||||
{
|
||||
RBXASSERT(isValueType<V>());
|
||||
static_cast<const TypedPropertyDescriptor<V>*>(descriptor)->setValue(const_cast<DescribedBase*>(instance), value);
|
||||
}
|
||||
|
||||
inline bool setStringValue(const std::string& text)
|
||||
{
|
||||
return descriptor->setStringValue(const_cast<DescribedBase*>(instance), text);
|
||||
}
|
||||
|
||||
inline void read(const XmlElement* element, RBX::IReferenceBinder& binder)
|
||||
{
|
||||
descriptor->read(const_cast<DescribedBase*>(instance), element, binder);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
std::size_t hash_value(const ConstProperty& prop);
|
||||
|
||||
// Interface
|
||||
// maps enums to an index (Used by UIs like a property grid)
|
||||
class RBXInterface EnumPropertyDescriptor : public PropertyDescriptor {
|
||||
public:
|
||||
const EnumDescriptor& enumDescriptor;
|
||||
virtual size_t getIndexValue(const DescribedBase* instance) const = 0;
|
||||
virtual bool setIndexValue(DescribedBase* instance, size_t value) const = 0; // throws an exception if value is illegal
|
||||
virtual int getEnumValue(const DescribedBase* instance) const = 0;
|
||||
virtual bool setEnumValue(DescribedBase* instance, int index) const = 0;
|
||||
virtual const EnumDescriptor::Item* getEnumItem(const DescribedBase* instance) const = 0;
|
||||
bool setEnumItem(DescribedBase* instance, const EnumDescriptor::Item& item) const {
|
||||
if (item.owner!=enumDescriptor)
|
||||
return false;
|
||||
return setEnumValue(instance, item.value);
|
||||
}
|
||||
virtual int getDataSize(const DescribedBase* instance) const
|
||||
{ return sizeof(int); }
|
||||
protected:
|
||||
EnumPropertyDescriptor(ClassDescriptor& classDescriptor, const EnumDescriptor& enumDescriptor, const char* name, const char* category, Attributes flags = STANDARD, Security::Permissions security=Security::None)
|
||||
:PropertyDescriptor(classDescriptor, enumDescriptor, name, category, flags, security, true)
|
||||
,enumDescriptor(enumDescriptor)
|
||||
{}
|
||||
};
|
||||
|
||||
class RBXBaseClass RefPropertyDescriptor : public PropertyDescriptor {
|
||||
|
||||
private:
|
||||
typedef PropertyDescriptor Super;
|
||||
|
||||
public:
|
||||
virtual DescribedBase* getRefValue(const DescribedBase* instance) const = 0;
|
||||
virtual void setRefValue(DescribedBase* instance, DescribedBase* value) const = 0;
|
||||
virtual void setRefValueUnsafe(DescribedBase* instance, DescribedBase* value) const = 0;
|
||||
|
||||
RefPropertyDescriptor(ClassDescriptor& classDescriptor, const Type& type, const char* name, const char* category, Attributes flags = STANDARD, Security::Permissions security=Security::None)
|
||||
:PropertyDescriptor(classDescriptor, type, name, category, flags, security)
|
||||
{}
|
||||
|
||||
virtual int getDataSize(const DescribedBase* instance) const
|
||||
{ return 0; }
|
||||
|
||||
bool hasStringValue() const {
|
||||
return false;
|
||||
}
|
||||
std::string getStringValue(const DescribedBase* instance) const{
|
||||
return Super::getStringValue(instance);
|
||||
}
|
||||
bool setStringValue(DescribedBase* instance, const std::string& text) const {
|
||||
return Super::setStringValue(instance, text);
|
||||
}
|
||||
|
||||
|
||||
static bool isRefPropertyDescriptor(const Reflection::Type& type)
|
||||
{
|
||||
static const RBX::Name& name = RBX::Name::lookup("Object");
|
||||
return (type.name == name);
|
||||
}
|
||||
|
||||
static bool isRefPropertyDescriptor(const PropertyDescriptor& descriptor)
|
||||
{
|
||||
// See RefType in reflection.h
|
||||
bool result = isRefPropertyDescriptor(descriptor.type);
|
||||
RBXASSERT(result == (0 != dynamic_cast<const Reflection::RefPropertyDescriptor*>(&descriptor)));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// A very useful class for binding Instance members to PropertyDescriptors
|
||||
template<typename V, Mutability mutability = READWRITE>
|
||||
class BoundProp : public Reflection::TypedPropertyDescriptor<V>
|
||||
{
|
||||
template<class Class>
|
||||
class BoundPropGetSet : public TypedPropertyDescriptor<V>::GetSet
|
||||
{
|
||||
BoundProp& desc;
|
||||
V Class::*member;
|
||||
typedef void (Class::*ChangedMember)(const Reflection::PropertyDescriptor&);
|
||||
ChangedMember changed;
|
||||
public:
|
||||
BoundPropGetSet(BoundProp& desc, V Class::*member, ChangedMember changed):desc(desc),member(member),changed(changed) {}
|
||||
virtual bool isReadOnly() const {
|
||||
return mutability == READONLY;
|
||||
}
|
||||
virtual bool isWriteOnly() const {
|
||||
return false;
|
||||
}
|
||||
virtual V getValue(const Reflection::DescribedBase* object) const {
|
||||
const Class* c = static_cast<const Class*>(object);
|
||||
return c->*member;
|
||||
}
|
||||
virtual void setValue(Reflection::DescribedBase* object, const V& value) const {
|
||||
if (mutability == READONLY)
|
||||
throw std::runtime_error("can't set value");
|
||||
|
||||
Class* c = static_cast<Class*>(object);
|
||||
if (c->*member != value)
|
||||
{
|
||||
c->*member = value;
|
||||
if (changed)
|
||||
(c->*changed)(desc);
|
||||
c->raisePropertyChanged(desc);
|
||||
}
|
||||
}
|
||||
};
|
||||
public:
|
||||
template<class Class>
|
||||
BoundProp(const char* name, const char* category, V Class::*member, void (Class::*changed)(const Reflection::PropertyDescriptor&), typename PropertyDescriptor::Attributes flags = PropertyDescriptor::STANDARD, Security::Permissions security = Security::None)
|
||||
:Reflection::TypedPropertyDescriptor<V>(Class::classDescriptor(), name, category, std::auto_ptr<typename TypedPropertyDescriptor<V>::GetSet>(), flags, security)
|
||||
{
|
||||
this->getset.reset(new BoundPropGetSet<Class>(*this, member, changed));
|
||||
this->checkFlags();
|
||||
}
|
||||
template<class Class>
|
||||
BoundProp(const char* name, const char* category, V Class::*member, typename PropertyDescriptor::Attributes flags = PropertyDescriptor::STANDARD, Security::Permissions security = Security::None)
|
||||
:Reflection::TypedPropertyDescriptor<V>(Class::classDescriptor(), name, category, std::auto_ptr<typename TypedPropertyDescriptor<V>::GetSet>(), flags, security)
|
||||
{
|
||||
this->getset.reset(new BoundPropGetSet<Class>(*this, member, NULL));
|
||||
this->checkFlags();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "reflection/Descriptor.h"
|
||||
#include <boost/any.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <util/utilities.h>
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <list>
|
||||
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Reflection
|
||||
{
|
||||
template<typename T> class TypeRegistrar;
|
||||
|
||||
// Types supported by the Reflection framework
|
||||
class Type : public Descriptor
|
||||
{
|
||||
template<class T>
|
||||
friend class TypeRegistrar;
|
||||
|
||||
template<class T>
|
||||
static const Type& getSingleton(); // Must be implemented for each type used
|
||||
void addToAllTypes();
|
||||
|
||||
public:
|
||||
const Name& tag;
|
||||
const bool isFloat;
|
||||
const bool isNumber;
|
||||
const bool isEnum;
|
||||
|
||||
static const std::vector<const Type*>& getAllTypes();
|
||||
|
||||
template<class T>
|
||||
static inline const Type& singleton()
|
||||
{
|
||||
return getSingleton<T>();
|
||||
}
|
||||
|
||||
bool operator==(const Type& right) const {
|
||||
return this==&right;
|
||||
}
|
||||
bool operator!=(const Type& right) const {
|
||||
return this!=&right;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool isType() const {
|
||||
return this == &getSingleton<T>();
|
||||
}
|
||||
|
||||
protected:
|
||||
template<class T>
|
||||
Type(const char* name, T* dummy)
|
||||
:Descriptor(name, Descriptor::Attributes())
|
||||
,tag(Name::lookup(name))
|
||||
,isNumber(boost::is_arithmetic<T>::value)
|
||||
,isFloat(boost::is_float<T>::value)
|
||||
,isEnum(false)
|
||||
{
|
||||
*isOutdated = false;
|
||||
*isReplicable = true;
|
||||
RBXASSERT(!this->tag.empty());
|
||||
addToAllTypes();
|
||||
}
|
||||
template<class T>
|
||||
Type(const char* name, const char* tag, T* dummy)
|
||||
:Descriptor(name, Descriptor::Attributes())
|
||||
,tag(Name::declare(tag))
|
||||
,isNumber(boost::is_arithmetic<T>::value)
|
||||
,isFloat(boost::is_float<T>::value)
|
||||
,isEnum(false)
|
||||
{
|
||||
RBXASSERT(!this->tag.empty());
|
||||
addToAllTypes();
|
||||
}
|
||||
|
||||
Type(const char* name, const char* tag, bool isNumber, bool isFloat, bool isEnum)
|
||||
:Descriptor(name, Descriptor::Attributes())
|
||||
,tag(Name::declare(tag))
|
||||
,isNumber(isNumber)
|
||||
,isFloat(isFloat)
|
||||
,isEnum(isEnum)
|
||||
{
|
||||
RBXASSERT(!this->tag.empty());
|
||||
addToAllTypes();
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const RBX::Reflection::Type& type);
|
||||
|
||||
// Handy macro for registering a type
|
||||
#define RBX_REGISTER_TYPE(mType) template<> RBX::Reflection::TypeRegistrar<mType> RBX::Reflection::TypeRegistrar<mType>::registrar(0)
|
||||
|
||||
// This class is designed to prevent clients of the library
|
||||
// from forgetting to initialize their class descriptors
|
||||
template<class T>
|
||||
class TypeRegistrar : boost::noncopyable
|
||||
{
|
||||
int x;
|
||||
|
||||
//// GCC does not generate the registrar variable defination & fails at Link Time. Force Construct by passing in an dummy arg to ctor. That works. WEIRD huh?
|
||||
TypeRegistrar(int i):x(i)
|
||||
{
|
||||
// This assertion is added to catch a nasty implicit use of boost::any with Variant objects.
|
||||
// If you get a tricky link error, add your own assertion here
|
||||
BOOST_STATIC_ASSERT((!boost::is_same<T, boost::any>::value));
|
||||
// This call registers the Type descriptor
|
||||
// in the reflection database
|
||||
Type::getSingleton<T>();
|
||||
}
|
||||
|
||||
public:
|
||||
// The instantiation of this static member must be in a unit
|
||||
// that is initialized in the main thread before any objects
|
||||
// are created. Otherwise the reflection database
|
||||
// can change at runtime, which would be a disaster
|
||||
static TypeRegistrar registrar;
|
||||
};
|
||||
|
||||
// Helper class
|
||||
template<typename T>
|
||||
class TType : public Type
|
||||
{
|
||||
friend class Type;
|
||||
protected:
|
||||
TType(const char* name)
|
||||
:Type(name, (T*)NULL)
|
||||
{
|
||||
}
|
||||
TType(const char* name, const char* tag)
|
||||
:Type(name, tag, (T*)NULL)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Variant
|
||||
{
|
||||
struct Storage
|
||||
{
|
||||
char data[96];
|
||||
};
|
||||
|
||||
const Type* _type;
|
||||
rbx::placement_any<Storage> value;
|
||||
|
||||
public:
|
||||
inline Variant()
|
||||
: _type(&Type::singleton<void>())
|
||||
, value()
|
||||
{}
|
||||
|
||||
inline Variant(const Variant& other)
|
||||
: _type(other._type)
|
||||
, value(other.value)
|
||||
{}
|
||||
|
||||
inline Variant& operator=(const Variant& rhs)
|
||||
{
|
||||
_type = rhs._type;
|
||||
value = rhs.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
inline Variant(const ValueType& value)
|
||||
: _type(&Type::singleton<ValueType>())
|
||||
, value(value)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
inline Variant& operator=(const ValueType& rhs)
|
||||
{
|
||||
_type = &Type::singleton<ValueType>();
|
||||
value = rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const Type& type() const {
|
||||
return *_type;
|
||||
}
|
||||
|
||||
inline bool isVoid() const
|
||||
{
|
||||
return *_type==Type::singleton<void>();
|
||||
}
|
||||
inline bool isFloat() const { return type().isFloat; }
|
||||
inline bool isNumber() const { return type().isNumber; }
|
||||
inline bool isString() const { return isType<std::string>();}
|
||||
|
||||
template<class ValueType>
|
||||
inline bool isType() const {
|
||||
return _type->isType<ValueType>();
|
||||
}
|
||||
|
||||
// throws an exception if unable to convert
|
||||
template<typename ValueType>
|
||||
ValueType& convert();
|
||||
|
||||
// throws an exception if unable to convert
|
||||
template<typename ValueType>
|
||||
inline ValueType get() const
|
||||
{
|
||||
if (isType<ValueType>())
|
||||
return cast<ValueType>();
|
||||
else
|
||||
{
|
||||
// Create a non-const copy to extract the value from
|
||||
Variant v(*this);
|
||||
return v.convert<ValueType>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline const T& cast() const {
|
||||
if (!isType<T>())
|
||||
throw std::runtime_error("Variant cast failed");
|
||||
return *reinterpret_cast<const T*>(value.getData());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T& cast() {
|
||||
if (!isType<T>())
|
||||
throw std::runtime_error("Variant cast failed");
|
||||
return *reinterpret_cast<T*>(value.getData());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline const T* tryCast() const {
|
||||
if (!isType<T>())
|
||||
return NULL;
|
||||
return reinterpret_cast<const T*>(value.getData());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T* tryCast() {
|
||||
if (!isType<T>())
|
||||
return NULL;
|
||||
return reinterpret_cast<T*>(value.getData());
|
||||
}
|
||||
|
||||
private:
|
||||
template<class ValueType>
|
||||
ValueType& genericConvert();
|
||||
|
||||
};
|
||||
|
||||
// Equivalent to an array in Lua
|
||||
typedef std::vector<Variant> ValueArray;
|
||||
|
||||
// A limited table in Lua (keys must be strings for now)
|
||||
typedef boost::unordered_map<std::string, Variant> ValueTable;
|
||||
|
||||
struct Tuple
|
||||
{
|
||||
ValueArray values;
|
||||
Tuple() {}
|
||||
Tuple(size_t count):values(count) {}
|
||||
Tuple(const Tuple& other):values(other.values) {}
|
||||
//Tuple(const ValueArray& values):values(values) {}
|
||||
Variant& at(size_t i) { return values[i]; }
|
||||
const Variant& at(size_t i) const { return values[i]; }
|
||||
};
|
||||
|
||||
// The same as a ValueTable for now, but will always have a string key.
|
||||
// TODO: Use boost::unordered_map<> or vector<> instead?
|
||||
typedef std::map<std::string, Variant> ValueMap;
|
||||
|
||||
// Describes a function's signature
|
||||
class SignatureDescriptor
|
||||
{
|
||||
public:
|
||||
struct Item {
|
||||
friend class SignatureDescriptor;
|
||||
public:
|
||||
Item(const RBX::Name* name, const Type* type, const Variant& defaultValue);
|
||||
Item(const RBX::Name* name, const Type* type);
|
||||
const RBX::Name* name;
|
||||
const Type* type;
|
||||
const Variant defaultValue;
|
||||
bool hasDefaultValue() const
|
||||
{
|
||||
return defaultValue.type() == *type;
|
||||
}
|
||||
};
|
||||
// TODO: Would vector be more efficient?
|
||||
typedef std::list<Item> Arguments;
|
||||
|
||||
const Type* resultType;
|
||||
Arguments arguments;
|
||||
|
||||
void addArgument(const RBX::Name& name, const Type& type);
|
||||
void addArgument(const RBX::Name& name, const Type& type, const Variant& defaultValue);
|
||||
|
||||
SignatureDescriptor();
|
||||
};
|
||||
|
||||
template<class ValueType>
|
||||
ValueType& RBX::Reflection::Variant::genericConvert()
|
||||
{
|
||||
ValueType* id = tryCast<ValueType>();
|
||||
if (id!=NULL)
|
||||
return *id;
|
||||
|
||||
if (_type->isType<std::string>())
|
||||
{
|
||||
ValueType v;
|
||||
if (StringConverter<ValueType>::convertToValue(cast<std::string>(), v))
|
||||
{
|
||||
value = v;
|
||||
_type = &Type::singleton<ValueType>();
|
||||
return cast<ValueType>();
|
||||
}
|
||||
}
|
||||
|
||||
throw RBX::runtime_error("Unable to cast %s to %s", _type->tag.c_str(), Type::singleton<ValueType>().tag.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "Reflection/Function.h"
|
||||
#include <boost/function.hpp>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Reflection
|
||||
{
|
||||
class YieldFunction;
|
||||
|
||||
// Base that describes a YieldFunction
|
||||
class RBXBaseClass YieldFunctionDescriptor : public MemberDescriptor
|
||||
{
|
||||
public:
|
||||
|
||||
typedef YieldFunction ConstMember;
|
||||
typedef YieldFunction Member;
|
||||
|
||||
protected:
|
||||
SignatureDescriptor signature;
|
||||
YieldFunctionDescriptor(ClassDescriptor& classDescriptor, const char* name, Security::Permissions security, Attributes attributes);
|
||||
|
||||
public:
|
||||
const SignatureDescriptor& getSignature() const { return signature; }
|
||||
virtual void execute(DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function<void(Variant)> resumeFunction, boost::function<void(std::string)> errorFunction) const = 0;
|
||||
};
|
||||
|
||||
|
||||
// A light-weight convenience class that associates a FunctionDescriptor
|
||||
// with a described object to create a "Function"
|
||||
class YieldFunction
|
||||
{
|
||||
protected:
|
||||
const YieldFunctionDescriptor* descriptor;
|
||||
DescribedBase* instance;
|
||||
public:
|
||||
inline YieldFunction(const YieldFunctionDescriptor& descriptor, DescribedBase* instance)
|
||||
:descriptor(&descriptor),instance(instance)
|
||||
{}
|
||||
|
||||
inline YieldFunction(const YieldFunction& other)
|
||||
:descriptor(other.descriptor),instance(other.instance)
|
||||
{}
|
||||
inline YieldFunction& operator =(const YieldFunction& other)
|
||||
{
|
||||
this->descriptor = other.descriptor;
|
||||
this->instance = other.instance;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const RBX::Name& getName() const {
|
||||
return descriptor->name;
|
||||
}
|
||||
|
||||
inline const YieldFunctionDescriptor* getDescriptor() const {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
void execute(FunctionDescriptor::Arguments& arguments, boost::function<void(Variant)> resumeFunction, boost::function<void(std::string)> errorFunction) const {
|
||||
return descriptor->execute(const_cast<DescribedBase*>(instance), arguments, resumeFunction, errorFunction);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
#pragma once
|
||||
|
||||
#include "reflection/Descriptor.h"
|
||||
#include "util/Exception.h"
|
||||
#include <vector>
|
||||
#include "security/SecurityContext.h"
|
||||
|
||||
#include "rbx/DenseHash.h"
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Reflection
|
||||
{
|
||||
class ClassDescriptor;
|
||||
class DescribedBase;
|
||||
|
||||
struct StringHashPredicate
|
||||
{
|
||||
size_t operator()(const char* s) const;
|
||||
};
|
||||
|
||||
struct StringEqualPredicate
|
||||
{
|
||||
bool operator()(const char* lhs, const char* rhs) const
|
||||
{
|
||||
return strcmp(lhs, rhs) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Base class of describing a described object's member: (Member, Event, etc.)
|
||||
class RBXBaseClass MemberDescriptor : public Descriptor
|
||||
{
|
||||
public:
|
||||
static void (*memberHidingHook)(MemberDescriptor*, MemberDescriptor*);
|
||||
|
||||
// Category is a name used to group properties in the UI
|
||||
const RBX::Name& category;
|
||||
|
||||
const ClassDescriptor& owner;
|
||||
const Security::Permissions security;
|
||||
|
||||
protected:
|
||||
MemberDescriptor(const ClassDescriptor& owner, const char* name, const char* category, Attributes attributes, Security::Permissions security)
|
||||
:Descriptor(name, attributes)
|
||||
,owner(owner)
|
||||
,category(RBX::Name::declare(category))
|
||||
,security(security)
|
||||
{
|
||||
}
|
||||
virtual ~MemberDescriptor() {}
|
||||
public:
|
||||
bool isMemberOf(const ClassDescriptor& classDescriptor) const;
|
||||
bool isMemberOf(const DescribedBase* instance) const;
|
||||
};
|
||||
|
||||
|
||||
class MemberException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
const MemberDescriptor& desc;
|
||||
MemberException(const MemberDescriptor& desc, const std::string& _Message)
|
||||
:std::runtime_error(_Message)
|
||||
,desc(desc)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<class MemberDescriptorType>
|
||||
class MemberDescriptorContainer
|
||||
{
|
||||
// Used for sorting
|
||||
static bool compare(const MemberDescriptorType* a, const MemberDescriptorType* b)
|
||||
{
|
||||
return a->name < b->name;
|
||||
}
|
||||
public:
|
||||
class Collection : public std::vector<MemberDescriptorType*>
|
||||
{
|
||||
};
|
||||
|
||||
typedef DenseHashMap<const char*, MemberDescriptorType*, StringHashPredicate, StringEqualPredicate> DescriptorLookup;
|
||||
|
||||
typedef typename MemberDescriptorType::ConstMember ConstMemberType;
|
||||
typedef typename MemberDescriptorType::Member MemberType;
|
||||
class ConstIterator : public std::iterator<std::forward_iterator_tag, MemberType, void, MemberType>
|
||||
{
|
||||
friend class ClassDescriptor;
|
||||
const DescribedBase* instance;
|
||||
typename Collection::const_iterator iter;
|
||||
public:
|
||||
ConstIterator(const typename Collection::const_iterator& iter, const DescribedBase* instance)
|
||||
:iter(iter),instance(instance)
|
||||
{}
|
||||
ConstMemberType operator*() const
|
||||
{ // return designated object
|
||||
return ConstMemberType(**iter, instance);
|
||||
}
|
||||
|
||||
bool operator==(const ConstIterator& other) const { return iter==other.iter; }
|
||||
bool operator!=(const ConstIterator& other) const { return iter!=other.iter; }
|
||||
ConstIterator& operator++()
|
||||
{ // preincrement
|
||||
++iter;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
ConstIterator operator++(int)
|
||||
{ // postincrement
|
||||
ConstIterator _Tmp = *this;
|
||||
++*this;
|
||||
return (_Tmp);
|
||||
}
|
||||
|
||||
const MemberDescriptorType& getDescriptor() const { return **iter; }
|
||||
};
|
||||
|
||||
class Iterator : public std::iterator<std::forward_iterator_tag, MemberType, void, MemberType>
|
||||
{
|
||||
friend class ClassDescriptor;
|
||||
DescribedBase* instance;
|
||||
// iter is a const iterator, since we never modifiy the collection of descriptors
|
||||
typename Collection::const_iterator iter;
|
||||
public:
|
||||
Iterator(const typename Collection::const_iterator& iter, DescribedBase* instance)
|
||||
:iter(iter),instance(instance)
|
||||
{}
|
||||
MemberType operator*() const
|
||||
{ // return designated object
|
||||
return MemberType(**iter, instance);
|
||||
}
|
||||
|
||||
bool operator==(const Iterator& other) const { return iter==other.iter; }
|
||||
bool operator!=(const Iterator& other) const { return iter!=other.iter; }
|
||||
Iterator& operator++()
|
||||
{ // preincrement
|
||||
++iter;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
Iterator operator++(int)
|
||||
{ // postincrement
|
||||
Iterator _Tmp = *this;
|
||||
++*this;
|
||||
return (_Tmp);
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
Collection descriptors;
|
||||
DescriptorLookup descriptorLookup;
|
||||
private:
|
||||
static Collection& staticData()
|
||||
{
|
||||
static Collection result;
|
||||
return result;
|
||||
}
|
||||
static void initStaticData()
|
||||
{
|
||||
staticData();
|
||||
}
|
||||
static Collection& allDescriptors()
|
||||
{
|
||||
static boost::once_flag flag = BOOST_ONCE_INIT;
|
||||
boost::call_once(&initStaticData, flag);
|
||||
return staticData();
|
||||
}
|
||||
|
||||
protected:
|
||||
// This is a list of "subclasses"
|
||||
std::vector<MemberDescriptorContainer*> derivedContainers;
|
||||
|
||||
MemberDescriptorContainer* const base;
|
||||
protected:
|
||||
MemberDescriptorContainer(MemberDescriptorContainer* base)
|
||||
:base(base), descriptorLookup("")
|
||||
{
|
||||
if (base!=NULL)
|
||||
{
|
||||
// Grab base members that have already been declared
|
||||
mergeMembers(base);
|
||||
|
||||
// Subsequent members declared in a base class will be pushed down in the declare() function
|
||||
base->derivedContainers.push_back(this);
|
||||
}
|
||||
}
|
||||
|
||||
void declareSub(MemberDescriptorType* descriptor, MemberDescriptorType* replaceable)
|
||||
{
|
||||
RBXASSERT(replaceable != descriptor);
|
||||
{
|
||||
typename Collection::iterator iter = std::lower_bound(descriptors.begin(), descriptors.end(), descriptor, compare);
|
||||
if (iter == descriptors.end())
|
||||
{
|
||||
descriptors.insert(iter, descriptor);
|
||||
descriptorLookup[descriptor->name.c_str()] = descriptor;
|
||||
}
|
||||
else
|
||||
{
|
||||
RBXASSERT(*iter != descriptor);
|
||||
|
||||
if (*iter == replaceable)
|
||||
{
|
||||
// Replace it
|
||||
*iter = descriptor;
|
||||
descriptorLookup[descriptor->name.c_str()] = descriptor;
|
||||
}
|
||||
else if ((*iter)->name != descriptor->name)
|
||||
{
|
||||
descriptors.insert(iter, descriptor);
|
||||
descriptorLookup[descriptor->name.c_str()] = descriptor;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We've hit upon a member that will hide this member
|
||||
if (MemberDescriptor::memberHidingHook)
|
||||
(*MemberDescriptor::memberHidingHook)(descriptor, replaceable);
|
||||
return; // No need to continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recurse:
|
||||
for (typename std::vector<MemberDescriptorContainer*>::iterator iter = derivedContainers.begin(); iter != derivedContainers.end(); ++iter)
|
||||
(*iter)->declareSub(descriptor, replaceable);
|
||||
}
|
||||
public:
|
||||
void declare(MemberDescriptorType* descriptor)
|
||||
{
|
||||
MemberDescriptorType* replaceable = NULL;
|
||||
|
||||
{
|
||||
typename Collection::iterator iter = std::lower_bound(descriptors.begin(), descriptors.end(), descriptor, compare);
|
||||
if (iter == descriptors.end())
|
||||
{
|
||||
// add a new one
|
||||
descriptors.insert(iter, descriptor);
|
||||
descriptorLookup[descriptor->name.c_str()] = descriptor;
|
||||
}
|
||||
else if (*iter == descriptor)
|
||||
{
|
||||
// drop out if we've been here before
|
||||
return;
|
||||
}
|
||||
else if ((*iter)->name != descriptor->name)
|
||||
{
|
||||
// add a new one
|
||||
descriptors.insert(iter, descriptor);
|
||||
descriptorLookup[descriptor->name.c_str()] = descriptor;
|
||||
}
|
||||
else
|
||||
{
|
||||
// hide a member of a base class
|
||||
// TODO: Eventually we'd like to nuke this feature, but it is
|
||||
// required for some legacy things, like BoolValue
|
||||
replaceable = *iter;
|
||||
*iter = descriptor;
|
||||
descriptorLookup[descriptor->name.c_str()] = descriptor;
|
||||
if (MemberDescriptor::memberHidingHook)
|
||||
(*MemberDescriptor::memberHidingHook)(descriptor, replaceable);
|
||||
}
|
||||
}
|
||||
|
||||
// Also declare this member in sub-classes
|
||||
for (typename std::vector<MemberDescriptorContainer*>::iterator iter = derivedContainers.begin(); iter != derivedContainers.end(); ++iter)
|
||||
(*iter)->declareSub(descriptor, replaceable);
|
||||
|
||||
// Add this to allDescriptors (in a determanistic order)
|
||||
{
|
||||
typename Collection::iterator iter = allDescriptors().begin();
|
||||
while (iter!=allDescriptors().end())
|
||||
{
|
||||
MemberDescriptorType* desc = *iter;
|
||||
if (desc==descriptor)
|
||||
goto SKIP;
|
||||
int compare = RBX::Name::compare(descriptor->name, desc->name);
|
||||
if (compare<0)
|
||||
break;
|
||||
if (compare==0)
|
||||
{
|
||||
// This descriptor name already exists in a different class
|
||||
compare = RBX::Name::compare(descriptor->owner.name, desc->owner.name);
|
||||
// Enforce order using class name
|
||||
if (compare<0)
|
||||
break;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
allDescriptors().insert(iter, descriptor);
|
||||
SKIP: ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Type info enumeration
|
||||
typename Collection::const_iterator descriptors_begin() const {
|
||||
return descriptors.begin();
|
||||
}
|
||||
typename Collection::const_iterator descriptors_end() const {
|
||||
return descriptors.end();
|
||||
}
|
||||
size_t descriptor_size() const
|
||||
{
|
||||
return descriptors.size();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Enumeration of all descriptors
|
||||
static typename Collection::const_iterator all_begin() {
|
||||
return allDescriptors().begin();
|
||||
}
|
||||
static typename Collection::const_iterator all_end() {
|
||||
return allDescriptors().end();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Type info query
|
||||
MemberDescriptorType* findDescriptor(const char* name) const
|
||||
{
|
||||
MemberDescriptorType* const * item = descriptorLookup.find(name);
|
||||
|
||||
return item ? *item : NULL;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Member enumeration
|
||||
ConstIterator members_begin(const DescribedBase* instance) const {
|
||||
return ConstIterator(descriptors.begin(), instance);
|
||||
}
|
||||
ConstIterator members_end(const DescribedBase* instance) const {
|
||||
return ConstIterator(descriptors.end(), instance);
|
||||
}
|
||||
|
||||
Iterator members_begin(DescribedBase* instance) const {
|
||||
return Iterator(descriptors.begin(), instance);
|
||||
}
|
||||
Iterator members_end(DescribedBase* instance) const {
|
||||
return Iterator(descriptors.end(), instance);
|
||||
}
|
||||
protected:
|
||||
void mergeMembers(const MemberDescriptorContainer* source)
|
||||
{
|
||||
for (typename Collection::const_iterator iter = source->descriptors.begin(); iter != source->descriptors.end(); ++iter)
|
||||
declare(*iter);
|
||||
|
||||
// Recursively merge parent members as well
|
||||
if (source->base!=NULL)
|
||||
mergeMembers(source->base);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user