From 251e7e4fd95b4fd8678ccabc09bf167e82f5e095 Mon Sep 17 00:00:00 2001
From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz>
Date: Fri, 15 Nov 2019 12:33:19 +0100
Subject: [PATCH] use optional ref in environment nesting

---
 alib2cli/src/environment/Environment.h        |  11 +-
 alib2std/src/alib/optional                    |   1 +
 .../src/extensions/container/optional.hpp     |   1 +
 .../src/extensions/container/optional_ref.hpp | 278 ++++++++++++++++++
 .../extensions/container/OptionalTest.cpp     |  19 ++
 5 files changed, 308 insertions(+), 2 deletions(-)
 create mode 100644 alib2std/src/extensions/container/optional_ref.hpp
 create mode 100644 alib2std/test-src/extensions/container/OptionalTest.cpp

diff --git a/alib2cli/src/environment/Environment.h b/alib2cli/src/environment/Environment.h
index b7d7fc72d0..351dbb7e67 100644
--- a/alib2cli/src/environment/Environment.h
+++ b/alib2cli/src/environment/Environment.h
@@ -5,6 +5,7 @@
 #include <alib/map>
 #include <alib/memory>
 #include <alib/typeinfo>
+#include <alib/optional>
 
 #include <abstraction/Value.hpp>
 #include <exception/CommonException.h>
