Explorar el Código

Data variables address parsing and get/set values

Michael Ragazzon hace 6 años
padre
commit
70fb557e0c
Se han modificado 1 ficheros con 237 adiciones y 75 borrados
  1. 237 75
      Samples/basic/databinding/src/main.cpp

+ 237 - 75
Samples/basic/databinding/src/main.cpp

@@ -330,35 +330,35 @@ namespace Data {
 	};
 
 
-	class Instancers;
+	class TypeRegister;
 
 	class InstancerBuilderBase {
 	public:
-		operator bool() const { return instancers && GetInstancer(); }
+		operator bool() const { return type_register && GetInstancer(); }
 
 		virtual VariableInstancer* GetInstancer() const = 0;
 
 	protected:
-		InstancerBuilderBase(Instancers* instancers) : instancers(instancers) {}
-		Instancers* instancers;
+		InstancerBuilderBase(TypeRegister* type_register) : type_register(type_register) {}
+		TypeRegister* type_register;
 	};
 
 	template<typename Object>
 	class InstancerBuilderStruct final : public InstancerBuilderBase {
 	public:
-		InstancerBuilderStruct(Instancers* instancers, StructInstancer* instancer) : InstancerBuilderBase(instancers), instancer(instancer) {}
+		InstancerBuilderStruct(TypeRegister* type_register, StructInstancer* instancer) : InstancerBuilderBase(type_register), instancer(instancer) {}
 
 		template <typename MemberType>
 		InstancerBuilderStruct<Object>& AddMember(const String& name, MemberType Object::* member_ptr) {
 			static_assert(typename is_valid_scalar<MemberType>::value, "Not a valid scalar member type. Did you mean to add a struct member?");
-			VariableInstancer* member_instancer = instancers->GetOrAddScalar<MemberType>();
+			VariableInstancer* member_instancer = type_register->GetOrAddScalar<MemberType>();
 			instancer->AddMember(name, member_ptr, member_instancer);
 			return *this;
 		}
 
 		template <typename MemberType>
 		InstancerBuilderStruct<Object>& AddMember(const String& name, MemberType Object::* member_ptr, const InstancerBuilderBase& member_instancer) {
-			RMLUI_ASSERTMSG(instancers->Get<MemberType>() == member_instancer.GetInstancer(), "Mismatch between member type and provided struct instancer.");
+			RMLUI_ASSERTMSG(type_register->Get<MemberType>() == member_instancer.GetInstancer(), "Mismatch between member type and provided struct instancer.");
 			instancer->AddMember(name, member_ptr, member_instancer.GetInstancer());
 			return *this;
 		}
@@ -373,7 +373,7 @@ namespace Data {
 	template<typename Container>
 	class InstancerBuilderArray final : public InstancerBuilderBase {
 	public:
-		InstancerBuilderArray(Instancers* instancers, ArrayInstancer<Container>* instancer) : InstancerBuilderBase(instancers), instancer(instancer) {}
+		InstancerBuilderArray(TypeRegister* type_register, ArrayInstancer<Container>* instancer) : InstancerBuilderBase(type_register), instancer(instancer) {}
 
 		VariableInstancer* GetInstancer() const override {
 			return instancer;
@@ -385,21 +385,20 @@ namespace Data {
 
 
 
-	class Instancers {
+	class TypeRegister {
 	public:
-
 		template<typename T>
 		InstancerBuilderStruct<T> RegisterStruct() 
 		{
 			int family_id = Family<T>::Id();
 
-			auto result = instancers.emplace(family_id, std::make_unique<StructInstancer>());
+			auto result = type_register.emplace(family_id, std::make_unique<StructInstancer>());
 			auto& it = result.first;
 			bool inserted = result.second;
 			if (!inserted)
 			{
 				RMLUI_ERRORMSG("Type already declared");
-				return  InstancerBuilderStruct<T>(nullptr, nullptr);
+				return InstancerBuilderStruct<T>(nullptr, nullptr);
 			}
 			
 			return InstancerBuilderStruct<T>(this, static_cast<StructInstancer*>(it->second.get()));
@@ -425,7 +424,7 @@ namespace Data {
 			if (!value_instancer)
 			{
 				RMLUI_ERRORMSG("Underlying value type of array has not been registered.");
-				return  InstancerBuilderArray<Container>(nullptr, nullptr);
+				return InstancerBuilderArray<Container>(nullptr, nullptr);
 			}
 
 			return RegisterArray<Container>(value_instancer);
@@ -436,7 +435,7 @@ namespace Data {
 		{
 			int id = Family<T>::Id();
 
-			auto result = instancers.emplace(id, nullptr);
+			auto result = type_register.emplace(id, nullptr);
 			auto& it = result.first;
 			bool inserted = result.second;
 
@@ -452,8 +451,8 @@ namespace Data {
 		VariableInstancer* Get() const
 		{
 			int id = Family<T>::Id();
-			auto it = instancers.find(id);
-			if (it == instancers.end())
+			auto it = type_register.find(id);
+			if (it == type_register.end())
 				return nullptr;
 
 			return it->second.get();
@@ -473,93 +472,187 @@ namespace Data {
 		{
 			int container_id = Family<Container>::Id();
 
-			auto result = instancers.emplace(container_id, std::make_unique<ArrayInstancer<Container>>(value_instancer));
+			auto result = type_register.emplace(container_id, std::make_unique<ArrayInstancer<Container>>(value_instancer));
 			auto& it = result.first;
 			bool inserted = result.second;
 			if (!inserted)
 			{
 				RMLUI_ERRORMSG("Type already declared");
-				return  InstancerBuilderArray<Container>(nullptr, nullptr);
+				return InstancerBuilderArray<Container>(nullptr, nullptr);
 			}
 
 			return InstancerBuilderArray<Container>(this, static_cast<ArrayInstancer<Container>*>(it->second.get()));
 		}
 
-		UnorderedMap<int, UniquePtr<VariableInstancer>> instancers;
+		UnorderedMap<int, UniquePtr<VariableInstancer>> type_register;
 	};
 
 
 
+	class Model {
+	public:
+		Model(TypeRegister* type_register) : type_register(type_register) {}
 
-}
+		template<typename T>
+		bool BindScalar(String name, T* ptr)
+		{
+			return Bind(name, ptr, type_register->GetOrAddScalar<T>());
+		}
 
+		template<typename T>
+		bool BindStruct(String name, T* ptr)
+		{
+			return Bind(name, ptr, type_register->Get<T>());
+		}
 
+		template<typename T>
+		bool BindArray(String name, T* ptr)
+		{
+			return Bind(name, ptr, type_register->Get<T>());
+		}
 
-void TestDataVariable()
-{
-	using namespace Rml::Core;
-	using namespace Data;
+		Variant GetValue(const String& address_str) const;
+		bool SetValue(const String& address_str, const Variant& variant) const;
 
-	using IntVector = std::vector<int>;
+	private:
+		bool Bind(String name, void* ptr, VariableInstancer* instancer);
 
-	struct FunData {
-		int i = 99;
-		String x = "hello";
-		IntVector magic = { 3, 5, 7, 11, 13 };
+		Variable* GetVariable(const String& address_str) const;
+
+		TypeRegister* type_register;
+		UnorderedMap<String, UniquePtr<Variable>> variables;
 	};
 
-	using FunArray = std::array<FunData, 3>;
 
-	struct SmartData {
-		bool valid = true;
-		FunData fun;
-		FunArray more_fun;
+	struct AddressEntry {
+		AddressEntry(String name) : name(name), index(-1) { }
+		AddressEntry(int index) : index(index) { }
+		String name;
+		int index;
 	};
+	using Address = std::vector<AddressEntry>;
+
+
+
+	Address ParseAddress(const String& address_str)
+	{
+		StringList list;
+		StringUtilities::ExpandString(list, address_str, '.');
+
+		Address address;
+		address.reserve(list.size() * 2);
 
-	Instancers instancers;
+		for (const auto& item : list)
+		{
+			if (item.empty())
+				return Address();
+
+			size_t i_open = item.find('[', 0);
+			if (i_open == 0)
+				return Address();
+
+			address.emplace_back(item.substr(0, i_open));
+
+			while (i_open != String::npos)
+			{
+				size_t i_close = item.find(']', i_open + 1);
+				if (i_close == String::npos)
+					return Address();
 
-	auto int_vector_instancer = instancers.RegisterArray<IntVector>();
+				int index = FromString<int>(item.substr(i_open + 1, i_close - i_open), -1);
+				if (index < 0)
+					return Address();
+
+				address.emplace_back(index);
+
+				i_open = item.find('[', i_close + 1);
+			}
+			// TODO: Abort on invalid characters among [ ] and after the last found bracket?
+		}
 
-	auto fun_instancer = instancers.RegisterStruct<FunData>();
-	if (fun_instancer)
+		return address;
+	};
+
+	Variant Model::GetValue(const Rml::Core::String& address_str) const
 	{
-		fun_instancer.AddMember("i", &FunData::i);
-		fun_instancer.AddMember("x", &FunData::x);
-		fun_instancer.AddMember("magic", &FunData::magic, int_vector_instancer);
+		Variable* var = GetVariable(address_str);
+		Variant result;
+
+		if (var && var->Type() == VariableType::Scalar)
+		{
+			if (!static_cast<Scalar*>(var)->Get(result))
+				Log::Message(Log::LT_WARNING, "Could not parse data value '%s'", address_str.c_str());
+		}
+
+		return result;
 	}
 
-	auto fun_array_instancer = instancers.RegisterArray<FunArray>(fun_instancer);
 
-	auto smart_instancer = instancers.RegisterStruct<SmartData>();
-	if (smart_instancer)
+	bool Model::SetValue(const String& address_str, const Variant& variant) const
 	{
-		smart_instancer.AddMember("valid", &SmartData::valid);
-		smart_instancer.AddMember("fun", &SmartData::fun, fun_instancer);
-		smart_instancer.AddMember("more_fun", &SmartData::more_fun, fun_array_instancer);
+		Variable* var = GetVariable(address_str);
+		bool result;
+
+		if (var && var->Type() == VariableType::Scalar)
+		{
+			if (static_cast<Scalar*>(var)->Set(variant))
+				result = true;
+			else
+				Log::Message(Log::LT_WARNING, "Could not assign data value '%s'", address_str.c_str());
+		}
+
+		return result;
 	}
 
+	bool Model::Bind(String name, void* ptr, VariableInstancer* instancer)
 	{
-		SmartData data;
-		data.fun.x = "Hello, we're in SmartData!";
-		UniquePtr<Variable> variable_struct = instancers.Instance(&data);
-
-		struct AddressEntry {
-			AddressEntry(String name) : name(name), index(-1) { }
-			AddressEntry(int index) : index(index) { }
-			String name;
-			int index;
-		};
-		std::vector<AddressEntry> address = { AddressEntry("more_fun"), AddressEntry(1), AddressEntry("magic"), AddressEntry(3) };
+		RMLUI_ASSERT(ptr);
+		if (!instancer)
+		{
+			Log::Message(Log::LT_WARNING, "No instancer could be found for the data variable '%s'.", name.c_str());
+			return false;
+		}
+
+		UniquePtr<Variable> variable = instancer->Instance(ptr);
+		if (!variable)
+		{
+			Log::Message(Log::LT_WARNING, "Could not instance data variable '%s'.", name.c_str());
+			return false;
+		}
+
+		bool inserted = variables.emplace(name, std::move(variable)).second;
+		if (!inserted)
+		{
+			Log::Message(Log::LT_WARNING, "Data model variable with name '%s' already exists.", name.c_str());
+			return false;
+		}
+
+		return true;
+	}
+
+	Variable* Model::GetVariable(const String& address_str) const
+	{
+		Address address = ParseAddress(address_str);
 
+		if (address.empty() || address.front().name.empty())
+		{
+			Log::Message(Log::LT_WARNING, "Invalid data address '%s'.", address_str.c_str());
+			return nullptr;
+		}
 
-		Variable* var = variable_struct.get();
-		for (int i = 0; i < (int)address.size(); i++)
+		auto it = variables.find(address.front().name);
+		if (it == variables.end())
+		{
+			Log::Message(Log::LT_WARNING, "Could not find the data variable '%s'.", address_str.c_str());
+			return nullptr;
+		}
+
+		Variable* var = it->second.get();
+		for (int i = 1; i < (int)address.size(); i++)
 		{
 			if (!var)
-			{
-				RMLUI_ERROR;
 				break;
-			}
+
 			const AddressEntry& entry = address[i];
 
 			switch (var->Type()) {
@@ -577,36 +670,105 @@ void TestDataVariable()
 			}
 			case VariableType::Scalar:
 			{
-				Log::Message(Log::LT_WARNING, "Invalid data variable address. The scalar variable '%s' was encountered, but it needs to be the final entry of an address.", (i > 0 ? address[i - 1].name.c_str() : ""));
+				Log::Message(Log::LT_WARNING, "Invalid data variable address '%s'. The scalar variable '%s' was encountered before the end of the address.", address_str.c_str(), (i > 0 ? address[i - 1].name.c_str() : ""));
 				var = nullptr;
+				return nullptr;
 				break;
 			}
 			default:
 				RMLUI_ERROR;
 				var = nullptr;
+				return nullptr;
 			}
 		}
 
-		bool success = false;
+		if (!var)
+		{
+			Log::Message(Log::LT_WARNING, "Could not find the data variable '%s'.", address_str.c_str());
+			return nullptr;
+		}
 
-		if (var && var->Type() == VariableType::Scalar)
+		return var;
+	}
+
+
+}
+
+
+
+void TestDataVariable()
+{
+	using namespace Rml::Core;
+	using namespace Data;
+
+	using IntVector = std::vector<int>;
+
+	struct FunData {
+		int i = 99;
+		String x = "hello";
+		IntVector magic = { 3, 5, 7, 11, 13 };
+	};
+
+	using FunArray = std::array<FunData, 3>;
+
+	struct SmartData {
+		bool valid = true;
+		FunData fun;
+		FunArray more_fun;
+	};
+
+	TypeRegister types;
+
+	{
+		auto int_vector_instancer = types.RegisterArray<IntVector>();
+
+		auto fun_instancer = types.RegisterStruct<FunData>();
+		if (fun_instancer)
 		{
-			Variant variant;
-			if (static_cast<Scalar*>(var)->Get(variant))
-			{
-				String result = variant.Get<String>();
-				success = true;
-			}
+			fun_instancer.AddMember("i", &FunData::i);
+			fun_instancer.AddMember("x", &FunData::x);
+			fun_instancer.AddMember("magic", &FunData::magic, int_vector_instancer);
 		}
 
-		if (!success)
+		auto fun_array_instancer = types.RegisterArray<FunArray>(fun_instancer);
+
+		auto smart_instancer = types.RegisterStruct<SmartData>();
+		if (smart_instancer)
 		{
-			RMLUI_ERRORMSG("Could not get value");
+			smart_instancer.AddMember("valid", &SmartData::valid);
+			smart_instancer.AddMember("fun", &SmartData::fun, fun_instancer);
+			smart_instancer.AddMember("more_fun", &SmartData::more_fun, fun_array_instancer);
 		}
 	}
 
-}
+	Model model(&types);
+
+	SmartData data;
+	data.fun.x = "Hello, we're in SmartData!";
+
+	model.BindStruct("data", &data);
+
+	{
+		std::vector<String> test_addresses = { "data.more_fun[1].magic[3]", "data.fun.x", "data.valid" };
+		std::vector<String> expected_results = { ToString(data.more_fun[1].magic[3]), ToString(data.fun.x), ToString(data.valid) };
+
+		std::vector<String> results;
 
+		for(auto& address : test_addresses)
+		{
+			auto the_address = ParseAddress(address);
+
+			Variant variant = model.GetValue(address);
+			results.push_back(variant.Get<String>());
+		}
+
+		RMLUI_ASSERT(results == expected_results);
+
+		bool success = model.SetValue("data.more_fun[1].magic[1]", Variant(String("199")));
+		RMLUI_ASSERT(success && data.more_fun[1].magic[1] == 199);
+	}
+
+}