Browse Source

Support for pointer types in data bindings, automatically unwrapping and dereferencing the underlying type

Co-authored-by: Omegapl <[email protected]>
Michael Ragazzon 4 years ago
parent
commit
929715dd7c

+ 2 - 2
Include/RmlUi/Core/DataModelHandle.h

@@ -70,9 +70,9 @@ public:
 	template<typename T>
 	bool Bind(const String& name, T* ptr) {
 		RMLUI_ASSERTMSG(ptr, "Invalid pointer to data variable");
-		return BindVariable(name, DataVariable(type_register->GetOrAddScalar<T>(), ptr));
+		return BindVariable(name, DataVariable(type_register->GetDefinition<T>(), (void*)ptr));
 	}
-
+	
 	// Bind a get/set function pair.
 	bool BindFunc(const String& name, DataGetFunc get_func, DataSetFunc set_func = {});
 

+ 57 - 22
Include/RmlUi/Core/DataTypeRegister.h

@@ -81,7 +81,8 @@ private:
 
 
 
-class RMLUICORE_API DataTypeRegister : NonCopyMoveable {
+
+class RMLUICORE_API DataTypeRegister final : NonCopyMoveable {
 public:
 	DataTypeRegister();
 	~DataTypeRegister();
@@ -92,10 +93,10 @@ public:
 		static_assert(std::is_class<T>::value, "Type must be a struct or class type.");
 		FamilyId id = Family<T>::Id();
 
-		auto struct_variable = MakeUnique<StructDefinition>();
-		StructDefinition* struct_variable_raw = struct_variable.get();
+		auto struct_definition = MakeUnique<StructDefinition>();
+		StructDefinition* struct_variable_raw = struct_definition.get();
 
-		bool inserted = type_register.emplace(id, std::move(struct_variable)).second;
+		bool inserted = type_register.emplace(id, std::move(struct_definition)).second;
 		if (!inserted)
 		{
 			RMLUI_LOG_TYPE_ERROR(T, "Type already declared");
@@ -109,16 +110,16 @@ public:
 	bool RegisterArray()
 	{
 		using value_type = typename Container::value_type;
-		VariableDefinition* value_variable = GetOrAddScalar<value_type>();
+		VariableDefinition* value_variable = GetDefinitionDetail<value_type>();
 		RMLUI_LOG_TYPE_ERROR_ASSERT(value_type, value_variable, "Underlying value type of array has not been registered.");
 		if (!value_variable)
 			return false;
 
 		FamilyId container_id = Family<Container>::Id();
 
-		auto array_variable = MakeUnique<ArrayDefinition<Container>>(value_variable);
+		auto array_definition = MakeUnique<ArrayDefinition<Container>>(value_variable);
 
-		bool inserted = type_register.emplace(container_id, std::move(array_variable)).second;
+		bool inserted = type_register.emplace(container_id, std::move(array_definition)).second;
 		if (!inserted)
 		{
 			RMLUI_LOG_TYPE_ERROR(Container, "Array type already declared.");
@@ -161,8 +162,22 @@ public:
 		return it->second.get();
 	}
 
-	template<typename T, typename std::enable_if<is_valid_data_scalar<T>::value, int>::type = 0>
-	VariableDefinition* GetOrAddScalar()
+	template<typename T>
+	VariableDefinition* GetDefinition()
+	{
+		return GetDefinitionDetail<T>();
+	}
+
+	TransformFuncRegister* GetTransformFuncRegister() {
+		return &transform_register;
+	}
+
+private:
+
+	// 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.
+	template<typename T, typename std::enable_if<!PointerTraits<T>::is_pointer::value&& is_valid_data_scalar<T>::value, int>::type = 0>
+	VariableDefinition* GetDefinitionDetail()
 	{
 		FamilyId id = Family<T>::Id();
 
@@ -176,14 +191,10 @@ public:
 		return definition.get();
 	}
 
-	template<typename T, typename std::enable_if<!is_valid_data_scalar<T>::value, int>::type = 0>
-	VariableDefinition* GetOrAddScalar()
-	{
-		return Get<T>();
-	}
-
-	template<typename T>
-	VariableDefinition* Get()
+	// Get definition for non-scalar types.
+	// These must already have been registered by the user.
+	template<typename T, typename std::enable_if<!PointerTraits<T>::is_pointer::value && !is_valid_data_scalar<T>::value, int>::type = 0>
+	VariableDefinition* GetDefinitionDetail()
 	{
 		FamilyId id = Family<T>::Id();
 		auto it = type_register.find(id);
@@ -196,21 +207,45 @@ public:
 		return it->second.get();
 	}
 
-	TransformFuncRegister* GetTransformFuncRegister() {
-		return &transform_register;
+	// Get definition for pointer types, or create one as needed.
+	// This will create a wrapper definition that forwards the call to the definition for the underlying type.
+	template<typename T, typename std::enable_if<PointerTraits<T>::is_pointer::value, int>::type = 0>
+	VariableDefinition* GetDefinitionDetail()
+	{
+		static_assert(PointerTraits<T>::is_pointer::value, "Not a valid pointer type.");
+		static_assert(!PointerTraits<PointerTraits<T>::element_type>::is_pointer::value, "Recursive pointer type (pointer to pointer) detected.");
+
+		// Get the underlying definition.
+		VariableDefinition* underlying_definition = GetDefinitionDetail<typename std::remove_cv<PointerTraits<T>::element_type>::type>();
+		if (!underlying_definition)
+		{
+			RMLUI_LOG_TYPE_ERROR(T, "Underlying type of pointer not registered.");
+			return nullptr;
+		}
+
+		// Get or create the pointer wrapper definition.
+		FamilyId id = Family<T>::Id();
+
+		auto result = type_register.emplace(id, nullptr);
+		bool inserted = result.second;
+		UniquePtr<VariableDefinition>& definition = result.first->second;
+
+		if (inserted)
+			definition = MakeUnique<PointerDefinition<T>>(underlying_definition);
+
+		return definition.get();
 	}
 
-private:
+
 	UnorderedMap<FamilyId, UniquePtr<VariableDefinition>> type_register;
 
 	TransformFuncRegister transform_register;
-
 };
 
 template<typename Object>
 template<typename MemberType>
 inline StructHandle<Object>& StructHandle<Object>::RegisterMember(const String& name, MemberType Object::* member_ptr) {
-	VariableDefinition* member_type = type_register->GetOrAddScalar<MemberType>();
+	VariableDefinition* member_type = type_register->GetDefinition<MemberType>();
 	struct_definition->AddMember(name, MakeUnique<StructMemberObject<Object, MemberType>>(member_type, member_ptr));
 	return *this;
 }

+ 22 - 0
Include/RmlUi/Core/DataTypes.h

@@ -61,5 +61,27 @@ struct DataAddressEntry {
 };
 using DataAddress = Vector<DataAddressEntry>;
 
+template<class T>
+struct PointerTraits {
+	using is_pointer = std::false_type;
+	using element_type = T;
+};
+template<class T>
+struct PointerTraits<T*> {
+	using is_pointer = std::true_type;
+	using element_type = T;
+};
+template<class T>
+struct PointerTraits<UniquePtr<T>> {
+	using is_pointer = std::true_type;
+	using element_type = T;
+};
+template<class T>
+struct PointerTraits<SharedPtr<T>> {
+	using is_pointer = std::true_type;
+	using element_type = T;
+};
+
+
 } // namespace Rml
 #endif

+ 43 - 1
Include/RmlUi/Core/DataVariable.h

@@ -110,7 +110,6 @@ public:
 	}
 };
 
-
 class FuncDefinition final : public VariableDefinition {
 public:
 
@@ -196,6 +195,49 @@ private:
 	VariableDefinition* underlying_definition;
 };
 
+template<typename T>
+class PointerDefinition final : public VariableDefinition {
+public:
+	PointerDefinition(VariableDefinition* underlying_definition) : VariableDefinition(underlying_definition->Type()), underlying_definition(underlying_definition) {}
+
+	bool Get(void* ptr, Variant& variant) override
+	{
+		return underlying_definition->Get(DereferencePointer(ptr), variant);
+	}
+	bool Set(void* ptr, const Variant& variant) override
+	{
+		return SetDetail<T>(ptr, variant);
+	}
+	int Size(void* ptr) override
+	{
+		return underlying_definition->Size(DereferencePointer(ptr));
+	}
+	DataVariable Child(void* ptr, const DataAddressEntry& address) override
+	{
+		// TODO: Return the constness of T?
+		return underlying_definition->Child(DereferencePointer(ptr), address);
+	}
+
+private:
+	template<typename U, typename std::enable_if<!std::is_const<typename PointerTraits<U>::element_type>::value, int>::type = 0>
+	bool SetDetail(void* ptr, const Variant& variant)
+	{
+		return underlying_definition->Set(DereferencePointer(ptr), variant);
+	}
+	template<typename U, typename std::enable_if<std::is_const<typename PointerTraits<U>::element_type>::value, int>::type = 0>
+	bool SetDetail(void* /*ptr*/, const Variant& /*variant*/)
+	{
+		return false;
+	}
+
+	static void* DereferencePointer(void* ptr)
+	{
+		return (void*)(&(*(*static_cast<T*>(ptr))));
+	}
+
+	VariableDefinition* underlying_definition;
+};
+
 
 class StructMember {
 public: