Browse Source

Data bindings: Never cast away const. Const-correctness is hard, so for now we disable anything const submitted in data bindings with compile-time errors.

Michael Ragazzon 4 years ago
parent
commit
f869c0fd57

+ 60 - 122
Include/RmlUi/Core/DataTypeRegister.h

@@ -39,13 +39,13 @@
 
 
 namespace Rml {
 namespace Rml {
 
 
-#define RMLUI_LOG_TYPE_ERROR(T, msg) RMLUI_ERRORMSG((String(msg) + String(" T: ") + String(rmlui_type_name<T>())).c_str())
-#define RMLUI_LOG_TYPE_ERROR_ASSERT(T, val, msg) RMLUI_ASSERTMSG(val, (String(msg) + String(" T: ") + String(rmlui_type_name<T>())).c_str())
+#define RMLUI_LOG_TYPE_ERROR(T, msg) RMLUI_ERRORMSG((String(msg) + String("\nT: ") + String(rmlui_type_name<T>())).c_str())
+#define RMLUI_LOG_TYPE_ERROR_ASSERT(T, val, msg) RMLUI_ASSERTMSG(val, (String(msg) + String("\nT: ") + String(rmlui_type_name<T>())).c_str())
 
 
 template<typename T>
 template<typename T>
 struct is_builtin_data_scalar {
 struct is_builtin_data_scalar {
 	static constexpr bool value = std::is_arithmetic<T>::value
 	static constexpr bool value = std::is_arithmetic<T>::value
-		|| std::is_same<typename std::remove_cv<T>::type, String>::value;
+		|| std::is_same<typename std::remove_const<T>::type, String>::value;
 };
 };
 
 
 
 
@@ -54,24 +54,38 @@ class StructHandle {
 public:
 public:
 	StructHandle(DataTypeRegister* type_register, StructDefinition* struct_definition) : type_register(type_register), struct_definition(struct_definition) {}
 	StructHandle(DataTypeRegister* type_register, StructDefinition* struct_definition) : type_register(type_register), struct_definition(struct_definition) {}
 
 
-	/// Register a member object or member getter function.
+	/// Register a member object.
 	/// @note Underlying type must be registered before it is used as a member.
 	/// @note Underlying type must be registered before it is used as a member.
 	/// @note Getter functions can return by reference, raw pointer, or by value. If returned by value,
 	/// @note Getter functions can return by reference, raw pointer, or by value. If returned by value,
 	///       the returned type must be a scalar data type, otherwise any data type can be used.
 	///       the returned type must be a scalar data type, otherwise any data type can be used.
 	/// @example
 	/// @example
 	///		struct Invader {
 	///		struct Invader {
 	///			int health;
 	///			int health;
+	/// 	};
+	///		struct_handle.RegisterMember("health", &Invader::health);
+	template <typename MemberType>
+	bool RegisterMember(const String& name, MemberType Object::* member_object_ptr)
+	{
+		return CreateMemberObjectDefinition(name, member_object_ptr);
+	}
+
+	/// Register a member getter function.
+	/// @note Underlying type must be registered before it is used as a member.
+	/// @note Getter functions can return by reference, raw pointer, or by value. If returned by value,
+	///       the returned type must be a scalar data type, otherwise any data type can be used.
+	/// @example
+	///		struct Invader {
 	///			std::vector<Weapon>& GetWeapons();
 	///			std::vector<Weapon>& GetWeapons();
 	///			/* ... */
 	///			/* ... */
 	/// 	};
 	/// 	};
-	///		struct_handle.RegisterMember("health", &Invader::health);
 	///		struct_handle.RegisterMember("weapons", &Invader::GetWeapons);
 	///		struct_handle.RegisterMember("weapons", &Invader::GetWeapons);
-	template <typename MemberType>
-	StructHandle<Object>& RegisterMember(const String& name, MemberType Object::* member_ptr) {
-		RegisterMemberDetail(name, member_ptr);
-		return *this;
+	template <typename ReturnType>
+	bool RegisterMember(const String& name, ReturnType(Object::* member_get_func_ptr)())
+	{
+		return RegisterMemberGetter(name, member_get_func_ptr);
 	}
 	}
 
 
+
 	/// Register member getter and setter functions. The getter and setter functions must return and assign scalar value types.
 	/// Register member getter and setter functions. The getter and setter functions must return and assign scalar value types.
 	/// @note Underlying type must be registered before it is used as a member.
 	/// @note Underlying type must be registered before it is used as a member.
 	/// @note Getter and setter functions can return by reference, raw pointer, or by value. Only scalar data types allowed.
 	/// @note Getter and setter functions can return by reference, raw pointer, or by value. Only scalar data types allowed.
@@ -82,10 +96,16 @@ public:
 	///			/* ... */
 	///			/* ... */
 	/// 	};
 	/// 	};
 	///		struct_handle.RegisterMember("color", &Invader::GetColor, &Invader::SetColor);
 	///		struct_handle.RegisterMember("color", &Invader::GetColor, &Invader::SetColor);
-	template <typename MemberGetType, typename MemberSetType>
-	StructHandle<Object>& RegisterMember(const String& name, MemberGetType Object::* member_get_func_ptr, MemberSetType Object::* member_set_func_ptr) {
-		RegisterMemberDetail(name, member_get_func_ptr, member_set_func_ptr);
-		return *this;
+	template <typename ReturnType, typename AssignType>
+	bool RegisterMember(const String& name, ReturnType(Object::* member_get_func_ptr)(), void(Object::* member_set_func_ptr)(AssignType))
+	{
+		using BasicReturnType = typename std::remove_reference<ReturnType>::type;
+		using BasicAssignType = typename std::remove_const<typename std::remove_reference<AssignType>::type>::type;
+		using UnderlyingType = typename std::conditional<std::is_null_pointer<BasicReturnType>::value, BasicAssignType, BasicReturnType>::type;
+
+		static_assert(std::is_null_pointer<ReturnType>::value || std::is_null_pointer<AssignType>::value || std::is_same<BasicReturnType, BasicAssignType>::value, "Provided getter and setter functions must get and set the same type.");
+
+		return CreateMemberScalarGetSetFuncDefinition<UnderlyingType>(name, member_get_func_ptr, member_set_func_ptr);
 	}
 	}
 
 
 	explicit operator bool() const {
 	explicit operator bool() const {
@@ -94,32 +114,22 @@ public:
 
 
 private:
 private:
 
 
-	template <typename MemberType>
-	bool RegisterMemberDetail(const String& name, MemberType Object::* member_object_ptr);
-
-	template <typename ReturnType>
-	bool RegisterMemberDetail(const String& name, ReturnType& (Object::* member_get_func_ptr)());
-
-	template <typename ReturnType>
-	bool RegisterMemberDetail(const String& name, ReturnType& (Object::* member_get_func_ptr)() const);
-
-	template <typename ReturnType>
-	bool RegisterMemberDetail(const String& name, ReturnType* (Object::* member_get_func_ptr)());
-
 	template <typename ReturnType>
 	template <typename ReturnType>
-	bool RegisterMemberDetail(const String& name, ReturnType* (Object::* member_get_func_ptr)() const);
+	bool RegisterMemberGetter(const String& name, ReturnType& (Object::* member_get_func_ptr)()) {
+		return CreateMemberGetFuncDefinition<ReturnType>(name, member_get_func_ptr);
+	}
 
 
 	template <typename ReturnType>
 	template <typename ReturnType>
-	bool RegisterMemberDetail(const String& name, ReturnType(Object::* member_get_func_ptr)());
+	bool RegisterMemberGetter(const String& name, ReturnType* (Object::* member_get_func_ptr)()) {
+		return CreateMemberGetFuncDefinition<ReturnType>(name, member_get_func_ptr);
+	}
 
 
 	template <typename ReturnType>
 	template <typename ReturnType>
-	bool RegisterMemberDetail(const String& name, ReturnType(Object::* member_get_func_ptr)() const);
-
-	template <typename ReturnType, typename AssignType>
-	bool RegisterMemberDetail(const String& name, ReturnType(Object::* member_get_func_ptr)(), void(Object::* member_set_func_ptr)(AssignType));
-
-	template <typename ReturnType, typename AssignType>
-	bool RegisterMemberDetail(const String& name, ReturnType(Object::* member_get_func_ptr)() const, void(Object::* member_set_func_ptr)(AssignType));
+	bool RegisterMemberGetter(const String& name, ReturnType(Object::* member_get_func_ptr)()) {
+		using BasicReturnType = typename std::remove_reference<ReturnType>::type;
+		using SetType = std::nullptr_t Object::*;
+		return CreateMemberScalarGetSetFuncDefinition<BasicReturnType>(name, member_get_func_ptr, SetType{});
+	}
 
 
 	template<typename MemberType>
 	template<typename MemberType>
 	bool CreateMemberObjectDefinition(const String& name, MemberType Object::* member_ptr);
 	bool CreateMemberObjectDefinition(const String& name, MemberType Object::* member_ptr);
@@ -224,12 +234,12 @@ public:
 	}
 	}
 
 
 private:
 private:
-
 	// Get definition for scalar types that can be assigned to and from Rml::Variant.
 	// Get definition for scalar types that can be assigned to and from Rml::Variant.
 	// We automatically register these when needed, so users don't have to register trivial types manually.
 	// We automatically register these when needed, so users don't have to register trivial types manually.
 	template<typename T, typename std::enable_if<!PointerTraits<T>::is_pointer::value && is_builtin_data_scalar<T>::value, int>::type = 0>
 	template<typename T, typename std::enable_if<!PointerTraits<T>::is_pointer::value && is_builtin_data_scalar<T>::value, int>::type = 0>
 	VariableDefinition* GetDefinitionDetail()
 	VariableDefinition* GetDefinitionDetail()
 	{
 	{
+		static_assert(!std::is_const<T>::value, "Data binding variables cannot point to constant variables.");
 		FamilyId id = Family<T>::Id();
 		FamilyId id = Family<T>::Id();
 
 
 		auto result = type_register.emplace(id, nullptr);
 		auto result = type_register.emplace(id, nullptr);
@@ -263,11 +273,14 @@ private:
 	template<typename T, typename std::enable_if<PointerTraits<T>::is_pointer::value, int>::type = 0>
 	template<typename T, typename std::enable_if<PointerTraits<T>::is_pointer::value, int>::type = 0>
 	VariableDefinition* GetDefinitionDetail()
 	VariableDefinition* GetDefinitionDetail()
 	{
 	{
-		static_assert(PointerTraits<T>::is_pointer::value, "Not a valid pointer type.");
-		static_assert(!PointerTraits<typename PointerTraits<T>::element_type>::is_pointer::value, "Recursive pointer type (pointer to pointer) detected.");
+		static_assert(PointerTraits<T>::is_pointer::value, "Invalid pointer type provided.");
+
+		using UnderlyingType = typename PointerTraits<T>::element_type;
+		static_assert(!PointerTraits<UnderlyingType>::is_pointer::value, "Recursive pointer types (pointer to pointer) to data variables are disallowed.");
+		static_assert(!std::is_const<UnderlyingType>::value, "Pointer to a const data variable is not supported.");
 
 
 		// Get the underlying definition.
 		// Get the underlying definition.
-		VariableDefinition* underlying_definition = GetDefinitionDetail<typename std::remove_cv<typename PointerTraits<T>::element_type>::type>();
+		VariableDefinition* underlying_definition = GetDefinitionDetail<UnderlyingType>();
 		if (!underlying_definition)
 		if (!underlying_definition)
 		{
 		{
 			RMLUI_LOG_TYPE_ERROR(T, "Underlying type of pointer not registered.");
 			RMLUI_LOG_TYPE_ERROR(T, "Underlying type of pointer not registered.");
@@ -293,92 +306,15 @@ private:
 	TransformFuncRegister transform_register;
 	TransformFuncRegister transform_register;
 };
 };
 
 
-
-
-template <typename Object>
-template <typename MemberType>
-bool StructHandle<Object>::RegisterMemberDetail(const String& name, MemberType Object::* member_object_ptr)
-{
-	return CreateMemberObjectDefinition(name, member_object_ptr);
-}
-
-template <typename Object>
-template <typename ReturnType>
-bool StructHandle<Object>::RegisterMemberDetail(const String& name, ReturnType& (Object::* member_get_func_ptr)())
-{
-	return CreateMemberGetFuncDefinition<ReturnType>(name, member_get_func_ptr);
-}
-
-template <typename Object>
-template <typename ReturnType>
-bool StructHandle<Object>::RegisterMemberDetail(const String& name, ReturnType& (Object::* member_get_func_ptr)() const)
-{
-	return CreateMemberGetFuncDefinition<ReturnType>(name, member_get_func_ptr);
-}
-
-template <typename Object>
-template <typename ReturnType>
-bool StructHandle<Object>::RegisterMemberDetail(const String& name, ReturnType* (Object::* member_get_func_ptr)())
-{
-	return CreateMemberGetFuncDefinition<ReturnType>(name, member_get_func_ptr);
-}
-
-template <typename Object>
-template <typename ReturnType>
-bool StructHandle<Object>::RegisterMemberDetail(const String& name, ReturnType* (Object::* member_get_func_ptr)() const)
-{
-	return CreateMemberGetFuncDefinition<ReturnType>(name, member_get_func_ptr);
-}
-
-template <typename Object>
-template <typename ReturnType>
-bool StructHandle<Object>::RegisterMemberDetail(const String& name, ReturnType(Object::* member_get_func_ptr)())
-{
-	using BasicReturnType = typename std::remove_cv<typename std::remove_reference<ReturnType>::type>::type;
-	using SetType = std::nullptr_t Object::*;
-	return CreateMemberScalarGetSetFuncDefinition<BasicReturnType>(name, member_get_func_ptr, SetType{});
-}
-
-
-template <typename Object>
-template <typename ReturnType>
-bool StructHandle<Object>::RegisterMemberDetail(const String& name, ReturnType(Object::* member_get_func_ptr)() const)
-{
-	using BasicReturnType = typename std::remove_cv<typename std::remove_reference<ReturnType>::type>::type;
-	using SetType = std::nullptr_t Object::*;
-	return CreateMemberScalarGetSetFuncDefinition<BasicReturnType>(name, member_get_func_ptr, SetType{});
-}
-
-template <typename Object>
-template <typename ReturnType, typename AssignType>
-bool StructHandle<Object>::RegisterMemberDetail(const String& name, ReturnType(Object::* member_get_func_ptr)(), void(Object::* member_set_func_ptr)(AssignType))
-{
-	using BasicReturnType = typename std::remove_cv<typename std::remove_reference<ReturnType>::type>::type;
-	using BasicAssignType = typename std::remove_cv<typename std::remove_reference<AssignType>::type>::type;
-	using UnderlyingType = typename std::conditional<std::is_null_pointer<BasicReturnType>::value, BasicAssignType, BasicReturnType>::type;
-
-	static_assert(std::is_null_pointer<ReturnType>::value || std::is_null_pointer<AssignType>::value || std::is_same<BasicReturnType, BasicAssignType>::value, "Provided getter and setter functions must get and set the same type.");
-
-	return CreateMemberScalarGetSetFuncDefinition<UnderlyingType>(name, member_get_func_ptr, member_set_func_ptr);
-}
-
-template <typename Object>
-template <typename ReturnType, typename AssignType>
-bool StructHandle<Object>::RegisterMemberDetail(const String& name, ReturnType(Object::* member_get_func_ptr)() const, void(Object::* member_set_func_ptr)(AssignType))
-{
-	using BasicReturnType = typename std::remove_cv<typename std::remove_reference<ReturnType>::type>::type;
-	using BasicAssignType = typename std::remove_cv<typename std::remove_reference<AssignType>::type>::type;
-	using UnderlyingType = typename std::conditional<std::is_null_pointer<BasicReturnType>::value, BasicAssignType, BasicReturnType>::type;
-
-	static_assert(std::is_null_pointer<ReturnType>::value || std::is_null_pointer<AssignType>::value || std::is_same<BasicReturnType, BasicAssignType>::value, "Provided getter and setter functions must get and set the same type.");
-
-	return CreateMemberScalarGetSetFuncDefinition<UnderlyingType>(name, member_get_func_ptr, member_set_func_ptr);
-}
-
 template<typename Object>
 template<typename Object>
 template<typename MemberType>
 template<typename MemberType>
 bool StructHandle<Object>::CreateMemberObjectDefinition(const String& name, MemberType Object::* member_ptr)
 bool StructHandle<Object>::CreateMemberObjectDefinition(const String& name, MemberType Object::* member_ptr)
 {
 {
+	using MemberObjectPtr = MemberType Object::*;
+	// If the member function signature doesn't match the getter function signature, it will end up calling this function. Emit a compile error in that case.
+	static_assert(!std::is_member_function_pointer<MemberObjectPtr>::value, "Illegal data member getter function signature. Make sure it takes no arguments and is not const qualified.");
+	static_assert(!std::is_const<MemberType>::value, "Data member objects cannot be const qualified.");
+
 	VariableDefinition* underlying_definition = type_register->GetDefinition<MemberType>();
 	VariableDefinition* underlying_definition = type_register->GetDefinition<MemberType>();
 	if (!underlying_definition)
 	if (!underlying_definition)
 		return false;
 		return false;
@@ -393,6 +329,8 @@ template<typename Object>
 template<typename BasicReturnType, typename MemberType>
 template<typename BasicReturnType, typename MemberType>
 bool StructHandle<Object>::CreateMemberGetFuncDefinition(const String& name, MemberType Object::* member_get_func_ptr)
 bool StructHandle<Object>::CreateMemberGetFuncDefinition(const String& name, MemberType Object::* member_get_func_ptr)
 {
 {
+	static_assert(!std::is_const<BasicReturnType>::value, "Returned type from data member function cannot be const qualified.");
+
 	VariableDefinition* underlying_definition = type_register->GetDefinition<BasicReturnType>();
 	VariableDefinition* underlying_definition = type_register->GetDefinition<BasicReturnType>();
 	if (!underlying_definition)
 	if (!underlying_definition)
 		return false;
 		return false;
@@ -408,8 +346,8 @@ template<typename Object>
 template<typename UnderlyingType, typename MemberGetType, typename MemberSetType>
 template<typename UnderlyingType, typename MemberGetType, typename MemberSetType>
 bool StructHandle<Object>::CreateMemberScalarGetSetFuncDefinition(const String& name, MemberGetType Object::* member_get_func_ptr, MemberSetType Object::* member_set_func_ptr)
 bool StructHandle<Object>::CreateMemberScalarGetSetFuncDefinition(const String& name, MemberGetType Object::* member_get_func_ptr, MemberSetType Object::* member_set_func_ptr)
 {
 {
-	static_assert(std::is_copy_assignable<UnderlyingType>::value, "Struct member getter/setter functions must return/assign a type that is copy assignable.");
 	static_assert(std::is_default_constructible<UnderlyingType>::value, "Struct member getter/setter functions must return/assign a type that is default constructible.");
 	static_assert(std::is_default_constructible<UnderlyingType>::value, "Struct member getter/setter functions must return/assign a type that is default constructible.");
+	static_assert(!std::is_const<UnderlyingType>::value, "Const qualified type illegal in data member getter functions.");
 
 
 	VariableDefinition* underlying_definition = type_register->GetDefinition<UnderlyingType>();
 	VariableDefinition* underlying_definition = type_register->GetDefinition<UnderlyingType>();
 	if (!underlying_definition)
 	if (!underlying_definition)
@@ -417,7 +355,7 @@ bool StructHandle<Object>::CreateMemberScalarGetSetFuncDefinition(const String&
 
 
 	if (underlying_definition->Type() != DataVariableType::Scalar)
 	if (underlying_definition->Type() != DataVariableType::Scalar)
 	{
 	{
-		RMLUI_LOG_TYPE_ERROR(UnderlyingType, "Non-scalar data variable member registered, but only scalar data variables are allowed here: A getter function returning by value, or a getter/setter function pair.");
+		RMLUI_LOG_TYPE_ERROR(UnderlyingType, "Returning or assigning a non-scalar variable by value in a data struct member function is illegal. Only scalar data variables are allowed here: A getter function returning by value, or a getter/setter function pair.");
 		return false;
 		return false;
 	}
 	}
 
 

+ 3 - 3
Include/RmlUi/Core/DataTypes.h

@@ -78,7 +78,7 @@ struct PointerTraits<T*> {
 	using is_pointer = std::true_type;
 	using is_pointer = std::true_type;
 	using element_type = T;
 	using element_type = T;
 	static void* Dereference(void* ptr) {
 	static void* Dereference(void* ptr) {
-		return (void*)*static_cast<T**>(ptr);
+		return static_cast<void*>(*static_cast<T**>(ptr));
 	}
 	}
 };
 };
 template<class T>
 template<class T>
@@ -86,7 +86,7 @@ struct PointerTraits<UniquePtr<T>> {
 	using is_pointer = std::true_type;
 	using is_pointer = std::true_type;
 	using element_type = T;
 	using element_type = T;
 	static void* Dereference(void* ptr) {
 	static void* Dereference(void* ptr) {
-		return (void*)static_cast<UniquePtr<T>*>(ptr)->get();
+		return static_cast<void*>(static_cast<UniquePtr<T>*>(ptr)->get());
 	}
 	}
 };
 };
 template<class T>
 template<class T>
@@ -94,7 +94,7 @@ struct PointerTraits<SharedPtr<T>> {
 	using is_pointer = std::true_type;
 	using is_pointer = std::true_type;
 	using element_type = T;
 	using element_type = T;
 	static void* Dereference(void* ptr) {
 	static void* Dereference(void* ptr) {
-		return (void*)static_cast<SharedPtr<T>*>(ptr)->get();
+		return static_cast<void*>(static_cast<SharedPtr<T>*>(ptr)->get());
 	}
 	}
 };
 };
 
 

+ 9 - 23
Include/RmlUi/Core/DataVariable.h

@@ -101,7 +101,7 @@ public:
 
 
 	bool Get(void* ptr, Variant& variant) override
 	bool Get(void* ptr, Variant& variant) override
 	{
 	{
-		variant = *static_cast<T*>(ptr);
+		variant = *static_cast<const T*>(ptr);
 		return true;
 		return true;
 	}
 	}
 	bool Set(void* ptr, const Variant& variant) override
 	bool Set(void* ptr, const Variant& variant) override
@@ -128,6 +128,7 @@ public:
 		set(variant);
 		set(variant);
 		return true;
 		return true;
 	}
 	}
+
 private:
 private:
 	DataGetFunc get;
 	DataGetFunc get;
 	DataSetFunc set;
 	DataSetFunc set;
@@ -152,6 +153,7 @@ public:
 		set(static_cast<T*>(ptr), variant);
 		set(static_cast<T*>(ptr), variant);
 		return true;
 		return true;
 	}
 	}
+
 private:
 private:
 	DataTypeGetFunc<T> get;
 	DataTypeGetFunc<T> get;
 	DataTypeSetFunc<T> set;
 	DataTypeSetFunc<T> set;
@@ -221,22 +223,6 @@ protected:
 	}
 	}
 };
 };
 
 
-//template<typename T>
-//class ConstPointerDefinition final : public BasePointerDefinition {
-//public:
-//	ConstPointerDefinition(VariableDefinition* underlying_definition) : BasePointerDefinition(underlying_definition) {}
-//
-//	bool Set(void* ptr, const Variant& variant) override
-//	{
-//		// Emit warning
-//		return false;
-//	}
-//	DataVariable Child(void* ptr, const DataAddressEntry& address) override
-//	{
-//		// TODO: Return the constness of T?
-//		return PointerDefinition::Child(ptr, address);
-//	}
-//};
 
 
 class StructDefinition final : public VariableDefinition {
 class StructDefinition final : public VariableDefinition {
 public:
 public:
@@ -288,6 +274,7 @@ protected:
 	{
 	{
 		return &(static_cast<Object*>(base_ptr)->*member_ptr);
 		return &(static_cast<Object*>(base_ptr)->*member_ptr);
 	}
 	}
+
 private:
 private:
 	MemberType Object::* member_ptr;
 	MemberType Object::* member_ptr;
 };
 };
@@ -296,21 +283,21 @@ private:
 template<typename Object, typename MemberType, typename BasicReturnType>
 template<typename Object, typename MemberType, typename BasicReturnType>
 class MemberGetFuncDefinition final : public BasePointerDefinition {
 class MemberGetFuncDefinition final : public BasePointerDefinition {
 public:
 public:
-	using MemberFunc = MemberType Object::*;
 
 
 	MemberGetFuncDefinition(VariableDefinition* underlying_definition, MemberType Object::* member_get_func_ptr)
 	MemberGetFuncDefinition(VariableDefinition* underlying_definition, MemberType Object::* member_get_func_ptr)
 		: BasePointerDefinition(underlying_definition), member_get_func_ptr(member_get_func_ptr)
 		: BasePointerDefinition(underlying_definition), member_get_func_ptr(member_get_func_ptr)
 	{
 	{
+		using MemberFunc = MemberType Object::*;
 		static_assert(std::is_member_function_pointer<MemberFunc>::value, "Must be a member function pointer");
 		static_assert(std::is_member_function_pointer<MemberFunc>::value, "Must be a member function pointer");
 	}
 	}
 
 
 protected:
 protected:
 	void* DereferencePointer(void* base_ptr) override
 	void* DereferencePointer(void* base_ptr) override
 	{
 	{
-		return (void*)Extract((static_cast<Object*>(base_ptr)->*member_get_func_ptr)());
+		return static_cast<void*>(Extract((static_cast<Object*>(base_ptr)->*member_get_func_ptr)()));
 	}
 	}
-private:
 
 
+private:
 	// Pointer return types
 	// Pointer return types
 	BasicReturnType* Extract(BasicReturnType* value) {
 	BasicReturnType* Extract(BasicReturnType* value) {
 		return value;
 		return value;
@@ -343,7 +330,6 @@ public:
 	}
 	}
 
 
 private:
 private:
-
 	template<typename T = MemberGetType, typename std::enable_if<std::is_null_pointer<T>::value, int>::type = 0>
 	template<typename T = MemberGetType, typename std::enable_if<std::is_null_pointer<T>::value, int>::type = 0>
 	bool GetDetail(void* /*ptr*/, Variant& /*variant*/)
 	bool GetDetail(void* /*ptr*/, Variant& /*variant*/)
 	{
 	{
@@ -358,7 +344,7 @@ private:
 
 
 		// TODO: Does this work for pointers?
 		// TODO: Does this work for pointers?
 		auto&& value = (static_cast<Object*>(ptr)->*member_get_func_ptr)();
 		auto&& value = (static_cast<Object*>(ptr)->*member_get_func_ptr)();
-		bool result = underlying_definition->Get((void*)&value, variant);
+		bool result = underlying_definition->Get(static_cast<void*>(&value), variant);
 		return result;
 		return result;
 	}
 	}
 
 
@@ -375,7 +361,7 @@ private:
 			return false;
 			return false;
 
 
 		UnderlyingType result;
 		UnderlyingType result;
-		if (!underlying_definition->Set((void*)&result, variant))
+		if (!underlying_definition->Set(static_cast<void*>(&result), variant))
 			return false;
 			return false;
 
 
 		(static_cast<Object*>(ptr)->*member_set_func_ptr)(result);
 		(static_cast<Object*>(ptr)->*member_set_func_ptr)(result);

+ 164 - 170
Tests/Source/UnitTests/DataBinding.cpp

@@ -66,12 +66,21 @@ static const String document_rml = R"(
 <body template="window">
 <body template="window">
 <div data-model="basics">
 <div data-model="basics">
 
 
-<p>{{ test }}</p>
-<p>{{ str1.val }}</p>
-<p>{{ str2.val }}</p>
-<p>{{ str3.val }}</p>
-<p>{{ test2.val }}</p>
-	
+<input type="text" data-value="i0"/>
+
+<h1>Globals</h1>
+<p>{{ i0 }}</p>
+<p>{{ i1 }}</p>
+<p>{{ i2 }}</p>
+<p>{{ i3 }}</p>
+
+<p>{{ s0 }}</p>
+<p>{{ s1 }}</p>
+<p>{{ s2.val }}</p>
+<p>{{ s3.val }}</p>
+<p>{{ s4.val }}</p>
+<p>{{ s5.val }}</p>
+
 <h1>Basic</h1>
 <h1>Basic</h1>
 <p>{{ basic.a }}</p>
 <p>{{ basic.a }}</p>
 <p>{{ basic.b }}</p>
 <p>{{ basic.b }}</p>
@@ -79,41 +88,25 @@ static const String document_rml = R"(
 <p>{{ basic.d }}</p>
 <p>{{ basic.d }}</p>
 <p>{{ basic.e }}</p>
 <p>{{ basic.e }}</p>
 <p>{{ basic.f }}</p>
 <p>{{ basic.f }}</p>
-<p>{{ basic.g }}</p>
-<p>{{ basic.h }}</p>
-	
+
 <h1>Wrapped</h1>
 <h1>Wrapped</h1>
 <p>{{ wrapped.a.val }}</p>
 <p>{{ wrapped.a.val }}</p>
 <p>{{ wrapped.b.val }}</p>
 <p>{{ wrapped.b.val }}</p>
 <p>{{ wrapped.c.val }}</p>
 <p>{{ wrapped.c.val }}</p>
 <p>{{ wrapped.d.val }}</p>
 <p>{{ wrapped.d.val }}</p>
 <p>{{ wrapped.e.val }}</p>
 <p>{{ wrapped.e.val }}</p>
-<p>{{ wrapped.f.val }}</p>
-<p>{{ wrapped.g.val }}</p>
-<p>{{ wrapped.h.val }}</p>
-	
+
 <h1>Pointed</h1>
 <h1>Pointed</h1>
 <p>{{ pointed.a.val }}</p>
 <p>{{ pointed.a.val }}</p>
-<p>{{ pointed.e.val }}</p>
-<p>{{ pointed.f.val }}</p>
-<p>{{ pointed.g.val }}</p>
-<p>{{ pointed.h.val }}</p>
-	
-<h1>ConstPointed</h1>
-<p>{{ const_pointed.a.val }}</p>
-<p>{{ const_pointed.e.val }}</p>
-<p>{{ const_pointed.f.val }}</p>
-<p>{{ const_pointed.g.val }}</p>
-<p>{{ const_pointed.h.val }}</p>
-	
+<p>{{ pointed.b.val }}</p>
+<p>{{ pointed.c.val }}</p>
+
 <h1>Arrays</h1>
 <h1>Arrays</h1>
 <p><span data-for="arrays.a">{{ it }} </span></p>
 <p><span data-for="arrays.a">{{ it }} </span></p>
 <p><span data-for="arrays.b">{{ it }} </span></p>
 <p><span data-for="arrays.b">{{ it }} </span></p>
-<p><span data-for="arrays.c">{{ it }} </span></p>
+<p><span data-for="arrays.c">{{ it.val }} </span></p>
 <p><span data-for="arrays.d">{{ it.val }} </span></p>
 <p><span data-for="arrays.d">{{ it.val }} </span></p>
 <p><span data-for="arrays.e">{{ it.val }} </span></p>
 <p><span data-for="arrays.e">{{ it.val }} </span></p>
-<p><span data-for="arrays.f">{{ it.val }} </span></p>
-<p><span data-for="arrays.g">{{ it.val }} </span></p>
 
 
 </div>
 </div>
 </body>
 </body>
@@ -126,66 +119,101 @@ struct StringWrap
 	String val;
 	String val;
 };
 };
 
 
-StringWrap gStr0 = StringWrap("obj");
-StringWrap* gStr1 = new StringWrap("raw");
-UniquePtr<StringWrap> gStr2 = MakeUnique<StringWrap>("unique");
-SharedPtr<StringWrap> gStr3 = MakeShared<StringWrap>("shared");
-const StringWrap* gStr4 = new StringWrap("const raw");
-const int* const_ptr_test = new int(5);
+struct Globals
+{
+	int i0 = 0;
+	int* i1 = new int(1);
+	UniquePtr<int> i2 = MakeUnique<int>(2);
+	SharedPtr<int> i3 = MakeShared<int>(3);
+
+	String s0 = "s0";
+	String* s1 = new String("s1");
+	StringWrap s2 = StringWrap("s2");
+	StringWrap* s3 = new StringWrap("s3");
+	UniquePtr<StringWrap> s4 = MakeUnique<StringWrap>("s4");
+	SharedPtr<StringWrap> s5 = MakeShared<StringWrap>("s5");
+
+	// Invalid
+	const int x0 = 100;                                            // Invalid: const variable
+	const int* x1 = new int(101);                                  // Invalid: const pointer
+	UniquePtr<const int> x2 = MakeUnique<int>(102);                // Invalid: const pointer
+	const StringWrap* x3 = new StringWrap("x2");                   // Invalid: const pointer
+	UniquePtr<const StringWrap> x4 = MakeUnique<StringWrap>("x3"); // Invalid: const pointer
+} globals;
 
 
 struct Basic
 struct Basic
 {
 {
 	int a = 1;
 	int a = 1;
 	int* b = new int(2);
 	int* b = new int(2);
-	const int* c = new int(3);
 
 
-	int GetD() {
-		return 4;
+	int GetC() {
+		static int v = 5;
+		return v;
+	}
+	int& GetD() {
+		static int v = 5;
+		return v;
 	}
 	}
-	int& GetE() const {
-		static int e = 5;
-		return e;
+	int* GetE() {
+		static int v = 6;
+		return &v;
 	}
 	}
-	int* GetF() {
-		static int f = 6;
-		return &f;
+	UniquePtr<int> GetF() {
+		return MakeUnique<int>(7);
 	}
 	}
-	const int& GetG() {
+
+	// Invalid: const member
+	const int x0 = 2;
+	// Invalid: const pointer
+	const int* x1 = new int(3);
+	// Invalid: const qualified member function
+	int GetX2() const {
+		return 4;
+	}
+	// Invalid: const reference return
+	const int& GetX3() {
 		static int g = 7;
 		static int g = 7;
 		return g;
 		return g;
 	}
 	}
-	const int* GetH() {
+	// Invalid: const pointer return
+	const int* GetX4() {
 		static int h = 8;
 		static int h = 8;
 		return &h;
 		return &h;
 	}
 	}
-
+	// Invalid: Illegal signature
+	int GetX5(int) {
+		return 9;
+	}
+	// Invalid: Const qualified return type
+	const int GetX6() {
+		return 9;
+	}
 };
 };
 
 
 struct Wrapped
 struct Wrapped
 {
 {
 	StringWrap a = { "a" };
 	StringWrap a = { "a" };
 	StringWrap* b = new StringWrap("b");
 	StringWrap* b = new StringWrap("b");
-	const StringWrap* c = new StringWrap("c");
+	UniquePtr<StringWrap> c = MakeUnique<StringWrap>("c");
 
 
-	// Illegal: Must return by reference, or return scalar value.
-	StringWrap GetD() {
-		return { "d" };
-	}
-	StringWrap& GetE() {
-		static StringWrap e = { "e" };
-		return e;
+	StringWrap& GetD() {
+		static StringWrap v = { "e" };
+		return v;
 	}
 	}
-	StringWrap* GetF() {
-		static StringWrap f = { "f" };
-		return &f;
+	StringWrap* GetE() {
+		static StringWrap v = { "f" };
+		return &v;
 	}
 	}
-	const StringWrap& GetG() {
-		static StringWrap g = { "g" };
-		return g;
+	
+	// Invalid: const pointer
+	const StringWrap* x0 = new StringWrap("x0");
+	// Invalid (run-time): Returning non-scalar variable by value.
+	StringWrap GetX1() {
+		return { "x1" };
 	}
 	}
-	const StringWrap* GetH() {
-		static StringWrap h = { "h" };
-		return &h;
+	// Invalid (run-time): Returning non-scalar variable by value.
+	UniquePtr<StringWrap> GetX2() {
+		return MakeUnique<StringWrap>("x2");
 	}
 	}
 };
 };
 
 
@@ -195,83 +223,45 @@ struct Pointed
 {
 {
 	StringWrapPtr a = MakeUnique<StringWrap>("a");
 	StringWrapPtr a = MakeUnique<StringWrap>("a");
 
 
-	// We disallow recursive pointer types (pointer to pointer)
-	// Invalid: 
-	StringWrapPtr* b = new StringWrapPtr(new StringWrap("b"));
-	const StringWrapPtr* c = new StringWrapPtr(new StringWrap("c"));
-
-	StringWrapPtr GetD() {
-		return MakeUnique<StringWrap>("d");
-	}
-	// -- End Invalid
-
-	StringWrapPtr& GetE() {
-		static StringWrapPtr e = MakeUnique<StringWrap>("e");
-		return e;
-	}
-	StringWrapPtr* GetF() {
-		static StringWrapPtr f = MakeUnique<StringWrap>("f");
-		return &f;
-	}
-	const StringWrapPtr& GetG() {
-		static StringWrapPtr g = MakeUnique<StringWrap>("g");
-		return g;
+	StringWrapPtr& GetB() {
+		static StringWrapPtr v = MakeUnique<StringWrap>("b");
+		return v;
 	}
 	}
-	const StringWrapPtr* GetH() {
-		static StringWrapPtr h = MakeUnique<StringWrap>("h");
-		return &h;
+	StringWrapPtr* GetC() {
+		static StringWrapPtr v = MakeUnique<StringWrap>("c");
+		return &v;
 	}
 	}
-};
-
-using StringWrapConstPtr = UniquePtr<const StringWrap>;
-
-struct ConstPointed
-{
-	StringWrapConstPtr a = MakeUnique<StringWrap>("a");
-
-	// We disallow recursive pointer types (pointer to pointer)
-	// -- Invalid
-	StringWrapConstPtr* b = new StringWrapConstPtr(new StringWrap("b"));
-	const StringWrapConstPtr* c = new StringWrapConstPtr(new StringWrap("c"));
+	
+	// Invalid: We disallow recursive pointer types (pointer to pointer)
+	StringWrapPtr* x0 = new StringWrapPtr(new StringWrap("x0"));
 
 
-	StringWrapConstPtr GetD() {
-		return MakeUnique<StringWrap>("d");
+	// Invalid (run-time error): Only scalar data members can be returned by value
+	StringWrapPtr GetX1() {
+		return MakeUnique<StringWrap>("x1");
 	}
 	}
-	// -- End Invalid
 
 
-	StringWrapConstPtr& GetE() {
-		static StringWrapConstPtr e = MakeUnique<StringWrap>("e");
-		return e;
-	}
-	StringWrapConstPtr* GetF() {
-		static StringWrapConstPtr f = MakeUnique<StringWrap>("f");
-		return &f;
-	}
-	const StringWrapConstPtr& GetG() {
-		static StringWrapConstPtr g = MakeUnique<StringWrap>("g");
-		return g;
-	}
-	const StringWrapConstPtr* GetH() {
-		static StringWrapConstPtr h = MakeUnique<StringWrap>("h");
-		return &h;
-	}
 };
 };
 
 
-struct Arrays {
+struct Arrays
+{
 	Vector<int> a = { 10, 11, 12 };
 	Vector<int> a = { 10, 11, 12 };
 	Vector<int*> b = { new int(20), new int(21), new int(22) };
 	Vector<int*> b = { new int(20), new int(21), new int(22) };
-	Vector<const int*> c = { new int(30), new int(31), new int(32) };
-	Vector<StringWrap> d = { StringWrap("d1"), StringWrap("d2"), StringWrap("d3") };
-	Vector<StringWrap*> e = { new StringWrap("e1"), new StringWrap("e2"), new StringWrap("e3") };
-	Vector<StringWrapPtr> f;
-	Vector<StringWrapConstPtr> g;
+	Vector<StringWrap> c = { StringWrap("c1"), StringWrap("c2"), StringWrap("c3") };
+	Vector<StringWrap*> d = { new StringWrap("d1"), new StringWrap("d2"), new StringWrap("d3") };
+	Vector<StringWrapPtr> e;
+	
+	// Invalid: const pointer
+	Vector<const int*> x0 = { new int(30), new int(31), new int(32) };
+	// Invalid: const pointer
+	Vector<UniquePtr<const StringWrap>> x1;
+	
 	Arrays() {
 	Arrays() {
-		f.emplace_back(MakeUnique<StringWrap>("f1"));
-		f.emplace_back(MakeUnique<StringWrap>("f2"));
-		f.emplace_back(MakeUnique<StringWrap>("f3"));
-		g.emplace_back(MakeUnique<StringWrap>("g1"));
-		g.emplace_back(MakeUnique<StringWrap>("g2"));
-		g.emplace_back(MakeUnique<StringWrap>("g3"));
+		e.emplace_back(MakeUnique<StringWrap>("e1"));
+		e.emplace_back(MakeUnique<StringWrap>("e2"));
+		e.emplace_back(MakeUnique<StringWrap>("e3"));
+		x1.emplace_back(MakeUnique<StringWrap>("x1_1"));
+		x1.emplace_back(MakeUnique<StringWrap>("x1_2"));
+		x1.emplace_back(MakeUnique<StringWrap>("x1_3"));
 	}
 	}
 };
 };
 
 
@@ -290,24 +280,43 @@ bool InitializeDataBindings(Context* context)
 		handle.RegisterMember("val", &StringWrap::val);
 		handle.RegisterMember("val", &StringWrap::val);
 	}
 	}
 
 
-	constructor.Bind("test", &const_ptr_test);
-	constructor.Bind("test2", &gStr4);
-
-	constructor.Bind("str0", &gStr0);
-	constructor.Bind("str1", &gStr1);
-	constructor.Bind("str2", &gStr2);
-	constructor.Bind("str3", &gStr3);
+	{
+		// Globals
+		constructor.Bind("i0", &globals.i0);
+		constructor.Bind("i1", &globals.i1);
+		constructor.Bind("i2", &globals.i2);
+		constructor.Bind("i3", &globals.i3);
+
+		constructor.Bind("s0", &globals.s0);
+		constructor.Bind("s1", &globals.s1);
+		constructor.Bind("s2", &globals.s2);
+		constructor.Bind("s3", &globals.s3);
+		constructor.Bind("s4", &globals.s4);
+		constructor.Bind("s5", &globals.s5);
+
+		// Invalid: Each of the following should give a compile-time failure.
+		//constructor.Bind("x0", &globals.x0);
+		//constructor.Bind("x1", &globals.x1);
+		//constructor.Bind("x2", &globals.x2);
+		//constructor.Bind("x3", &globals.x3);
+		//constructor.Bind("x4", &globals.x4);
+	}
 
 
 	if (auto handle = constructor.RegisterStruct<Basic>())
 	if (auto handle = constructor.RegisterStruct<Basic>())
 	{
 	{
 		handle.RegisterMember("a", &Basic::a);
 		handle.RegisterMember("a", &Basic::a);
 		handle.RegisterMember("b", &Basic::b);
 		handle.RegisterMember("b", &Basic::b);
-		handle.RegisterMember("c", &Basic::c);
+		handle.RegisterMember("c", &Basic::GetC);
 		handle.RegisterMember("d", &Basic::GetD);
 		handle.RegisterMember("d", &Basic::GetD);
 		handle.RegisterMember("e", &Basic::GetE);
 		handle.RegisterMember("e", &Basic::GetE);
 		handle.RegisterMember("f", &Basic::GetF);
 		handle.RegisterMember("f", &Basic::GetF);
-		handle.RegisterMember("g", &Basic::GetG);
-		handle.RegisterMember("h", &Basic::GetH);
+
+		//handle.RegisterMember("x0", &Basic::x0);
+		//handle.RegisterMember("x1", &Basic::x1);
+		//handle.RegisterMember("x2", &Basic::GetX2);
+		//handle.RegisterMember("x3", &Basic::GetX3);
+		//handle.RegisterMember("x4", &Basic::GetX4);
+		//handle.RegisterMember("x6", &Basic::GetX6);
 	}
 	}
 	constructor.Bind("basic", new Basic);
 	constructor.Bind("basic", new Basic);
 	
 	
@@ -316,50 +325,34 @@ bool InitializeDataBindings(Context* context)
 		handle.RegisterMember("a", &Wrapped::a);
 		handle.RegisterMember("a", &Wrapped::a);
 		handle.RegisterMember("b", &Wrapped::b);
 		handle.RegisterMember("b", &Wrapped::b);
 		handle.RegisterMember("c", &Wrapped::c);
 		handle.RegisterMember("c", &Wrapped::c);
-		//handle.RegisterMember("d", &Wrapped::GetD);
+		handle.RegisterMember("d", &Wrapped::GetD);
 		handle.RegisterMember("e", &Wrapped::GetE);
 		handle.RegisterMember("e", &Wrapped::GetE);
-		handle.RegisterMember("f", &Wrapped::GetF);
-		handle.RegisterMember("g", &Wrapped::GetG);
-		handle.RegisterMember("h", &Wrapped::GetH);
+
+		//handle.RegisterMember("x0", &Wrapped::x0);
+		//handle.RegisterMember("x1", &Wrapped::GetX1);
+		//handle.RegisterMember("x2", &Wrapped::GetX2);
 	}
 	}
 	constructor.Bind("wrapped", new Wrapped);
 	constructor.Bind("wrapped", new Wrapped);
 	
 	
 	if (auto handle = constructor.RegisterStruct<Pointed>())
 	if (auto handle = constructor.RegisterStruct<Pointed>())
 	{
 	{
 		handle.RegisterMember("a", &Pointed::a);
 		handle.RegisterMember("a", &Pointed::a);
-		//handle.RegisterMember("b", &Pointed::b);
-		//handle.RegisterMember("c", &Pointed::c);
-		//handle.RegisterMember("d", &Pointed::GetD);
-		handle.RegisterMember("e", &Pointed::GetE);
-		handle.RegisterMember("f", &Pointed::GetF);
-		handle.RegisterMember("g", &Pointed::GetG);
-		handle.RegisterMember("h", &Pointed::GetH);
-	}
-	constructor.Bind("pointed", new Pointed);
+		handle.RegisterMember("b", &Pointed::GetB);
+		handle.RegisterMember("c", &Pointed::GetC);
 
 
-	
-	if (auto handle = constructor.RegisterStruct<ConstPointed>())
-	{
-		handle.RegisterMember("a", &ConstPointed::a);
-		//handle.RegisterMember("b", &ConstPointed::b);
-		//handle.RegisterMember("c", &ConstPointed::c);
-		//handle.RegisterMemberGetter("d", &ConstPointed::GetD);
-		handle.RegisterMember("e", &ConstPointed::GetE);
-		handle.RegisterMember("f", &ConstPointed::GetF);
-		handle.RegisterMember("g", &ConstPointed::GetG);
-		handle.RegisterMember("h", &ConstPointed::GetH);
+		//handle.RegisterMember("x0", &Pointed::x0);
+		//handle.RegisterMember("x1", &Pointed::GetX1);
 	}
 	}