@@ -20,7 +21,7 @@ class Environment {
 	ext::map < std::string, std::shared_ptr < abstraction::Value > > m_variables;
 	std::shared_ptr < abstraction::Value > m_result;
 
-	std::unique_ptr < Environment > m_upper;
+	ext::optional_ref < Environment > m_upper;
 
 	std::shared_ptr < abstraction::Value > getVariableInt ( const std::string & name ) const {
 		auto res = m_variables.find ( name );
@@ -41,7 +42,7 @@ class Environment {
 public:
 	Environment ( ) = default;
 
-	Environment ( std::unique_ptr < Environment > upper ) : m_upper ( std::move ( upper ) ) {
+	Environment ( ext::optional_ref < Environment > upper ) : m_upper ( std::move ( upper ) ) {
 	}
 
 	std::string getBinding ( const std::string & name ) const {
@@ -118,6 +119,12 @@ public:
 	cli::CommandResult execute ( std::shared_ptr < cli::LineInterface > lineInterface );
 
 	cli::CommandResult execute_line ( cli::CharSequence charSequence );
+
+	Environment & getGlobalScope ( ) {
+		if ( ! ( bool ) m_upper )
+			return * this;
+		return getGlobalScope ( );
+	}
 };
 
 } /* namespace cli */
diff --git a/alib2std/src/alib/optional b/alib2std/src/alib/optional
index 0d36636307..1f1606cc70 100644
--- a/alib2std/src/alib/optional
+++ b/alib2std/src/alib/optional
@@ -1 +1,2 @@
 #include <extensions/container/optional.hpp>
+#include <extensions/container/optional_ref.hpp>
diff --git a/alib2std/src/extensions/container/optional.hpp b/alib2std/src/extensions/container/optional.hpp
index aafc2beda6..d9b09b69c1 100644
--- a/alib2std/src/extensions/container/optional.hpp
+++ b/alib2std/src/extensions/container/optional.hpp
@@ -27,6 +27,7 @@
 #define __OPTIONAL_HPP_
 
 #include <optional>
+#include <sstream>
 
 #include <extensions/compare.hpp>
 
diff --git a/alib2std/src/extensions/container/optional_ref.hpp b/alib2std/src/extensions/container/optional_ref.hpp
new file mode 100644
index 0000000000..11afe17a68
--- /dev/null
+++ b/alib2std/src/extensions/container/optional_ref.hpp
@@ -0,0 +1,278 @@
+/*
+ * optional.hpp
+ *
+ * This file is part of Algorithms library toolkit.
+ * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)
+
+ * Algorithms library toolkit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * Algorithms library toolkit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Created on: Apr 7, 2016
+ * Author: Jan Travnicek
+ */
+
+#ifndef __OPTIONAL_REF_HPP_
+#define __OPTIONAL_REF_HPP_
+
+#include <optional>
+#include <memory>
+
+namespace ext {
+
+template < class T >
+class optional_ref {
+	T * m_value;
+
+public:
+	optional_ref ( ) : optional_ref ( std::nullopt ) { }
+	optional_ref ( std::nullopt_t ) : m_value ( nullptr ) { }
+
+	optional_ref ( T & val ) : m_value ( std::addressof ( val ) ) {
+	}
+
+	template < class U >
+	optional_ref ( const optional < U > & other ) : optional_ref ( other.value ( ) ) {
+	}
+
+	template < class U >
+	optional_ref ( optional < U > && other ) : optional_ref ( std::move ( other ).value ( ) ) {
+	}
+
+	optional_ref & operator = ( const optional_ref & ) = delete;
+	optional_ref & operator = ( optional_ref && ) = delete;
+
+	optional_ref ( const optional_ref & other ) = default;
+	optional_ref ( optional_ref && other ) noexcept = default;
+
+	~ optional_ref ( ) noexcept = default;
+
+	const T * operator-> ( ) const {
+		return m_value;
+	}
+
+	T * operator-> ( ) {
+		return m_value;
+	}
+
+	const T & operator * ( ) const & {
+		return value ( );
+	}
+
+	T & operator * ( ) & {
+		return value ( );
+	}
+
+	const T && operator * ( ) const && {
+		return std::move ( value ( ) );
+	}
+
+	T && operator * ( ) && {
+		return std::move ( value ( ) );
+	}
+
+	explicit operator bool ( ) const noexcept {
+		return has_value ( );
+	}
+
+	bool has_value ( ) const noexcept {
+		return m_value != nullptr;
+	}
+
+	T & value ( ) & {
+		return * m_value;
+	}
+
+	const T & value ( ) const & {
+		return * m_value;
+	}
+
+	T && value ( ) && {
+		return std::move ( * m_value );
+	}
+
+	const T && value ( ) const && {
+		return std::move ( * m_value );
+	}
+
+	const T & value_or ( const T & default_value ) const & {
+		if ( has_value ( ) )
+			return value ( );
+		else
+			return default_value;
+	}
+
+	T & value_or ( T & default_value ) & {
+		if ( has_value ( ) )
+			return value ( );
+		else
+			return default_value;
+	}
+
+	T && value_or ( T && default_value ) && {
+		if ( has_value ( ) )
+			return std::move ( value ( ) );
+		else
+			return default_value;
+	}
+
+	/**
+	 * \brief
+	 * Compares two optional_ref instances for equvalence.
+	 *
+	 * \param lhs the first instance to compare
+	 * \param rhs the second instance to compare
+	 *
+	 * \return true if the two compared instance are equal, false othervise
+	 */
+	friend bool operator== ( const optional_ref < T >& lhs, const optional_ref < T >& rhs ) {
+		return lhs.value ( ) == rhs.value ( );
+	}
+
+	/**
+	 * \brief
+	 * Compares two optional_ref instances for non equvalence.
+	 *
+	 * \param lhs the first instance to compare
+	 * \param rhs the second instance to compare
+	 *
+	 * \return true if the two compared instance are not equal, false othervise
+	 */
+	friend bool operator!= ( const optional_ref < T >& lhs, const optional_ref < T >& rhs ) {
+		return lhs.value ( ) != rhs.value ( );
+	}
+
+	/**
+	 * \brief
+	 * Compares two optional_ref instances by less relation.
+	 *
+	 * \param lhs the first instance to compare
+	 * \param rhs the second instance to compare
+	 *
+	 * \return true if the first compared instance is less than the other instance, false othervise
+	 */
+	friend bool operator<  ( const optional_ref < T >& lhs, const optional_ref < T >& rhs ) {
+		return lhs.value ( ) < rhs.value ( );
+	}
+
+	/**
+	 * \brief
+	 * Compares two optional_ref instances by less or equal relation.
+	 *
+	 * \param lhs the first instance to compare
+	 * \param rhs the second instance to compare
+	 *
+	 * \return true if the first compared instance is less than or equal than the other instance, false othervise
+	 */
+	friend bool operator<= ( const optional_ref < T >& lhs, const optional_ref < T >& rhs ) {
+		return lhs.value ( ) <= rhs.value ( );
+	}
+
+	/**
+	 * \brief
+	 * Compares two optional_ref instances by greater relation.
+	 *
+	 * \param lhs the first instance to compare
+	 * \param rhs the second instance to compare
+	 *
+	 * \return true if the first compared instance is greater than the other instance, false othervise
+	 */
+	friend bool operator>  ( const optional_ref < T >& lhs, const optional_ref < T >& rhs ) {
+		return lhs.value ( ) > rhs.value ( );
+	}
+
+	/**
+	 * \brief
+	 * Compares two optional_ref instances by greater or equal relation.
+	 *
+	 * \param lhs the first instance to compare
+	 * \param rhs the second instance to compare
+	 *
+	 * \return true if the first compared instance is greater than or equal than the other instance, false othervise
+	 */
+	friend bool operator>= ( const optional_ref < T >& lhs, const optional_ref < T >& rhs ) {
+		return lhs.value ( ) >= rhs.value ( );
+	}
+
+};
+
+/**
+ * \brief
+ * Specialisation of the compare structure implementing the three-way comparison.
+ *
+ * \tparam T type in the optional
+ */
+template < typename T >
+struct compare < ext::optional_ref < T > > {
+
+	/**
+	 * \brief
+	 * Implementation of the three-way comparison.
+	 *
+	 * \param first the left operand of the comparison
+	 * \param second the right operand of the comparison
+	 *
+	 * \return negative value of left < right, positive value if left > right, zero if left == right
+	 */
+	int operator ( ) ( const ext::optional_ref < T > & first, const ext::optional_ref < T > & second ) const {
+		if ( ! first && ! second )
+			return 0;
+		if ( first && ! second )
+			return 1;
+		if ( ! first && second )
+			return -1;
+
+		static ext::compare < T > comp;
+		return comp ( first.value ( ), second.value ( ) );
+	}
+};
+
+/**
+ * \brief
+ * Operator to print the optional to the output stream.
+ *
+ * \param out the output stream
+ * \param optional the optional to print
+ *
+ * \tparam T the type of value inside the optional
+ *
+ * \return the output stream from the \p out
+ */
+template< class T >
+std::ostream & operator << ( std::ostream & out, const ext::optional_ref < T > & optional ) {
+	if ( ! optional )
+		return out << "void";
+	else
+		return out << optional.value ( );
+}
+
+
+/**
+ * \brief
+ * Overload of to_string function.
+ *
+ * \param value the optional to be converted to string
+ *
+ * \tparam T the type of values inside the optional
+ *
+ * \return string representation
+ */
+template < class T >
+std::string to_string ( const ext::optional_ref < T > & value ) {
+	std::stringstream ss;
+	ss << value;
+	return ss.str();
+}
+
+} /* namespace ext */
+
+#endif /* __OPTIONAL_REF_HPP_ */
diff --git a/alib2std/test-src/extensions/container/OptionalTest.cpp b/alib2std/test-src/extensions/container/OptionalTest.cpp
new file mode 100644
index 0000000000..0b40754a63
--- /dev/null
+++ b/alib2std/test-src/extensions/container/OptionalTest.cpp
@@ -0,0 +1,19 @@
+#include <catch2/catch.hpp>
+#include <alib/optional>
+#include <sstream>
+
+TEST_CASE ( "Optional", "[unit][std][container]" ) {
+	SECTION ( "Ref test" ) {
+		int a = 5;
+
+		ext::optional_ref < int > empty;
+		ext::optional_ref < int > a_ref ( a );
+		CHECK ( a_ref.value ( ) == a );
+		a_ref.value ( ) = 10;
+		CHECK ( a == 10 );
+
+		ext::optional_ref < int > a_ref2 ( a_ref );
+		CHECK ( a_ref2 == a_ref );
+	}
+}
+
-- 
GitLab