#include #include #include #include #include #include #include struct Variable_Error : public std::runtime_error { using std::runtime_error::runtime_error; }; class Variable { private: using Number = double; using Text = std::string; using List = std::vector; public: Variable(Number number = 0.0) : value { number } {} Variable(Text const& text) : value { text } {} Variable(List const& list) : value { list } {} Variable& operator=(Number number) { value = number; return *this; } Variable& operator=(Text const& text) { value = text; return *this; } Variable& operator=(List const& list) { value = list; return *this; } Variable operator+(Variable const& other) const { Variable result { }; ensure_compatible(other); if (std::holds_alternative(value)) result = std::get(value) + std::get(other.value); else if (std::holds_alternative(value)) result = std::get(value) + std::get(other.value); else { List a { std::get(value) }; List b { std::get(other.value) }; a.insert(std::end(a), std::begin(b), std::end(b)); result = std::move(a); } return result; } Variable operator-(Variable const& other) const { Variable result { }; ensure_compatible(other); if (std::holds_alternative(value)) { result = std::get(value) - std::get(other.value); return result; } throw Variable_Error { "operator- only defined for numbers" }; } std::size_t size() const { if (std::holds_alternative(value)) throw Variable_Error { "size() only defined for texts and lists" }; else if (std::holds_alternative(value)) return std::get(value).size(); else return std::get(value).size(); } void print(std::ostream& os) const { if (std::holds_alternative(value)) os << std::get(value); else if (std::holds_alternative(value)) os << '"' << std::get(value) << '"'; else { List const& list { std::get(value) }; os << "[ "; for (Variable const& var : list) { var.print(os); os << " "; } os << "]"; } } private: void ensure_compatible(Variable const& other) const { if (value.index() != other.value.index()) throw Variable_Error { "Incompatible types" }; } std::variant value { }; }; int main() { Variable v1 { 3.0 }; Variable v2 { 5.0 }; { std::ostringstream oss { }; (v1 + v2).print(oss); assert( oss.str() == "8" ); } // ensure that v1.size() throws a Variable_Error exception try { v1.size(); assert(false); } catch (Variable_Error& error) { } catch (...) { assert(false); } { std::ostringstream oss { }; (v2 - v1).print(oss); assert( oss.str() == "2" ); } v2 = "my text"; assert( v2.size() == 7 ); // ensure that variables of different types throws a Variable_Error exception try { (v1 + v2); assert(false); } catch (Variable_Error& error) { } catch (...) { assert(false); } // operator- should not work for anyting but numbers try { (v2 - v2); assert(false); } catch (Variable_Error& error) { } catch (...) { assert(false); } Variable v3 { { v1, v2 } }; assert( v3.size() == 2 ); { std::ostringstream oss { }; v3.print(oss); assert( oss.str() == "[ 3 \"my text\" ]" ); } v1 = { v1, v2, v3 }; assert( v1.size() == 3 ); { std::ostringstream oss { }; v1.print(oss); assert( oss.str() == "[ 3 \"my text\" [ 3 \"my text\" ] ]" ); } }