-	constructor.Bind("const_pointed", new ConstPointed);
-
+	constructor.Bind("pointed", new Pointed);
 
 
 	constructor.RegisterArray<decltype(Arrays::a)>();
 	constructor.RegisterArray<decltype(Arrays::a)>();
 	constructor.RegisterArray<decltype(Arrays::b)>();
 	constructor.RegisterArray<decltype(Arrays::b)>();
 	constructor.RegisterArray<decltype(Arrays::c)>();
 	constructor.RegisterArray<decltype(Arrays::c)>();
 	constructor.RegisterArray<decltype(Arrays::d)>();
 	constructor.RegisterArray<decltype(Arrays::d)>();
 	constructor.RegisterArray<decltype(Arrays::e)>();
 	constructor.RegisterArray<decltype(Arrays::e)>();
-	constructor.RegisterArray<decltype(Arrays::f)>();
-	constructor.RegisterArray<decltype(Arrays::g)>();
 
 
+	//constructor.RegisterArray<decltype(Arrays::x0)>();
+	//constructor.RegisterArray<decltype(Arrays::x1)>();
 
 
 	if (auto handle = constructor.RegisterStruct<Arrays>())
 	if (auto handle = constructor.RegisterStruct<Arrays>())
 	{
 	{
@@ -368,8 +361,9 @@ bool InitializeDataBindings(Context* context)
 		handle.RegisterMember("c", &Arrays::c);
 		handle.RegisterMember("c", &Arrays::c);
 		handle.RegisterMember("d", &Arrays::d);
 		handle.RegisterMember("d", &Arrays::d);
 		handle.RegisterMember("e", &Arrays::e);
 		handle.RegisterMember("e", &Arrays::e);
-		handle.RegisterMember("f", &Arrays::f);
-		handle.RegisterMember("g", &Arrays::g);
+
+		//handle.RegisterMember("x0", &Arrays::x0);
+		//handle.RegisterMember("x1", &Arrays::x1);
 	}
 	}
 	constructor.Bind("arrays", new Arrays);
 	constructor.Bind("arrays", new Arrays);