From 4f433e9e491a64b468dbf3c1caf22bdd170453fd Mon Sep 17 00:00:00 2001
From: Nicolas Pope <nwpope@utu.fi>
Date: Sun, 7 Jun 2020 21:16:00 +0300
Subject: [PATCH] Add reference returns

---
 .../structures/include/ftl/data/new_frame.hpp |  60 ++++++--
 components/structures/test/frame_unit.cpp     | 130 ++++++++++++++----
 2 files changed, 153 insertions(+), 37 deletions(-)

diff --git a/components/structures/include/ftl/data/new_frame.hpp b/components/structures/include/ftl/data/new_frame.hpp
index caec76a6d..68960b975 100644
--- a/components/structures/include/ftl/data/new_frame.hpp
+++ b/components/structures/include/ftl/data/new_frame.hpp
@@ -31,11 +31,16 @@ class Frame {
 		return changed_.find(c) != changed_.end();
 	}
 
+	inline const std::unordered_set<ftl::codecs::Channel> &changed() const { return changed_; }
+
 	template <typename T>
 	bool isType(ftl::codecs::Channel c);
 
 	template <typename T>
-	const T *get(ftl::codecs::Channel c) const;
+	const T &get(ftl::codecs::Channel c) const;
+
+	template <typename T>
+	const T *getPtr(ftl::codecs::Channel c) const;
 
 	template <typename T>
 	T *getMutable(ftl::codecs::Channel c);
@@ -49,10 +54,13 @@ class Frame {
 	}
 
 	template <typename T>
-	void create(ftl::codecs::Channel c, const T &value);
+	T &create(ftl::codecs::Channel c, const T &value);
 
 	template <typename T>
-	void create(ftl::codecs::Channel c);
+	T &create(ftl::codecs::Channel c);
+
+	template <typename T, typename ...ARGS>
+	T &emplace(ftl::codecs::Channel, ARGS...);
 
 	template <typename T>
 	void push(ftl::codecs::Channel c, const T &v);
@@ -69,9 +77,22 @@ class Frame {
 		any_triggers_.push_back(cb);
 	}
 
+	void merge(Frame &);
+
+	void swap(Frame &);
+
+	void swapChannel(ftl::codecs::Channel, Frame &);
+
+	inline void reset() { changed_.clear(); }
+
+	void clear();
+
 	/** Send changes back through origin stream. */
 	bool flush();
 
+	inline auto begin() const { return data_.begin(); }
+	inline auto end() const { return data_.end(); }
+
 	// onBeginFlush
 	// onEndFlush
 	// onError
@@ -98,25 +119,46 @@ bool ftl::data::Frame::isType(ftl::codecs::Channel c) {
 }
 
 template <typename T>
