Refactor of EvaluationValue class 47/165047/47
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Fri, 22 Dec 2017 16:09:02 +0000 (17:09 +0100)
committerRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Tue, 23 Jan 2018 11:37:11 +0000 (12:37 +0100)
Minor refactor of EvaluationValue class in preparation to switch to
std::variant.

Change-Id: I6aac2f1f1bc61c885da405e776464385eb4a5350

src/batch/BatchRunner.cpp
src/batch/EvaluationContext.cpp
src/batch/EvaluationContext.hpp
src/batch/EvaluationValue.cpp
src/batch/EvaluationValue.hpp
src/batch/Evaluator.cpp
tests/no-ui-scenarios/BatchExecTests.cpp

index b226532..39f38a2 100644 (file)
@@ -194,8 +194,8 @@ template <typename T, typename MONITORED_TYPE> MONITORED_TYPE executeOnMainThrea
 
 namespace std
 {
-       template <> struct hash<std::pair<EvaluationValue::Kind, std::string>> {
-               size_t operator()(const std::pair<EvaluationValue::Kind, std::string> &p) const
+       template <> struct hash<std::pair<detail::Kind, std::string>> {
+               size_t operator()(const std::pair<detail::Kind, std::string> &p) const
                {
                        return std::hash<std::string>()(p.second) ^ (unsigned char)p.first;
                }
@@ -207,8 +207,8 @@ struct TestExecutor : ExecutorInterface {
        std::shared_ptr<UIElement> root;
        std::shared_ptr<NavigationElement> navigationContext;
        std::unordered_map<std::string, EvaluationValue> variables;
-       std::unordered_map<std::pair<EvaluationValue::Kind, std::string>, std::function<EvaluationValue(const EvaluationValue &, EvaluationValue)>> setters;
-       std::unordered_map<std::pair<EvaluationValue::Kind, std::string>, std::function<EvaluationValue(const EvaluationValue &)>> getters;
+       std::unordered_map<std::pair<detail::Kind, std::string>, std::function<EvaluationValue(const EvaluationValue &, EvaluationValue)>> setters;
+       std::unordered_map<std::pair<detail::Kind, std::string>, std::function<EvaluationValue(const EvaluationValue &)>> getters;
        std::mutex mt;
        std::ostream &output;
 
@@ -219,7 +219,7 @@ struct TestExecutor : ExecutorInterface {
        {
                insertRoleConstants();
                insertStateConstants();
-               variables["sleep"] = EvaluationValue::UserFunctionType<double> {
+               variables["sleep"] = detail::UserFunctionType<double> {
                        [&](EvaluationContext & ec, double tm) -> EvaluationValue {
                                if (tm > 0)
                                {
@@ -229,12 +229,12 @@ struct TestExecutor : ExecutorInterface {
                                return {};
                        }
                };
-               variables["get_at_point"] = EvaluationValue::UserFunctionType<Point> {
+               variables["get_at_point"] = detail::UserFunctionType<Point> {
                        [&](EvaluationContext & ec, Point pt) -> EvaluationValue {
                                return convert(pt);
                        }
                };
-               variables["find_by_name"] = EvaluationValue::UserFunctionType<std::string, std::vector<int>, std::vector<int>> {
+               variables["find_by_name"] = detail::UserFunctionType<std::string, std::vector<int>, std::vector<int>> {
                        [&](EvaluationContext & ec, std::string name, std::vector<int> roles, std::vector<int> states) -> EvaluationValue {
                                auto root = getVisibleRoot();
                                if (!root) throw EvaluationFailure{} << "no visible root (context changed didn't happen)";
@@ -255,8 +255,8 @@ struct TestExecutor : ExecutorInterface {
                                }, monitor);
                        }
                };
-               getters[ { EvaluationValue::Kind::UIELEMENT, "name" }] = [&](const EvaluationValue & self) -> EvaluationValue {
-                       auto uiElem = self.asUiElement();
+               getters[ { detail::Kind::UIELEMENT, "name" }] = [&](const EvaluationValue & self) -> EvaluationValue {
+                       auto uiElem = self.as<std::shared_ptr<UIElement>>();
                        ASSERT(uiElem->getObject());
                        return executeOnMainThread([&]()
                        {
@@ -268,8 +268,8 @@ struct TestExecutor : ExecutorInterface {
                                throw EvaluationFailure{} << "failed to get at-spi object's name (use dlogutil to get at-spi error message)";
                        });
                };
-               getters[ { EvaluationValue::Kind::UIELEMENT, "middle_point" }] = [&](const EvaluationValue & self) -> EvaluationValue {
-                       auto uiElem = self.asUiElement();
+               getters[ { detail::Kind::UIELEMENT, "middle_point" }] = [&](const EvaluationValue & self) -> EvaluationValue {
+                       auto uiElem = self.as<std::shared_ptr<UIElement>>();
                        ASSERT(uiElem->getObject());
                        return executeOnMainThread([&]()
                        {
@@ -284,7 +284,7 @@ struct TestExecutor : ExecutorInterface {
                        });
                };
                for (auto activityName : ActivityFactory::getInstance()->getAllActivityTypes()) {
-                       variables[activityName] = EvaluationValue::FunctionType::Type{
+                       variables[activityName] = detail::FunctionType::Type{
                                [ = ](EvaluationContext & ec, std::vector<EvaluationValue> args) -> EvaluationValue {
                                        auto activity = executeOnMainThread([&]()
                                        {
index f80088a..d656b08 100644 (file)
 
 #include "EvaluationContext.hpp"
 
+using detail::VectorType;
+using detail::SetType;
+using detail::FunctionType;
+using detail::DictType;
+
 namespace std
 {
-       template <> struct hash<std::pair<EvaluationValue::Kind, EvaluationValue::Kind>> {
-               size_t operator()(std::pair<EvaluationValue::Kind, EvaluationValue::Kind> p) const
+       template <> struct hash<std::pair<detail::Kind, detail::Kind>> {
+               size_t operator()(std::pair<detail::Kind, detail::Kind> p) const
                {
                        return ((unsigned char)p.first << 8) | (unsigned char)p.second;
                }
@@ -76,23 +81,23 @@ EvaluationValue EvaluationContext::callFunction(const Optional<TokenLocation> &l
        Optional<EvaluationValue> v;
 
        switch (function.kind()) {
-       case EvaluationValue::Kind::FUNCTION:
+       case detail::Kind::FUNCTION:
                try {
-                       return function.asFunctionType()(*this, std::move(values));
+                       return function.as<detail::FunctionType>()(*this, std::move(values));
                } catch (EvaluationFailure &e) {
                        if (loc) e.setLocationIfMissing(*loc);
                        throw;
                }
-       case EvaluationValue::Kind::STRING:
-       case EvaluationValue::Kind::INTEGER:
-       case EvaluationValue::Kind::DOUBLE:
-       case EvaluationValue::Kind::UIELEMENT:
-       case EvaluationValue::Kind::BOOLEAN:
-       case EvaluationValue::Kind::POINT:
-       case EvaluationValue::Kind::EMPTY:
-       case EvaluationValue::Kind::VECTOR:
-       case EvaluationValue::Kind::SET:
-       case EvaluationValue::Kind::DICT:
+       case detail::Kind::STRING:
+       case detail::Kind::INTEGER:
+       case detail::Kind::DOUBLE:
+       case detail::Kind::UIELEMENT:
+       case detail::Kind::BOOLEAN:
+       case detail::Kind::POINT:
+       case detail::Kind::EMPTY:
+       case detail::Kind::VECTOR:
+       case detail::Kind::SET:
+       case detail::Kind::DICT:
                break;
        }
        throw EvaluationFailure{loc} << "value of kind " << EvaluationValue::toString(function.kind()) <<
@@ -146,30 +151,30 @@ EvaluationValue EvaluationContext::setAttribute(const Optional<TokenLocation> &l
        }
 }
 
-EvaluationValue EvaluationContext::convert(const Optional<TokenLocation> &loc, const EvaluationValue &value, EvaluationValue::Kind targetType)
+EvaluationValue EvaluationContext::convert(const Optional<TokenLocation> &loc, const EvaluationValue &value, detail::Kind targetType)
 {
        if (value.kind() == targetType) return value;
 
        try {
                switch (value.kind()) {
-               case EvaluationValue::Kind::FUNCTION:
-               case EvaluationValue::Kind::STRING:
+               case detail::Kind::FUNCTION:
+               case detail::Kind::STRING:
                        return detail::ConvertTo<std::string>::convert(*this, value);
-               case EvaluationValue::Kind::INTEGER:
+               case detail::Kind::INTEGER:
                        return detail::ConvertTo<int>::convert(*this, value);
-               case EvaluationValue::Kind::DOUBLE:
+               case detail::Kind::DOUBLE:
                        return detail::ConvertTo<double>::convert(*this, value);
-               case EvaluationValue::Kind::UIELEMENT:
+               case detail::Kind::UIELEMENT:
                        return detail::ConvertTo<std::shared_ptr<UIElement>>::convert(*this, value);
-               case EvaluationValue::Kind::BOOLEAN:
+               case detail::Kind::BOOLEAN:
                        return detail::ConvertTo<bool>::convert(*this, value);
-               case EvaluationValue::Kind::POINT:
+               case detail::Kind::POINT:
                        return detail::ConvertTo<Point>::convert(*this, value);
-               case EvaluationValue::Kind::EMPTY:
+               case detail::Kind::EMPTY:
                        return {};
-               case EvaluationValue::Kind::VECTOR:
-               case EvaluationValue::Kind::SET:
-               case EvaluationValue::Kind::DICT:
+               case detail::Kind::VECTOR:
+               case detail::Kind::SET:
+               case detail::Kind::DICT:
                        break;
                }
        } catch (EvaluationFailure &e) {
@@ -177,7 +182,7 @@ EvaluationValue EvaluationContext::convert(const Optional<TokenLocation> &loc, c
                throw;
        }
        throw EvaluationFailure{loc} << "can't convert from " << EvaluationValue::toString(value.kind()) <<
-                                                                " to " << EvaluationValue::toString(EvaluationValue::Kind::FUNCTION);
+                                                                " to " << EvaluationValue::toString(detail::Kind::FUNCTION);
 }
 
 int EvaluationContext::getSingleIndex(const std::vector<EvaluationValue> &args, int size, std::string typeName)
@@ -211,42 +216,42 @@ EvaluationValue EvaluationContext::evaluateAccessGet(const Optional<TokenLocatio
                ASSERT(!args.empty());
                auto &self = args[0];
                switch (self.kind()) {
-               case EvaluationValue::Kind::STRING:
+               case detail::Kind::STRING:
                        if (args.size() == 2) {
-                               auto index = getSingleIndex(args, static_cast<int>(self.asString().size()), "string");
-                               return self.asString().substr(index, 1);
+                               auto index = getSingleIndex(args, static_cast<int>(self.as<std::string>().size()), "string");
+                               return self.as<std::string>().substr(index, 1);
                        } else if (args.size() == 3) {
-                               auto indexes = getDoubleIndexes(args, static_cast<int>(self.asString().size()));
-                               return self.asString().substr(indexes.first, indexes.second - indexes.first);
+                               auto indexes = getDoubleIndexes(args, static_cast<int>(self.as<std::string>().size()));
+                               return self.as<std::string>().substr(indexes.first, indexes.second - indexes.first);
                        }
                        break;
-               case EvaluationValue::Kind::FUNCTION:
-               case EvaluationValue::Kind::INTEGER:
-               case EvaluationValue::Kind::DOUBLE:
-               case EvaluationValue::Kind::UIELEMENT:
-               case EvaluationValue::Kind::BOOLEAN:
-               case EvaluationValue::Kind::POINT:
-               case EvaluationValue::Kind::EMPTY:
-               case EvaluationValue::Kind::SET:
+               case detail::Kind::FUNCTION:
+               case detail::Kind::INTEGER:
+               case detail::Kind::DOUBLE:
+               case detail::Kind::UIELEMENT:
+               case detail::Kind::BOOLEAN:
+               case detail::Kind::POINT:
+               case detail::Kind::EMPTY:
+               case detail::Kind::SET:
                        break;
-               case EvaluationValue::Kind::VECTOR:
+               case detail::Kind::VECTOR:
                        if (args.size() == 2) {
-                               auto index = getSingleIndex(args, static_cast<int>(self.asVector().size()), "vector");
-                               return self.asVector()[index];
+                               auto index = getSingleIndex(args, static_cast<int>(self.as<VectorType>().size()), "vector");
+                               return self.as<VectorType>()[index];
                        } else if (args.size() == 3) {
-                               auto indexes = getDoubleIndexes(args, static_cast<int>(self.asVector().size()));
+                               auto indexes = getDoubleIndexes(args, static_cast<int>(self.as<VectorType>().size()));
                                std::vector<EvaluationValue> tmp;
                                tmp.reserve(indexes.second - indexes.first);
                                for (auto i = indexes.first; i < indexes.second; ++i)
-                                       tmp.push_back(self.asVector()[i]);
+                                       tmp.push_back(self.as<VectorType>()[i]);
                                return std::move(tmp);
                        }
                        break;
-               case EvaluationValue::Kind::DICT:
+               case detail::Kind::DICT:
                        if (args.size() == 2) {
                                auto key = EvaluationValueKey{ args[1] };
-                               auto it = self.asDict().find(key);
-                               if (it == self.asDict().end()) throw EvaluationFailure{loc} << "key not found";
+                               auto it = self.as<detail::DictType>().find(key);
+                               if (it == self.as<detail::DictType>().end()) throw EvaluationFailure{loc} << "key not found";
                                return it->second;
                        }
                        break;
@@ -272,35 +277,35 @@ EvaluationValue EvaluationContext::evaluateAccessSet(const Optional<TokenLocatio
                ASSERT(!args.empty());
                auto &self = args[0];
                switch (self.kind()) {
-               case EvaluationValue::Kind::STRING:
-               case EvaluationValue::Kind::FUNCTION:
-               case EvaluationValue::Kind::INTEGER:
-               case EvaluationValue::Kind::DOUBLE:
-               case EvaluationValue::Kind::UIELEMENT:
-               case EvaluationValue::Kind::BOOLEAN:
-               case EvaluationValue::Kind::POINT:
-               case EvaluationValue::Kind::EMPTY:
-               case EvaluationValue::Kind::SET:
+               case detail::Kind::STRING:
+               case detail::Kind::FUNCTION:
+               case detail::Kind::INTEGER:
+               case detail::Kind::DOUBLE:
+               case detail::Kind::UIELEMENT:
+               case detail::Kind::BOOLEAN:
+               case detail::Kind::POINT:
+               case detail::Kind::EMPTY:
+               case detail::Kind::SET:
                        break;
-               case EvaluationValue::Kind::VECTOR:
+               case detail::Kind::VECTOR:
                        if (args.size() == 3) {
-                               auto index = getSingleIndex(args, static_cast<int>(self.asVector().size()), "vector");
-                               self.asVector()[index] = args[2];
+                               auto index = getSingleIndex(args, static_cast<int>(self.as<VectorType>().size()), "vector");
+                               self.as<VectorType>()[index] = args[2];
                                return std::move(args[2]);
-                       } else if (args.size() == 4 && args[3].kind() == EvaluationValue::Kind::VECTOR) {
-                               auto indexes = getDoubleIndexes(args, static_cast<int>(self.asVector().size()));
+                       } else if (args.size() == 4 && args[3].kind() == detail::Kind::VECTOR) {
+                               auto indexes = getDoubleIndexes(args, static_cast<int>(self.as<VectorType>().size()));
                                std::vector<EvaluationValue> tmp;
-                               tmp.reserve(self.asVector().size() - (indexes.second - indexes.first) + args[3].asVector().size());
-                               for (auto i = 0; i < indexes.first; ++i) tmp.push_back(std::move(self.asVector()[i]));
-                               for (auto &v : args[3].asVector()) tmp.push_back(v);
-                               for (auto i = indexes.second; i < static_cast<int>(self.asVector().size()); ++i) tmp.push_back(std::move(self.asVector()[i]));
-                               self.asVector() = std::move(tmp);
+                               tmp.reserve(self.as<VectorType>().size() - (indexes.second - indexes.first) + args[3].as<VectorType>().size());
+                               for (auto i = 0; i < indexes.first; ++i) tmp.push_back(std::move(self.as<VectorType>()[i]));
+                               for (auto &v : args[3].as<VectorType>()) tmp.push_back(v);
+                               for (auto i = indexes.second; i < static_cast<int>(self.as<VectorType>().size()); ++i) tmp.push_back(std::move(self.as<VectorType>()[i]));
+                               self.as<VectorType>() = std::move(tmp);
                                return {};
                        }
                        break;
-               case EvaluationValue::Kind::DICT:
+               case detail::Kind::DICT:
                        if (args.size() == 3) {
-                               self.asDict()[std::move(args[1])] = args[2];
+                               self.as<DictType>()[std::move(args[1])] = args[2];
                                return std::move(args[2]);
                        }
                        break;
@@ -325,19 +330,19 @@ EvaluationValue EvaluationContext::evaluateMinus(const Optional<TokenLocation> &
 {
        try {
                switch (self.kind()) {
-               case EvaluationValue::Kind::INTEGER:
-                       return -self.asInteger();
-               case EvaluationValue::Kind::DOUBLE:
-                       return -self.asDouble();
-               case EvaluationValue::Kind::STRING:
-               case EvaluationValue::Kind::SET:
-               case EvaluationValue::Kind::DICT:
-               case EvaluationValue::Kind::VECTOR:
-               case EvaluationValue::Kind::FUNCTION:
-               case EvaluationValue::Kind::UIELEMENT:
-               case EvaluationValue::Kind::BOOLEAN:
-               case EvaluationValue::Kind::POINT:
-               case EvaluationValue::Kind::EMPTY:
+               case detail::Kind::INTEGER:
+                       return -self.as<int>();
+               case detail::Kind::DOUBLE:
+                       return -self.as<double>();
+               case detail::Kind::STRING:
+               case detail::Kind::SET:
+               case detail::Kind::DICT:
+               case detail::Kind::VECTOR:
+               case detail::Kind::FUNCTION:
+               case detail::Kind::UIELEMENT:
+               case detail::Kind::BOOLEAN:
+               case detail::Kind::POINT:
+               case detail::Kind::EMPTY:
                        break;
                }
        } catch (EvaluationFailure &e) {
@@ -351,27 +356,27 @@ bool EvaluationContext::evaluateIn(const Optional<TokenLocation> &loc, const Eva
 {
        try {
                switch (self.kind()) {
-               case EvaluationValue::Kind::STRING: {
+               case detail::Kind::STRING: {
                        auto val = detail::ConvertTo<std::string>::convert(*this, value);
-                       return self.asString().find(val) != std::string::npos;
+                       return self.as<std::string>().find(val) != std::string::npos;
                }
-               case EvaluationValue::Kind::SET: {
-                       return self.asSet().find(value) != self.asSet().end();
+               case detail::Kind::SET: {
+                       return self.as<SetType>().find(value) != self.as<SetType>().end();
                }
-               case EvaluationValue::Kind::DICT: {
-                       return self.asDict().find(value) != self.asDict().end();
+               case detail::Kind::DICT: {
+                       return self.as<DictType>().find(value) != self.as<DictType>().end();
                }
-               case EvaluationValue::Kind::VECTOR:
-                       for (auto &v : self.asVector())
+               case detail::Kind::VECTOR:
+                       for (auto &v : self.as<VectorType>())
                                if (v == value) return true;
-                       return true;
-               case EvaluationValue::Kind::FUNCTION:
-               case EvaluationValue::Kind::INTEGER:
-               case EvaluationValue::Kind::DOUBLE:
-               case EvaluationValue::Kind::UIELEMENT:
-               case EvaluationValue::Kind::BOOLEAN:
-               case EvaluationValue::Kind::POINT:
-               case EvaluationValue::Kind::EMPTY:
+                       return false;
+               case detail::Kind::FUNCTION:
+               case detail::Kind::INTEGER:
+               case detail::Kind::DOUBLE:
+               case detail::Kind::UIELEMENT:
+               case detail::Kind::BOOLEAN:
+               case detail::Kind::POINT:
+               case detail::Kind::EMPTY:
                        break;
                }
        } catch (EvaluationFailure &e) {
index e4b35c3..4e48104 100644 (file)
@@ -52,7 +52,7 @@ public:
        EvaluationValue callFunction(const Optional<TokenLocation> &loc, EvaluationValue function, std::vector<EvaluationValue> values);
        EvaluationValue getAttribute(const Optional<TokenLocation> &loc, EvaluationValue self, const std::string &identifier);
        EvaluationValue setAttribute(const Optional<TokenLocation> &loc, EvaluationValue self, const std::string &identifier, EvaluationValue value);
-       EvaluationValue convert(const Optional<TokenLocation> &loc, const EvaluationValue &value, EvaluationValue::Kind targetType);
+       EvaluationValue convert(const Optional<TokenLocation> &loc, const EvaluationValue &value, detail::Kind targetType);
        EvaluationValue evaluateAccessGet(const Optional<TokenLocation> &loc, const std::vector<EvaluationValue> &args);
        EvaluationValue evaluateAccessSet(const Optional<TokenLocation> &loc, std::vector<EvaluationValue> args);
        bool evaluateIn(const Optional<TokenLocation> &loc, const EvaluationValue &self, const EvaluationValue &value);
index 9b893e5..d95a485 100644 (file)
@@ -44,90 +44,68 @@ TokenLocation EvaluationFailure::location()
        return *location_;
 }
 
-std::string EvaluationValue::toString(Kind k)
+std::string EvaluationValue::toString(detail::Kind k)
 {
        switch (k) {
-       case Kind::EMPTY:
+       case detail::Kind::EMPTY:
                return "EMPTY";
-       case Kind::STRING:
+       case detail::Kind::STRING:
                return "STRING";
-       case Kind::INTEGER:
+       case detail::Kind::INTEGER:
                return "INTEGER";
-       case Kind::DOUBLE:
+       case detail::Kind::DOUBLE:
                return "DOUBLE";
-       case Kind::UIELEMENT:
+       case detail::Kind::UIELEMENT:
                return "UIELEMENT";
-       case Kind::BOOLEAN:
+       case detail::Kind::BOOLEAN:
                return "BOOLEAN";
-       case Kind::POINT:
+       case detail::Kind::POINT:
                return "POINT";
-       case Kind::FUNCTION:
+       case detail::Kind::FUNCTION:
                return "FUNCTION";
-       case Kind::VECTOR:
+       case detail::Kind::VECTOR:
                return "VECTOR";
-       case Kind::SET:
+       case detail::Kind::SET:
                return "SET";
-       case Kind::DICT:
+       case detail::Kind::DICT:
                return "DICT";
        }
        return "";
 }
 
-EvaluationValue::EvaluationValue()
-{
-}
+detail::Data::Data(int t) : integer(t) { }
+detail::Data::Data(double t) : double_(t) { }
+detail::Data::Data(bool t) : boolean(t) { }
+detail::Data::Data(std::string t) : string(std::move(t)) { }
+detail::Data::Data(std::shared_ptr<UIElement> t) : uiElement(std::move(t)) { }
+detail::Data::Data(Point t) : point(t) { }
+detail::Data::Data(FunctionType t) : function(std::move(t)) { }
+detail::Data::Data(VectorType t) : vector(std::make_shared<VectorType>(std::move(t))) { }
+detail::Data::Data(SetType t) : set(std::make_shared<SetType>(std::move(t))) { }
+detail::Data::Data(DictType t) : dict(std::make_shared<DictType>(std::move(t))) { }
 
-EvaluationValue::Data::Data(int t) : integer(t) { }
-EvaluationValue::Data::Data(double t) : double_(t) { }
-EvaluationValue::Data::Data(bool t) : boolean(t) { }
-EvaluationValue::Data::Data(std::string t) : string(std::move(t)) { }
-EvaluationValue::Data::Data(std::shared_ptr<UIElement> t) : uiElement(std::move(t)) { }
-EvaluationValue::Data::Data(Point t) : point(t) { }
-EvaluationValue::Data::Data(FunctionType t) : function(std::move(t.function)) { }
-EvaluationValue::Data::Data(std::shared_ptr<VectorType> t) : vector(std::move(t)) { }
-EvaluationValue::Data::Data(std::shared_ptr<SetType> t) : set(std::move(t)) { }
-EvaluationValue::Data::Data(std::shared_ptr<DictType> t) : dict(std::move(t)) { }
-
-EvaluationValue::EvaluationValue(std::nullptr_t) {}
-EvaluationValue::EvaluationValue(int integer) : data(integer), kind_(Kind::INTEGER) { }
-EvaluationValue::EvaluationValue(double double_) : data(double_), kind_(Kind::DOUBLE) { }
-EvaluationValue::EvaluationValue(bool boolean) : data(boolean), kind_(Kind::BOOLEAN) { }
-EvaluationValue::EvaluationValue(std::string string) : data(std::move(string)), kind_(Kind::STRING) { }
-EvaluationValue::EvaluationValue(std::shared_ptr<UIElement> e) : data(std::move(e)), kind_(Kind::UIELEMENT)
-{
-       ASSERT(data.uiElement);
-}
-EvaluationValue::EvaluationValue(Point p) : data(p), kind_(Kind::POINT) { }
-EvaluationValue::EvaluationValue(FunctionType f) : data(std::move(f)), kind_(Kind::FUNCTION)
-{
-       ASSERT(data.function);
-}
-EvaluationValue::EvaluationValue(FunctionType::Type f) : EvaluationValue(FunctionType{std::move(f)}) { }
-EvaluationValue::EvaluationValue(VectorType f) : data(std::make_shared<VectorType>(std::move(f))), kind_(Kind::VECTOR)  { }
-EvaluationValue::EvaluationValue(SetType f) : data(std::make_shared<SetType>(std::move(f))), kind_(Kind::SET)  { }
-EvaluationValue::EvaluationValue(DictType f) : data(std::make_shared<DictType>(std::move(f))), kind_(Kind::DICT)  { }
 
 size_t EvaluationValueKey::calculateHash(const EvaluationValue &self)
 {
        switch (self.kind()) {
-       case EvaluationValue::Kind::STRING:
-               return std::hash<std::string>()(self.asString());
-       case EvaluationValue::Kind::INTEGER:
-               return std::hash<int>()(self.asInteger());
-       case EvaluationValue::Kind::DOUBLE:
-               return std::hash<double>()(self.asDouble());
-       case EvaluationValue::Kind::UIELEMENT:
-               return std::hash<std::string>()(Atspi::getUniqueId(self.asUiElement()->getObject()));
-       case EvaluationValue::Kind::BOOLEAN:
-               return std::hash<bool>()(self.asBoolean());
-       case EvaluationValue::Kind::POINT:
-               return std::hash<int>()(self.asPoint().x) ^ std::hash<int>()(self.asPoint().y);
-       case EvaluationValue::Kind::EMPTY:
+       case detail::Kind::STRING:
+               return std::hash<std::string>()(self.as<std::string>());
+       case detail::Kind::INTEGER:
+               return std::hash<int>()(self.as<int>());
+       case detail::Kind::DOUBLE:
+               return std::hash<double>()(self.as<double>());
+       case detail::Kind::UIELEMENT:
+               return std::hash<std::string>()(Atspi::getUniqueId(self.as<std::shared_ptr<UIElement>>()->getObject()));
+       case detail::Kind::BOOLEAN:
+               return std::hash<bool>()(self.as<bool>());
+       case detail::Kind::POINT:
+               return (std::hash<size_t>()(self.as<Point>().x) << 16) ^ std::hash<size_t>()(self.as<Point>().y);
+       case detail::Kind::EMPTY:
                return 0;
-       case EvaluationValue::Kind::FUNCTION:
-       case EvaluationValue::Kind::SET:
-       case EvaluationValue::Kind::VECTOR:
-       case EvaluationValue::Kind::DICT:
+       case detail::Kind::FUNCTION:
+       case detail::Kind::SET:
+       case detail::Kind::VECTOR:
+       case detail::Kind::DICT:
                break;
        }
        throw EvaluationFailure{} << "can't calculate hash for value of type " << EvaluationValue::toString(self.kind());
@@ -148,75 +126,34 @@ bool EvaluationValueKey::operator == (const EvaluationValueKey &k) const
        return getValue() == k.getValue();
 }
 
-const std::string &EvaluationValue::asString() const
-{
-       return data.string;
-}
-int EvaluationValue::asInteger() const
-{
-       return data.integer;
-}
-const std::shared_ptr<UIElement> &EvaluationValue::asUiElement() const
-{
-       return data.uiElement;
-}
-bool EvaluationValue::asBoolean() const
-{
-       return data.boolean;
-}
-Point EvaluationValue::asPoint() const
-{
-       return data.point;
-}
-double EvaluationValue::asDouble() const
-{
-       return data.double_;
-}
-const EvaluationValue::FunctionType::Type &EvaluationValue::asFunctionType() const
-{
-       return data.function;
-}
-EvaluationValue::VectorType &EvaluationValue::asVector() const
-{
-       return *data.vector;
-}
-EvaluationValue::SetType &EvaluationValue::asSet() const
-{
-       return *data.set;
-}
-EvaluationValue::DictType &EvaluationValue::asDict() const
-{
-       return *data.dict;
-}
-
 std::ostream &operator << (std::ostream &s, const EvaluationValue &v)
 {
        switch (v.kind()) {
-       case EvaluationValue::Kind::EMPTY:
+       case detail::Kind::EMPTY:
                s << "{}";
                break;
-       case EvaluationValue::Kind::STRING:
+       case detail::Kind::STRING:
                s << "'" << v.data.string << "'";
                break;
-       case EvaluationValue::Kind::INTEGER:
+       case detail::Kind::INTEGER:
                s << v.data.integer;
                break;
-       case EvaluationValue::Kind::DOUBLE:
+       case detail::Kind::DOUBLE:
                s << v.data.double_;
                break;
-       case EvaluationValue::Kind::UIELEMENT:
+       case detail::Kind::UIELEMENT:
                s << Atspi::getUniqueId(v.data.uiElement->getObject());
                break;
-       case EvaluationValue::Kind::BOOLEAN:
+       case detail::Kind::BOOLEAN:
                s << (v.data.boolean ? "true" : "false");
                break;
-       case EvaluationValue::Kind::POINT:
+       case detail::Kind::POINT:
                s << "<" << v.data.point.x << ", " << v.data.point.y << ">";
                break;
-       case EvaluationValue::Kind::FUNCTION:
+       case detail::Kind::FUNCTION:
                s << "functor";
                break;
-       case EvaluationValue::Kind::VECTOR: {
+       case detail::Kind::VECTOR: {
                s << "[";
                bool first = true;
                for (auto &a : *v.data.vector) {
@@ -231,7 +168,7 @@ std::ostream &operator << (std::ostream &s, const EvaluationValue &v)
                s << " ]";
                break;
        }
-       case EvaluationValue::Kind::SET: {
+       case detail::Kind::SET: {
                s << "{";
                bool first = true;
                for (auto &a : *v.data.set) {
@@ -246,7 +183,7 @@ std::ostream &operator << (std::ostream &s, const EvaluationValue &v)
                s << " }";
                break;
        }
-       case EvaluationValue::Kind::DICT: {
+       case detail::Kind::DICT: {
                s << "{";
                bool first = true;
                for (auto &a : *v.data.dict) {
@@ -270,184 +207,43 @@ std::ostream &operator << (std::ostream &s, const EvaluationValueKey &v)
        return s << v.getValue();
 }
 
-EvaluationValue::EvaluationValue(const EvaluationValue &v)
-{
-       copyFrom(v);
-}
-EvaluationValue::EvaluationValue(EvaluationValue &&v)
-{
-       moveFrom(v);
-}
-EvaluationValue::~EvaluationValue()
-{
-       destroy();
-}
-EvaluationValue &EvaluationValue::operator = (const EvaluationValue &v)
-{
-       copyFrom(v);
-       return *this;
-}
-EvaluationValue &EvaluationValue::operator = (EvaluationValue &&v)
-{
-       moveFrom(v);
-       return *this;
-}
-
-EvaluationValue::Kind EvaluationValue::kind() const
+detail::Kind EvaluationValue::kind() const
 {
        return kind_;
 }
 
-void EvaluationValue::destroy()
-{
-       switch (kind()) {
-       case Kind::EMPTY:
-               break;
-       case Kind::STRING:
-               data.string = {};
-               break;
-       case Kind::INTEGER:
-               break;
-       case Kind::DOUBLE:
-               break;
-       case Kind::UIELEMENT:
-               data.uiElement = {};
-               break;
-       case Kind::BOOLEAN:
-               break;
-       case Kind::POINT:
-               break;
-       case Kind::FUNCTION:
-               data.function = {};
-               break;
-       case Kind::VECTOR:
-               data.vector = {};
-               break;
-       case Kind::SET:
-               data.set = {};
-               break;
-       case Kind::DICT:
-               data.dict = {};
-               break;
-       }
-       kind_ = Kind::EMPTY;
-}
-
-void EvaluationValue::copyFrom(const EvaluationValue &src)
-{
-       if (this != &src) {
-               destroy();
-               kind_ = src.kind();
-               switch (kind()) {
-               case Kind::EMPTY:
-                       break;
-               case Kind::STRING:
-                       data.string = src.data.string;
-                       break;
-               case Kind::INTEGER:
-                       data.integer = src.data.integer;
-                       break;
-               case Kind::DOUBLE:
-                       data.double_ = src.data.double_;
-                       break;
-               case Kind::UIELEMENT:
-                       data.uiElement = src.data.uiElement;
-                       break;
-               case Kind::BOOLEAN:
-                       data.boolean = src.data.boolean;
-                       break;
-               case Kind::POINT:
-                       data.point = src.data.point;
-                       break;
-               case Kind::FUNCTION:
-                       data.function = src.data.function;
-                       break;
-               case Kind::VECTOR:
-                       data.vector = src.data.vector;
-                       break;
-               case Kind::SET:
-                       data.set = src.data.set;
-                       break;
-               case Kind::DICT:
-                       data.dict = src.data.dict;
-                       break;
-               }
-       }
-}
-
-void EvaluationValue::moveFrom(EvaluationValue &src)
-{
-       if (this != &src) {
-               destroy();
-               kind_ = src.kind();
-               switch (kind()) {
-               case Kind::EMPTY:
-                       break;
-               case Kind::STRING:
-                       data.string = std::move(src.data.string);
-                       break;
-               case Kind::INTEGER:
-                       data.integer = src.data.integer;
-                       break;
-               case Kind::DOUBLE:
-                       data.double_ = src.data.double_;
-                       break;
-               case Kind::UIELEMENT:
-                       data.uiElement = std::move(src.data.uiElement);
-                       break;
-               case Kind::BOOLEAN:
-                       data.boolean = src.data.boolean;
-                       break;
-               case Kind::POINT:
-                       data.point = src.data.point;
-                       break;
-               case Kind::FUNCTION:
-                       data.function = std::move(src.data.function);
-                       break;
-               case Kind::VECTOR:
-                       data.vector = std::move(src.data.vector);
-                       break;
-               case Kind::SET:
-                       data.set = std::move(src.data.set);
-                       break;
-               case Kind::DICT:
-                       data.dict = std::move(src.data.dict);
-                       break;
-               }
-       }
-}
-
 bool EvaluationValue::operator == (const EvaluationValue &other) const
 {
        if (kind() != other.kind()) {
-               if (kind() == Kind::INTEGER && other.kind() == Kind::DOUBLE)
-                       return static_cast<double>(asInteger()) == other.asDouble();
-               if (kind() == Kind::DOUBLE && other.kind() == Kind::INTEGER)
-                       return static_cast<double>(other.asInteger()) == asDouble();
+               if (kind() == detail::Kind::INTEGER && other.kind() == detail::Kind::DOUBLE)
+                       return static_cast<double>(as<int>()) == other.as<double>();
+               if (kind() == detail::Kind::DOUBLE && other.kind() == detail::Kind::INTEGER)
+                       return static_cast<double>(other.as<int>()) == as<double>();
                return false;
        }
        switch (kind()) {
-       case Kind::EMPTY:
+       case detail::Kind::EMPTY:
                return true;
-       case Kind::STRING:
-               return data.string == other.data.string;
-       case Kind::INTEGER:
-               return data.integer == other.data.integer;
-       case Kind::DOUBLE:
-               return data.double_ == other.data.double_;
-       case Kind::UIELEMENT:
-               return Atspi::getUniqueId(data.uiElement->getObject()) == Atspi::getUniqueId(other.data.uiElement->getObject());
-       case Kind::BOOLEAN:
-               return data.boolean == other.data.boolean;
-       case Kind::POINT:
-               return data.point == other.data.point;
-       case Kind::VECTOR:
-               return *data.vector == *other.data.vector;
-       case Kind::SET:
-               return *data.set == *other.data.set;
-       case Kind::DICT:
-               return *data.dict == *other.data.dict;
-       case Kind::FUNCTION:
+       case detail::Kind::STRING:
+               return as<std::string>() == other.as<std::string>();
+       case detail::Kind::INTEGER:
+               return as<int>() == other.as<int>();
+       case detail::Kind::DOUBLE:
+               return as<int>() == other.as<int>();
+       case detail::Kind::UIELEMENT:
+               return Atspi::getUniqueId(as<std::shared_ptr<UIElement>>()->getObject()) ==
+                          Atspi::getUniqueId(other.as<std::shared_ptr<UIElement>>()->getObject());
+       case detail::Kind::BOOLEAN:
+               return as<bool>() == other.as<bool>();
+       case detail::Kind::POINT:
+               return as<Point>() == other.as<Point>();
+       case detail::Kind::VECTOR:
+               return as<detail::VectorType>() == other.as<detail::VectorType>();
+       case detail::Kind::SET:
+               return as<detail::SetType>() == other.as<detail::SetType>();
+       case detail::Kind::DICT:
+               return as<detail::DictType>() == other.as<detail::DictType>();
+       case detail::Kind::FUNCTION:
                throw EvaluationFailure{} << "can't compare " << EvaluationValue::toString(kind()) <<
                                                                  " with " << EvaluationValue::toString(other.kind());
        };
@@ -457,37 +253,37 @@ bool EvaluationValue::operator == (const EvaluationValue &other) const
 bool EvaluationValue::operator < (const EvaluationValue &other) const
 {
        if (kind() != other.kind()) {
-               if (kind() == Kind::INTEGER && other.kind() == Kind::DOUBLE)
-                       return static_cast<double>(asInteger()) < other.asDouble();
-               if (kind() == Kind::DOUBLE && other.kind() == Kind::INTEGER)
-                       return asDouble() < static_cast<double>(other.asInteger());
+               if (kind() == detail::Kind::INTEGER && other.kind() == detail::Kind::DOUBLE)
+                       return static_cast<double>(as<int>()) < other.as<double>();
+               if (kind() == detail::Kind::DOUBLE && other.kind() == detail::Kind::INTEGER)
+                       return as<double>() < static_cast<double>(other.as<int>());
        } else {
                switch (kind()) {
-               case Kind::EMPTY:
+               case detail::Kind::EMPTY:
                        break;
-               case Kind::STRING:
-                       return data.string < other.data.string;
-               case Kind::INTEGER:
-                       return data.integer < other.data.integer;
-               case Kind::DOUBLE:
-                       return data.double_ < other.data.double_;
-               case Kind::UIELEMENT:
+               case detail::Kind::STRING:
+                       return as<std::string>() < other.as<std::string>();
+               case detail::Kind::INTEGER:
+                       return as<int>() < other.as<int>();
+               case detail::Kind::DOUBLE:
+                       return as<double>() < other.as<double>();
+               case detail::Kind::UIELEMENT:
                        break;
-               case Kind::BOOLEAN:
+               case detail::Kind::BOOLEAN:
                        break;
-               case Kind::POINT:
+               case detail::Kind::POINT:
                        break;
-               case Kind::FUNCTION:
+               case detail::Kind::FUNCTION:
                        break;
-               case Kind::VECTOR:
-                       for (auto i = 0u; i < std::min(asVector().size(), other.asVector().size()); ++i) {
-                               if (asVector()[i] < other.asVector()[i]) return true;
-                               if (!(asVector()[i] == other.asVector()[i])) return false;
+               case detail::Kind::VECTOR:
+                       for (auto i = 0u; i < std::min(as<detail::VectorType>().size(), other.as<detail::VectorType>().size()); ++i) {
+                               if (as<detail::VectorType>()[i] < other.as<detail::VectorType>()[i]) return true;
+                               if (!(as<detail::VectorType>()[i] == other.as<detail::VectorType>()[i])) return false;
                        }
-                       return asVector().size() < other.asVector().size();
-               case Kind::SET:
+                       return as<detail::VectorType>().size() < other.as<detail::VectorType>().size();
+               case detail::Kind::SET:
                        break;
-               case Kind::DICT:
+               case detail::Kind::DICT:
                        break;
                };
        }
@@ -498,27 +294,27 @@ bool EvaluationValue::operator < (const EvaluationValue &other) const
 EvaluationValue::operator bool () const
 {
        switch (kind()) {
-       case EvaluationValue::Kind::INTEGER:
-               return asInteger() != 0;
-       case EvaluationValue::Kind::DOUBLE:
-               return asDouble() != 0;
-       case EvaluationValue::Kind::BOOLEAN:
-               return asBoolean();
-       case EvaluationValue::Kind::STRING:
-               return !asString().empty();
-       case EvaluationValue::Kind::UIELEMENT:
+       case detail::Kind::INTEGER:
+               return as<int>() != 0;
+       case detail::Kind::DOUBLE:
+               return as<double>() != 0;
+       case detail::Kind::BOOLEAN:
+               return as<bool>();
+       case detail::Kind::STRING:
+               return !as<std::string>().empty();
+       case detail::Kind::UIELEMENT:
                return true;
-       case EvaluationValue::Kind::FUNCTION:
+       case detail::Kind::FUNCTION:
                return true;
-       case EvaluationValue::Kind::POINT:
+       case detail::Kind::POINT:
                return true;
-       case EvaluationValue::Kind::VECTOR:
-               return !asVector().empty();
-       case EvaluationValue::Kind::SET:
-               return !asSet().empty();
-       case EvaluationValue::Kind::DICT:
-               return !asDict().empty();
-       case EvaluationValue::Kind::EMPTY:
+       case detail::Kind::VECTOR:
+               return !as<detail::VectorType>().empty();
+       case detail::Kind::SET:
+               return !as<detail::SetType>().empty();
+       case detail::Kind::DICT:
+               return !as<detail::DictType>().empty();
+       case detail::Kind::EMPTY:
                return false;
        }
        ASSERT(0); // every type has a true / false value in boolean context
@@ -549,44 +345,44 @@ namespace detail
 {
        int ConvertTo<int>::convert(EvaluationContext &ec, EvaluationValue e)
        {
-               if (e.kind() == EvaluationValue::Kind::INTEGER) return e.asInteger();
-               if (e.kind() == EvaluationValue::Kind::DOUBLE) {
-                       if (static_cast<double>(static_cast<int>(e.asDouble())) == e.asDouble()) {
-                               return static_cast<int>(e.asDouble());
+               if (e.kind() == detail::Kind::INTEGER) return e.as<int>();
+               if (e.kind() == detail::Kind::DOUBLE) {
+                       if (static_cast<double>(static_cast<int>(e.as<double>())) == e.as<double>()) {
+                               return static_cast<int>(e.as<double>());
                        }
-               } else if (e.kind() == EvaluationValue::Kind::STRING) {
-                       auto text = e.asString();
+               } else if (e.kind() == detail::Kind::STRING) {
+                       auto text = e.as<std::string>();
                        size_t pos = 0;
                        auto value = std::stoi(text, &pos);
                        if (pos == text.size())
                                return value;
                }
                throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) <<
-                                                                 " to " << EvaluationValue::toString(EvaluationValue::Kind::INTEGER);
+                                                                 " to " << EvaluationValue::toString(detail::Kind::INTEGER);
        }
        double ConvertTo<double>::convert(EvaluationContext &ec, EvaluationValue e)
        {
-               if (e.kind() == EvaluationValue::Kind::INTEGER) return e.asInteger();
-               if (e.kind() == EvaluationValue::Kind::DOUBLE) return e.asDouble();
-               if (e.kind() == EvaluationValue::Kind::STRING) {
-                       auto text = e.asString();
+               if (e.kind() == detail::Kind::INTEGER) return e.as<int>();
+               if (e.kind() == detail::Kind::DOUBLE) return e.as<double>();
+               if (e.kind() == detail::Kind::STRING) {
+                       auto text = e.as<std::string>();
                        size_t pos = 0;
                        auto value = std::stod(text, &pos);
                        if (pos == text.size())
                                return value;
                }
                throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) <<
-                                                                 " to " << EvaluationValue::toString(EvaluationValue::Kind::DOUBLE);
+                                                                 " to " << EvaluationValue::toString(detail::Kind::DOUBLE);
        }
        std::string ConvertTo<std::string>::convert(EvaluationContext &ec, EvaluationValue e)
        {
-               if (e.kind() == EvaluationValue::Kind::INTEGER) return std::to_string(e.asInteger());
-               if (e.kind() == EvaluationValue::Kind::BOOLEAN) return e.asBoolean() ? "true" : "false";
-               if (e.kind() == EvaluationValue::Kind::STRING) return e.asString();
-               if (e.kind() == EvaluationValue::Kind::POINT)
-                       return "<" + std::to_string(e.asPoint().x) + ", " + std::to_string(e.asPoint().y) + ">";
+               if (e.kind() == detail::Kind::INTEGER) return std::to_string(e.as<int>());
+               if (e.kind() == detail::Kind::BOOLEAN) return e.as<bool>() ? "true" : "false";
+               if (e.kind() == detail::Kind::STRING) return e.as<std::string>();
+               if (e.kind() == detail::Kind::POINT)
+                       return "<" + std::to_string(e.as<Point>().x) + ", " + std::to_string(e.as<Point>().y) + ">";
                throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) <<
-                                                                 " to " << EvaluationValue::toString(EvaluationValue::Kind::STRING);
+                                                                 " to " << EvaluationValue::toString(detail::Kind::STRING);
        }
        bool ConvertTo<bool>::convert(EvaluationContext &ec, EvaluationValue e)
        {
@@ -594,11 +390,11 @@ namespace detail
        }
        std::shared_ptr<UIElement> ConvertTo<std::shared_ptr<UIElement>>::convert(EvaluationContext &ec, EvaluationValue e)
        {
-               if (e.kind() == EvaluationValue::Kind::UIELEMENT) return std::move(e.asUiElement());
-               if (e.kind() == EvaluationValue::Kind::POINT) return ec.executionInterface().convert(e.asPoint());
-               if (e.kind() == EvaluationValue::Kind::STRING) return ec.executionInterface().convert(e.asString());
+               if (e.kind() == detail::Kind::UIELEMENT) return std::move(e.as<std::shared_ptr<UIElement>>());
+               if (e.kind() == detail::Kind::POINT) return ec.executionInterface().convert(e.as<Point>());
+               if (e.kind() == detail::Kind::STRING) return ec.executionInterface().convert(e.as<std::string>());
                throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) <<
-                                                                 " to " << EvaluationValue::toString(EvaluationValue::Kind::UIELEMENT);
+                                                                 " to " << EvaluationValue::toString(detail::Kind::UIELEMENT);
        }
        EvaluationValue ConvertTo<EvaluationValue>::convert(EvaluationContext &ec, EvaluationValue e)
        {
@@ -606,8 +402,90 @@ namespace detail
        }
        Point ConvertTo<Point>::convert(EvaluationContext &ec, EvaluationValue e)
        {
-               if (e.kind() == EvaluationValue::Kind::POINT) return e.asPoint();
+               if (e.kind() == detail::Kind::POINT) return e.as<Point>();
                throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) <<
-                                                                 " to " << EvaluationValue::toString(EvaluationValue::Kind::POINT);
+                                                                 " to " << EvaluationValue::toString(detail::Kind::POINT);
+       }
+
+       template <> std::string &get<std::string>(detail::Data &d)
+       {
+               return d.string;
+       }
+       template <> int &get<int>(detail::Data &d)
+       {
+               return d.integer;
+       }
+       template <> std::shared_ptr<UIElement> &get<std::shared_ptr<UIElement>>(detail::Data &d)
+       {
+               return d.uiElement;
+       }
+       template <> bool &get<bool>(detail::Data &d)
+       {
+               return d.boolean;
+       }
+       template <> double &get<double>(detail::Data &d)
+       {
+               return d.double_;
+       }
+       template <> Point &get<Point>(detail::Data &d)
+       {
+               return d.point;
+       }
+       template <> FunctionType &get<detail::FunctionType>(detail::Data &d)
+       {
+               return d.function;
+       }
+       template <> VectorType &get<detail::VectorType>(detail::Data &d)
+       {
+               return *d.vector;
+       }
+       template <> SetType &get<detail::SetType>(detail::Data &d)
+       {
+               return *d.set;
+       }
+       template <> DictType &get<detail::DictType>(detail::Data &d)
+       {
+               return *d.dict;
+       }
+
+       template <> std::string get<std::string>(const detail::Data &d)
+       {
+               return d.string;
+       }
+       template <> int get<int>(const detail::Data &d)
+       {
+               return d.integer;
+       }
+       template <> std::shared_ptr<UIElement> get<std::shared_ptr<UIElement>>(const detail::Data &d)
+       {
+               return d.uiElement;
+       }
+       template <> bool get<bool>(const detail::Data &d)
+       {
+               return d.boolean;
+       }
+       template <> double get<double>(const detail::Data &d)
+       {
+               return d.double_;
+       }
+       template <> Point get<Point>(const detail::Data &d)
+       {
+               return d.point;
+       }
+       template <> FunctionType get<detail::FunctionType>(const detail::Data &d)
+       {
+               return d.function;
+       }
+       template <> VectorType get<detail::VectorType>(const detail::Data &d)
+       {
+               return *d.vector;
+       }
+       template <> SetType get<detail::SetType>(const detail::Data &d)
+       {
+               return *d.set;
+       }
+       template <> DictType get<detail::DictType>(const detail::Data &d)
+       {
+               return *d.dict;
        }
 };
index 4d6e2c6..4ffd35e 100644 (file)
 #include <tuple>
 #include <ostream>
 
-class EvaluationValue;
 class EvaluationContext;
 
+class EvaluationFailure;
+class EvaluationValue;
+class EvaluationValueKey
+{
+public:
+       EvaluationValueKey() = default;
+       EvaluationValueKey(EvaluationValue val);
+       EvaluationValueKey(EvaluationValue val, size_t hash);
+
+       const EvaluationValue &getValue() const
+       {
+               return *value;
+       }
+       operator const EvaluationValue &() const
+       {
+               return *value;
+       }
+
+       size_t getHash() const
+       {
+               return hash;
+       }
+
+       bool operator == (const EvaluationValueKey &k) const;
+
+       friend std::ostream &operator << (std::ostream &s, const EvaluationValue &v);
+
+       static size_t calculateHash(const EvaluationValue &v);
+private:
+       std::shared_ptr<EvaluationValue> value;
+       size_t hash = 0;
+};
+
+namespace std
+{
+       template <> struct hash<EvaluationValueKey> {
+               size_t operator()(const EvaluationValueKey &v) const
+               {
+                       return v.getHash();
+               }
+       };
+}
+
 class EvaluationFailure : public std::exception
 {
 public:
@@ -139,53 +181,7 @@ namespace detail
                {
                }
        };
-}
-
-class EvaluationValue;
-class EvaluationValueKey
-{
-public:
-       EvaluationValueKey() = default;
-       EvaluationValueKey(EvaluationValue val);
-       EvaluationValueKey(EvaluationValue val, size_t hash);
-
-       const EvaluationValue &getValue() const
-       {
-               return *value;
-       }
-       operator const EvaluationValue &() const
-       {
-               return *value;
-       }
-
-       size_t getHash() const
-       {
-               return hash;
-       }
-
-       bool operator == (const EvaluationValueKey &k) const;
-
-       friend std::ostream &operator << (std::ostream &s, const EvaluationValue &v);
-
-       static size_t calculateHash(const EvaluationValue &v);
-private:
-       std::shared_ptr<EvaluationValue> value;
-       size_t hash = 0;
-};
-
-namespace std
-{
-       template <> struct hash<EvaluationValueKey> {
-               size_t operator()(const EvaluationValueKey &v) const
-               {
-                       return v.getHash();
-               }
-       };
-}
 
-class EvaluationValue
-{
-public:
        class FunctionType
        {
        public:
@@ -198,10 +194,12 @@ public:
                {
                        return bool(function);
                }
-               auto operator()(EvaluationContext &ec, std::vector<EvaluationValue> args) const
+
+               template <typename ... ARGS> auto operator()(ARGS &&... args) const
                {
-                       return function(ec, std::move(args));
+                       return function(std::forward<ARGS>(args)...);
                }
+       private:
                Type function;
        };
        template <typename ... ARGS> class UserFunctionType : public FunctionType
@@ -210,15 +208,9 @@ public:
                UserFunctionType(std::function<EvaluationValue(EvaluationContext &, ARGS...)> f) :
                        FunctionType(constructConvertingArgsFunction(std::move(f))) { }
        private:
-               static Type constructConvertingArgsFunction(std::function<EvaluationValue(EvaluationContext &, ARGS...)> f)
-               {
-                       return [f = std::move(f)](EvaluationContext & ec, std::vector<EvaluationValue> sourceArgs) -> EvaluationValue {
-                               std::tuple<ARGS...> args;
-                               detail::Converter<0, sizeof...(ARGS)>::convert(ec, args, sourceArgs);
-                               return detail::apply(f, ec, args);
-                       };
-               }
+               static Type constructConvertingArgsFunction(std::function<EvaluationValue(EvaluationContext &, ARGS...)> f);
        };
+
        using VectorType = std::vector<EvaluationValue>;
        using SetType = std::unordered_set<EvaluationValueKey>;
        using DictType = std::unordered_map<EvaluationValueKey, EvaluationValue>;
@@ -228,58 +220,52 @@ public:
                STRING, INTEGER, DOUBLE, UIELEMENT, BOOLEAN, POINT, FUNCTION, VECTOR, SET, DICT
        };
 
-       static std::string toString(Kind k);
-
-       EvaluationValue();
-       EvaluationValue(std::nullptr_t);
-       EvaluationValue(const EvaluationValue &);
-       EvaluationValue(EvaluationValue &&);
-       EvaluationValue(int integer);
-       EvaluationValue(bool boolean);
-       EvaluationValue(double double_);
-       EvaluationValue(std::string);
-       EvaluationValue(std::shared_ptr<UIElement>);
-       EvaluationValue(Point);
-       EvaluationValue(VectorType);
-       EvaluationValue(SetType);
-       EvaluationValue(DictType);
-       EvaluationValue(FunctionType);
-       EvaluationValue(FunctionType::Type);
-       template <typename T> EvaluationValue(const std::vector<T> &v) : EvaluationValue(VectorType{ v.begin(), v.end() }) { }
-       template <typename T> EvaluationValue(const std::unordered_set<T> &v) : EvaluationValue(SetType{ v.begin(), v.end() }) { }
-       template <typename K, typename V> EvaluationValue(const std::unordered_map<K, V> &v) : EvaluationValue(DictType{ v.begin(), v.end() }) { }
-       ~EvaluationValue();
-
-       EvaluationValue &operator = (const EvaluationValue &);
-       EvaluationValue &operator = (EvaluationValue &&);
-
-       Kind kind() const;
-       const std::string &asString() const;
-       int asInteger() const;
-       const std::shared_ptr<UIElement> &asUiElement() const;
-       bool asBoolean() const;
-       Point asPoint() const;
-       double asDouble() const;
-       const FunctionType::Type &asFunctionType() const;
-       VectorType &asVector() const;
-       SetType &asSet() const;
-       DictType &asDict() const;
-
-       bool operator == (const EvaluationValue &other) const;
-       bool operator < (const EvaluationValue &other) const;
-       bool operator != (const EvaluationValue &other) const;
-       bool operator > (const EvaluationValue &other) const;
-       bool operator <= (const EvaluationValue &other) const;
-       bool operator >= (const EvaluationValue &other) const;
-
-       explicit operator bool () const;
-
-       friend std::ostream &operator << (std::ostream &s, const EvaluationValue &v);
-private:
-       void destroy();
-       void copyFrom(const EvaluationValue &src);
-       void moveFrom(EvaluationValue &src);
-
+       template <typename T> struct TypeToKind;
+       template <> struct TypeToKind<std::string> {
+               static constexpr auto kind = Kind::STRING;
+       };
+       template <> struct TypeToKind<int> {
+               static constexpr auto kind = Kind::INTEGER;
+       };
+       template <> struct TypeToKind<double> {
+               static constexpr auto kind = Kind::DOUBLE;
+       };
+       template <> struct TypeToKind<std::shared_ptr<UIElement>> {
+               static constexpr auto kind = Kind::UIELEMENT;
+       };
+       template <> struct TypeToKind<bool> {
+               static constexpr auto kind = Kind::BOOLEAN;
+       };
+       template <> struct TypeToKind<Point> {
+               static constexpr auto kind = Kind::STRING;
+       };
+       template <> struct TypeToKind<FunctionType> {
+               static constexpr auto kind = Kind::FUNCTION;
+       };
+       template <> struct TypeToKind<VectorType> {
+               static constexpr auto kind = Kind::VECTOR;
+       };
+       template <> struct TypeToKind<SetType> {
+               static constexpr auto kind = Kind::SET;
+       };
+       template <> struct TypeToKind<DictType> {
+               static constexpr auto kind = Kind::DICT;
+       };
+       template <typename ... ARGS> struct TypeToKind<UserFunctionType<ARGS...>> {
+               static constexpr auto kind = Kind::FUNCTION;
+       };
+       template <typename R, typename ... ARGS> struct TypeToKind<std::function<R(ARGS...)>> {
+               static constexpr auto kind = Kind::FUNCTION;
+       };
+       template <typename T> struct TypeToKind<std::vector<T>> {
+               static constexpr auto kind = Kind::VECTOR;
+       };
+       template <typename T> struct TypeToKind<std::unordered_set<T>> {
+               static constexpr auto kind = Kind::SET;
+       };
+       template <typename K, typename V> struct TypeToKind<std::unordered_map<K, V>> {
+               static constexpr auto kind = Kind::DICT;
+       };
        // TODO: turn it into c++17 std::variant, when we upgrade to c++17
        // although current version ain't optimal, it's not big of a deal
        // as it's only used for testing and any DBUS call will be
@@ -291,7 +277,7 @@ private:
                bool boolean = false;
                double double_ = 0;
                Point point;
-               FunctionType::Type function;
+               FunctionType function;
                std::shared_ptr<VectorType> vector;
                std::shared_ptr<SetType> set;
                std::shared_ptr<DictType> dict;
@@ -304,11 +290,80 @@ private:
                Data(std::shared_ptr<UIElement>);
                Data(Point);
                Data(FunctionType);
-               Data(std::shared_ptr<SetType>);
-               Data(std::shared_ptr<DictType>);
-               Data(std::shared_ptr<VectorType>);
-       } data;
-       Kind kind_ = Kind::EMPTY;
+               Data(SetType);
+               Data(DictType);
+               Data(VectorType);
+               template <typename T> Data(std::vector<T> t) : Data(VectorType{ t.begin(), t.end() }) { }
+               template <typename T> Data(std::unordered_set<T> t) : Data(SetType{ t.begin(), t.end() }) { }
+               template <typename K, typename V> Data(std::unordered_map<K, V> t) : Data(DictType{ t.begin(), t.end() }) { }
+       };
+
+       template <typename T> T &get(detail::Data &d) = delete;
+       template <> std::string &get<std::string>(detail::Data &d);
+       template <> int &get<int>(detail::Data &d);
+       template <> std::shared_ptr<UIElement> &get<std::shared_ptr<UIElement>>(detail::Data &d);
+       template <> bool &get<bool>(detail::Data &d);
+       template <> double &get<double>(detail::Data &d);
+       template <> Point &get<Point>(detail::Data &d);
+       template <> FunctionType &get<detail::FunctionType>(detail::Data &d);
+       template <> VectorType &get<detail::VectorType>(detail::Data &d);
+       template <> SetType &get<detail::SetType>(detail::Data &d);
+       template <> DictType &get<detail::DictType>(detail::Data &d);
+
+       template <typename T> T get(const detail::Data &d) = delete;
+       template <> std::string get<std::string>(const detail::Data &d);
+       template <> int get<int>(const detail::Data &d);
+       template <> std::shared_ptr<UIElement> get<std::shared_ptr<UIElement>>(const detail::Data &d);
+       template <> bool get<bool>(const detail::Data &d);
+       template <> double get<double>(const detail::Data &d);
+       template <> Point get<Point>(const detail::Data &d);
+       template <> FunctionType get<detail::FunctionType>(const detail::Data &d);
+       template <> VectorType get<detail::VectorType>(const detail::Data &d);
+       template <> SetType get<detail::SetType>(const detail::Data &d);
+       template <> DictType get<detail::DictType>(const detail::Data &d);
+}
+
+class EvaluationValue
+{
+public:
+       static std::string toString(detail::Kind k);
+
+       EvaluationValue() = default;
+       EvaluationValue(std::nullptr_t) {}
+       template <typename T, typename = typename std::enable_if<std::is_constructible<detail::Data, T>::value, void *>::type>
+       EvaluationValue(T && t) : data(std::move(t)), kind_(detail::TypeToKind<typename std::decay<T>::type>::kind) { }
+       EvaluationValue(const EvaluationValue &) = default;
+       EvaluationValue(EvaluationValue &&) = default;
+       ~EvaluationValue() = default;
+
+       EvaluationValue &operator = (const EvaluationValue &) = default;
+       EvaluationValue &operator = (EvaluationValue &&) = default;
+
+       detail::Kind kind() const;
+       template <typename T> auto &as()
+       {
+               ASSERT(kind() == detail::TypeToKind<typename std::decay<T>::type>::kind);
+               return detail::get<T>(data);
+       }
+       template <typename T> auto as() const
+       {
+               ASSERT(kind() == detail::TypeToKind<typename std::decay<T>::type>::kind);
+               return detail::get<T>(data);
+       }
+
+       bool operator == (const EvaluationValue &other) const;
+       bool operator < (const EvaluationValue &other) const;
+       bool operator != (const EvaluationValue &other) const;
+       bool operator > (const EvaluationValue &other) const;
+       bool operator <= (const EvaluationValue &other) const;
+       bool operator >= (const EvaluationValue &other) const;
+
+       explicit operator bool () const;
+
+       friend std::ostream &operator << (std::ostream &s, const EvaluationValue &v);
+private:
+       detail::Data data;
+       detail::Kind kind_ = detail::Kind::EMPTY;
 };
 
 namespace detail
@@ -317,63 +372,77 @@ namespace detail
        {
                std::vector<T> result;
 
-               if (e.kind() == EvaluationValue::Kind::VECTOR) {
-                       result.reserve(e.asVector().size());
-                       for (auto &e : e.asVector()) {
+               if (e.kind() == detail::Kind::VECTOR) {
+                       result.reserve(e.as<VectorType>().size());
+                       for (auto &e : e.as<VectorType>()) {
                                result.push_back(ConvertTo<T>::convert(ec, e));
                        }
-               } else if (e.kind() == EvaluationValue::Kind::SET) {
-                       result.reserve(e.asSet().size());
-                       for (auto &e : e.asSet()) {
+               } else if (e.kind() == detail::Kind::SET) {
+                       result.reserve(e.as<SetType>().size());
+                       for (auto &e : e.as<SetType>()) {
                                result.push_back(ConvertTo<T>::convert(ec, e));
                        }
-               } else if (e.kind() == EvaluationValue::Kind::DICT) {
-                       result.reserve(e.asDict().size());
-                       for (auto &e : e.asDict()) {
+               } else if (e.kind() == detail::Kind::DICT) {
+                       result.reserve(e.as<DictType>().size());
+                       for (auto &e : e.as<DictType>()) {
                                result.push_back(ConvertTo<T>::convert(ec, e.first));
                        }
                } else throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) <<
-                                                                                        " to " << EvaluationValue::toString(EvaluationValue::Kind::VECTOR);
+                                                                                        " to " << EvaluationValue::toString(detail::Kind::VECTOR);
                return std::move(result);
        }
        template <typename T> std::unordered_set<T> ConvertTo<std::unordered_set<T>>::convert(EvaluationContext &ec, EvaluationValue e)
        {
                std::unordered_set<T> result;
 
-               if (e.kind() == EvaluationValue::Kind::VECTOR) {
-                       result.reserve(e.asVector().size());
-                       for (auto &e : e.asVector()) {
+               if (e.kind() == detail::Kind::VECTOR) {
+                       result.reserve(e.as<VectorType>().size());
+                       for (auto &e : e.as<VectorType>()) {
                                result.insert(ConvertTo<T>::convert(ec, e));
                        }
-               } else if (e.kind() == EvaluationValue::Kind::SET) {
-                       result.reserve(e.asSet().size());
-                       for (auto &e : e.asSet()) {
+               } else if (e.kind() == detail::Kind::SET) {
+                       result.reserve(e.as<SetType>().size());
+                       for (auto &e : e.as<SetType>()) {
                                result.insert(ConvertTo<T>::convert(ec, e));
                        }
-               } else if (e.kind() == EvaluationValue::Kind::DICT) {
-                       result.reserve(e.asDict().size());
-                       for (auto &e : e.asDict()) {
+               } else if (e.kind() == detail::Kind::DICT) {
+                       result.reserve(e.as<DictType>().size());
+                       for (auto &e : e.as<DictType>()) {
                                result.insert(ConvertTo<T>::convert(ec, e.first));
                        }
                } else throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) <<
-                                                                                        " to " << EvaluationValue::toString(EvaluationValue::Kind::VECTOR);
+                                                                                        " to " << EvaluationValue::toString(detail::Kind::VECTOR);
                return std::move(result);
        }
        template <typename K, typename V> std::unordered_map<K, V> ConvertTo<std::unordered_map<K, V>>::convert(EvaluationContext &ec, EvaluationValue e)
        {
                std::unordered_map<K, V> result;
 
-               if (e.kind() == EvaluationValue::Kind::DICT) {
-                       result.reserve(e.asDict().size());
-                       for (auto &e : e.asDict()) {
+               if (e.kind() == detail::Kind::DICT) {
+                       result.reserve(e.as<DictType>().size());
+                       for (auto &e : e.as<DictType>()) {
                                auto k = ConvertTo<K>::convert(ec, e.first);
                                auto v = ConvertTo<V>::convert(ec, e.second);
                                result.insert({ k, v });
                        }
                } else throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) <<
-                                                                                        " to " << EvaluationValue::toString(EvaluationValue::Kind::VECTOR);
+                                                                                        " to " << EvaluationValue::toString(detail::Kind::VECTOR);
                return std::move(result);
        }
+       template <typename ... ARGS>
+       auto UserFunctionType<ARGS...>::constructConvertingArgsFunction(std::function<EvaluationValue(EvaluationContext &, ARGS...)> f) -> Type
+       {
+               return [f = std::move(f)](EvaluationContext & ec, std::vector<EvaluationValue> sourceArgs) -> EvaluationValue {
+                       constexpr auto expectedArgs = sizeof...(ARGS);
+                       if (sourceArgs.size() < expectedArgs)
+                               throw EvaluationFailure{} << "not enough arguments, expected " << expectedArgs << ", got " << sourceArgs.size();
+                       if (sourceArgs.size() > expectedArgs)
+                               throw EvaluationFailure{} << "too many arguments, expected " << expectedArgs << ", got " << sourceArgs.size();
+                       std::tuple<ARGS...> args;
+                       detail::Converter<0, sizeof...(ARGS)>::convert(ec, args, sourceArgs);
+                       return detail::apply(f, ec, args);
+               };
+       }
 }
 
 #endif
index cfc9c22..0a7f988 100644 (file)
 #include <string>
 #include <iomanip>
 
+using detail::VectorType;
+using detail::SetType;
+using detail::FunctionType;
+using detail::DictType;
+
 TokenLocation Evaluator::location() const
 {
        return tokenLocation;
@@ -176,7 +181,7 @@ EvaluationValue ArraySetDictEvaluator::evaluate(EvaluationContext &ec) const
                return { std::move(vals) };
 
        if (kind == Kind::SET) {
-               EvaluationValue::SetType tmp;
+               SetType tmp;
                for (auto &v : vals) {
                        tmp.insert(std::move(v));
                }
@@ -184,7 +189,7 @@ EvaluationValue ArraySetDictEvaluator::evaluate(EvaluationContext &ec) const
        }
 
        ASSERT(kind == Kind::DICT);
-       EvaluationValue::DictType tmp;
+       DictType tmp;
        for (auto i = 0u; i < vals.size(); i += 2) {
                tmp.insert({ std::move(vals[i]), std::move(vals[i + 1]) });
        }
index b5e0ab4..9b46e07 100644 (file)
@@ -22,7 +22,7 @@
 #include <sstream>
 
 using VarsType = std::unordered_map<std::string, EvaluationValue>;
-using ActsType = std::unordered_map<std::string, EvaluationValue::FunctionType>;
+using ActsType = std::unordered_map<std::string, detail::FunctionType>;
 
 struct TestExecutor : ExecutorInterface {
        TestExecutor(VarsType variables = {}, ActsType activities = {},
@@ -122,14 +122,14 @@ TEST(TestExec, simpleParser)
        std::string result2;
        auto vars = VarsType {
                {
-                       "foo", EvaluationValue::UserFunctionType<int, std::string, std::string, bool>{
+                       "foo", detail::UserFunctionType<int, std::string, std::string, bool>{
                                [&](EvaluationContext &, int v1, std::string v2, std::string v3, bool v4) -> EvaluationValue {
                                        return std::to_string(v1) + "," + v2 + "," + v3 + "," + (v4 ? "true" : "false");
                                }
                        }
                },
                {
-                       "bar", EvaluationValue::UserFunctionType<std::string>{
+                       "bar", detail::UserFunctionType<std::string>{
                                [&](EvaluationContext &, std::string value) -> EvaluationValue {
                                        result2 = value;
                                        return nullptr;
@@ -178,14 +178,14 @@ protected:
                size_t callCount = 0;
                auto vars = VarsType { };
                std::ostringstream output;
-               vars["assert"] = EvaluationValue::UserFunctionType<bool> {
+               vars["assert"] = detail::UserFunctionType<bool> {
                        [&](EvaluationContext &, bool val) -> EvaluationValue {
                                if (!val) throw EvaluationFailure{} << "assertion failed2";
                                ++callCount;
                                return {};
                        }
                };
-               vars["print"] = EvaluationValue::UserFunctionType<EvaluationValue> {
+               vars["print"] = detail::UserFunctionType<EvaluationValue> {
                        [&](EvaluationContext &, EvaluationValue val) -> EvaluationValue {
                                output << ">>> " << val << "\n";
                                return {};
@@ -248,6 +248,7 @@ TEST_F(ScriptTest, vector)
                "assert(v[1:2] == [2])\n"
                "assert(v[-2:-1] == [2])\n"
                "v[1:2] = [4, 5, 6]\n"
+               "print(v)\n"
                "assert(v == [1, 4, 5, 6, 3])\n"
                "assert([1] != [2])\n"
                "assert([1] == [1])\n"
@@ -258,7 +259,10 @@ TEST_F(ScriptTest, vector)
                "assert([1, 2, 3] <= [1, 2, 3, 4])\n"
                "assert([1, 2, 3, 5 ] > [1, 2, 3, 4])\n"
                "assert([1, 2, 3, 5 ] >= [1, 2, 3, 4])\n"
-               , 18);
+               "assert(1 in [1,2,3])\n"
+               "assert(1 not in [2,3])\n"
+               "assert(not (1 in [2,3]))\n"
+               , 21);
 }
 
 TEST_F(ScriptTest, set)