-const T *ftl::data::Frame::get(ftl::codecs::Channel c) const {
+const T *ftl::data::Frame::getPtr(ftl::codecs::Channel c) const {
 	auto i = data_.find(c);
 	if (i != data_.end()) {
 		return std::any_cast<T>(&i->second);
 	} else if (parent_) {
-		return parent_->get<T>(c);
+		return parent_->getPtr<T>(c);
 	} else return nullptr;
 }
 
 template <typename T>
-void ftl::data::Frame::create(ftl::codecs::Channel c, const T &value) {
-	data_[c] = value;
+const T &ftl::data::Frame::get(ftl::codecs::Channel c) const {
+	auto i = data_.find(c);
+	if (i != data_.end()) {
+		auto *p = std::any_cast<T>(&i->second);
+		if (!p) throw FTL_Error("'get' wrong type for channel (" << static_cast<unsigned int>(c) << ")");
+		return *p;
+	} else if (parent_) {
+		return parent_->get<T>(c);
+	} else throw FTL_Error("'get' on missing channel (" << static_cast<unsigned int>(c) << ")");
+}
+
+template <typename T>
+T &ftl::data::Frame::create(ftl::codecs::Channel c, const T &value) {
 	touch(c);
+	auto &d = data_[c];
+	d = value;
+	return *std::any_cast<T>(&d);
 }
 
 template <typename T>
-void ftl::data::Frame::create(ftl::codecs::Channel c) {
-	if (!isType<T>(c)) data_[c] = T{};
+T &ftl::data::Frame::create(ftl::codecs::Channel c) {
+	touch(c);
+	if (!isType<T>(c)) return data_[c].emplace<T>();
+	else return *std::any_cast<T>(&data_[c]);
+}
+
+template <typename T, typename ...ARGS>
+T &ftl::data::Frame::emplace(ftl::codecs::Channel c, ARGS... args) {
 	touch(c);
+	return data_[c].emplace<T>(std::forward<ARGS...>(args...));
 }
 
 template <typename T>
diff --git a/components/structures/test/frame_unit.cpp b/components/structures/test/frame_unit.cpp
index a866b8e44..af9c9387d 100644
--- a/components/structures/test/frame_unit.cpp
+++ b/components/structures/test/frame_unit.cpp
@@ -11,18 +11,16 @@ TEST_CASE("ftl::data::Frame create get", "[Frame]") {
 		Frame f;
 		f.create<int>(Channel::Pose, 55);
 
-		auto x = f.get<int>(Channel::Pose);
-		REQUIRE( x );
-		REQUIRE( *x == 55 );
+		const auto &x = f.get<int>(Channel::Pose);
+		REQUIRE( x == 55 );
 	}
 
 	SECTION("write and read floats") {
 		Frame f;
 		f.create<float>(Channel::Pose, 44.0f);
 
-		auto x = f.get<float>(Channel::Pose);
-		REQUIRE( x );
-		REQUIRE( *x == 44.0f );
+		const auto &x = f.get<float>(Channel::Pose);
+		REQUIRE( x == 44.0f );
 	}
 
 	SECTION("write and read structures") {
@@ -33,10 +31,9 @@ TEST_CASE("ftl::data::Frame create get", "[Frame]") {
 		Frame f;
 		f.create<Test>(Channel::Pose, {});
 
-		auto x = f.get<Test>(Channel::Pose);
-		REQUIRE( x );
-		REQUIRE( x->a == 44 );
-		REQUIRE( x->b == 33.0f );
+		const auto &x = f.get<Test>(Channel::Pose);
+		REQUIRE( x.a == 44 );
+		REQUIRE( x.b == 33.0f );
 	}
 
 	SECTION("write and read fail") {
@@ -47,8 +44,15 @@ TEST_CASE("ftl::data::Frame create get", "[Frame]") {
 		Frame f;
 		f.create<Test>(Channel::Pose, {});
 
-		auto x = f.get<int>(Channel::Pose);
-		REQUIRE( !x );
+		bool err = false;
+
+		try {
+			int x = f.get<int>(Channel::Pose);
+			REQUIRE(x);
+		} catch (...) {
+			err = true;
+		}
+		REQUIRE(err);
 	}
 }
 
@@ -104,28 +108,24 @@ TEST_CASE("ftl::data::Frame create", "[Frame]") {
 		Frame f;
 
 		f.create<int>(Channel::Pose, 55);
-		auto x = f.get<int>(Channel::Pose);
-		REQUIRE( x );
-		REQUIRE( *x == 55 );
+		const auto &x = f.get<int>(Channel::Pose);
+		REQUIRE( x == 55 );
 
 		f.create<int>(Channel::Pose);
-		auto y = f.get<int>(Channel::Pose);
-		REQUIRE( y );
-		REQUIRE( *y == 55 );
-
-		REQUIRE( x == y );
+		const auto &y = f.get<int>(Channel::Pose);
+		REQUIRE( y == 55 );
 	}
 
 	SECTION("change of type") {
 		Frame f;
 
 		f.create<int>(Channel::Pose, 55);
-		auto x = f.get<int>(Channel::Pose);
+		auto x = f.getPtr<int>(Channel::Pose);
 		REQUIRE( x );
 		REQUIRE( *x == 55 );
 
 		f.create<float>(Channel::Pose);
-		auto y = f.get<float>(Channel::Pose);
+		auto y = f.getPtr<float>(Channel::Pose);
 		REQUIRE( y );
 		REQUIRE( *y == 0.0f );
 	}
@@ -138,11 +138,11 @@ TEST_CASE("ftl::data::Frame use of parent", "[Frame]") {
 
 		p.create<int>(Channel::Pose, 55);
 
-		auto x = f.get<int>(Channel::Pose);
+		auto x = f.getPtr<int>(Channel::Pose);
 		REQUIRE( x );
 		REQUIRE( *x == 55 );
 
-		auto y = p.get<int>(Channel::Pose);
+		auto y = p.getPtr<int>(Channel::Pose);
 		REQUIRE( x == y );
 	}
 
@@ -169,11 +169,11 @@ TEST_CASE("ftl::data::Frame use of parent", "[Frame]") {
 		REQUIRE( f.changed(Channel::Pose) );
 		REQUIRE( !p.changed(Channel::Pose) );
 
-		auto x = f.get<int>(Channel::Pose);
+		auto x = f.getPtr<int>(Channel::Pose);
 		REQUIRE( x );
 		REQUIRE( *x == 66 );
 
-		auto y = p.get<int>(Channel::Pose);
+		auto y = p.getPtr<int>(Channel::Pose);
 		REQUIRE( y );
 		REQUIRE( *y == 55 );
 	}
@@ -221,12 +221,12 @@ TEST_CASE("ftl::data::Frame flush", "[Frame]") {
 		p.flush();
 
 		f.set<int>(Channel::Pose, 66);
-		auto x = p.get<int>(Channel::Pose);
+		auto x = p.getPtr<int>(Channel::Pose);
 		REQUIRE( x );
 		REQUIRE( *x == 55 );
 		
 		f.flush();
-		x = p.get<int>(Channel::Pose);
+		x = p.getPtr<int>(Channel::Pose);
 		REQUIRE( x );
 		REQUIRE( *x == 66 );
 	}
@@ -241,3 +241,77 @@ TEST_CASE("ftl::data::Frame flush", "[Frame]") {
 		REQUIRE( !f.changed(Channel::Pose) );
 	}
 }
+
+// ==== Complex type overload test =============================================
+
+struct TestA {
+	int a=55;
+};
+
+struct TestB {
+	int b=99;
+};
+
+struct TestC {
+	TestA a;
+	TestB b;
+};
+
+template <>
+TestA &ftl::data::Frame::create<TestA>(ftl::codecs::Channel c) {
+	return create<TestC>(c).a;
+}
+
+template <>
+TestA &ftl::data::Frame::create<TestA>(ftl::codecs::Channel c, const TestA &a) {
+	TestC cc;
+	cc.a = a;
+	return create<TestC>(c, cc).a;
+}
+
+template <>
+TestB &ftl::data::Frame::create<TestB>(ftl::codecs::Channel c, const TestB &b) {
+	TestC cc;
+	cc.b = b;
+	return create<TestC>(c, cc).b;
+}
+
+template <>
+const TestA *ftl::data::Frame::getPtr<TestA>(ftl::codecs::Channel c) const {
+	auto *ptr = getPtr<TestC>(c);
+	return (ptr) ? &ptr->a : nullptr;
+}
+
+template <>
+const TestB *ftl::data::Frame::getPtr<TestB>(ftl::codecs::Channel c) const {
+	auto *ptr = getPtr<TestC>(c);
+	return (ptr) ? &ptr->b : nullptr;
+}
+
+TEST_CASE("ftl::data::Frame Complex Overload", "[Frame]") {
+	SECTION("Create and get first type with default") {
+		Frame f;
+		f.create<TestA>(Channel::Pose);
+		
+		auto *x = f.getPtr<TestA>(Channel::Pose);
+		REQUIRE( x );
+		REQUIRE( x->a == 55 );
+
+		auto *y = f.getPtr<TestB>(Channel::Pose);
+		REQUIRE( y );
+		REQUIRE( y->b == 99 );
+	}
+
+	SECTION("Create and get first type with value") {
+		Frame f;
+		f.create<TestA>(Channel::Pose, {77});
+		
+		auto *x = f.getPtr<TestA>(Channel::Pose);
+		REQUIRE( x );
+		REQUIRE( x->a == 77 );
+
+		auto *y = f.getPtr<TestB>(Channel::Pose);
+		REQUIRE( y );
+		REQUIRE( y->b == 99 );
+	}
+}
-- 
GitLab