From 0c26390e71193947a67bdd0536915523da38f00f Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Tue, 2 Dec 2014 14:59:17 +0100 Subject: added revamped variant type --- src/core/variant/Variant.hpp | 624 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 624 insertions(+) create mode 100644 src/core/variant/Variant.hpp (limited to 'src/core/variant/Variant.hpp') diff --git a/src/core/variant/Variant.hpp b/src/core/variant/Variant.hpp new file mode 100644 index 0000000..26b053a --- /dev/null +++ b/src/core/variant/Variant.hpp @@ -0,0 +1,624 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program 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. + + This program 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 this program. If not, see . +*/ + +/** + * @file Variant.hpp + * + * The Variant class is used to efficiently represent a variables of varying + * type. Variant instances are used to represent user data and to exchange + * information between the host application and the script clients. + * + * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) + */ + +#ifndef _OUSIA_VARIANT_HPP_ +#define _OUSIA_VARIANT_HPP_ + +#include +#include +#include +#include + +// TODO: Use +// http://nikic.github.io/2012/02/02/Pointer-magic-for-efficient-dynamic-value-representations.html +// later (will allow to use 8 bytes for a variant) + +#include + +namespace ousia { + +/** + * Instances of the Variant class represent any kind of data that is exchanged + * between the host application and the script engine. Variants are immutable. + */ +class Variant { +public: + /** + * Enum containing the possible types a variant may have. + */ + enum class Type : int16_t { + NULLPTR, + BOOL, + INT, + DOUBLE, + STRING, + ARRAY, + MAP + }; + + /** + * Exception thrown whenever a variant is accessed via a getter function + * that + * is not supported for the current variant type. + */ + class TypeException : public OusiaException { + private: + /** + * Internally used string holding the exception message. + */ + const std::string msg; + + public: + /** + * Contains the actual type of the variant. + */ + const Type actualType; + + /** + * Contains the requested type of the variant. + */ + const Type requestedType; + + /** + * Constructor of the TypeException. + * + * @param actualType describes the actual type of the variant. + * @param requestedType describes the type in which the variant was + * requested. + */ + TypeException(Type actualType, Type requestedType); + }; + + using boolType = bool; + using intType = int32_t; + using doubleType = double; + using stringType = std::string; + using arrayType = std::vector; + using mapType = std::map; + +private: + /** + * Used to store the actual type of the variant. + */ + Type type = Type::NULLPTR; + + /** + * Anonymous union containing the possible value of the variant. + */ + union { + /** + * The boolean value. Only valid if type is Type::BOOL. + */ + boolType boolVal; + /** + * The integer value. Only valid if type is Type::INT. + */ + intType intVal; + /** + * The number value. Only valid if type is Type::DOUBLE. + */ + doubleType doubleVal; + /** + * Pointer to the more complex data structures on the free store. Only + * valid if type is one of Type::STRING, Type::ARRAY, + * Type::MAP. + */ + void *ptrVal = nullptr; + }; + + /** + * Internally used to convert the current pointer value to a reference of + * the specified type. + */ + template + T &asObj(Type requestedType) const + { + const Type actualType = getType(); + if (actualType == requestedType) { + return *(static_cast(ptrVal)); + } + throw TypeException{actualType, requestedType}; + } + + /** + * Used internally to assign the value of another Variant instance to this + * instance. + * + * @param v is the Variant instance that should be copied to this instance. + */ + void copy(const Variant &v) + { + type = v.type; + switch (type) { + case Type::NULLPTR: + break; + case Type::BOOL: + boolVal = v.boolVal; + break; + case Type::INT: + intVal = v.intVal; + break; + case Type::DOUBLE: + doubleVal = v.doubleVal; + break; + case Type::STRING: + ptrVal = new stringType{v.asString()}; + break; + case Type::ARRAY: + ptrVal = new arrayType{v.asArray()}; + break; + case Type::MAP: + ptrVal = new mapType{v.asMap()}; + break; + } + } + + /** + * Used internally to move the value of another Variant instance to this + * instance. + * + * @param v is the Variant instance that should be copied to this instance. + */ + void move(Variant &&v) + { + type = v.type; + switch (type) { + case Type::NULLPTR: + break; + case Type::BOOL: + boolVal = v.boolVal; + break; + case Type::INT: + intVal = v.intVal; + break; + case Type::DOUBLE: + doubleVal = v.doubleVal; + break; + case Type::STRING: + case Type::ARRAY: + case Type::MAP: + ptrVal = v.ptrVal; + v.ptrVal = nullptr; + break; + } + v.type = Type::NULLPTR; + } + + /** + * Used internally to destroy any value that was allocated on the heap. + */ + void destroy() + { + if (ptrVal) { + switch (type) { + case Type::STRING: + delete static_cast(ptrVal); + break; + case Type::ARRAY: + delete static_cast(ptrVal); + break; + case Type::MAP: + delete static_cast(ptrVal); + break; + default: + break; + } + } + } + +public: + /** + * Copy constructor of the Variant class. + * + * @param v is the Variant instance that should be cloned. + */ + Variant(const Variant &v) { copy(v); } + + /** + * Move constructor of the Variant class. + * + * @param v is the reference to the Variant instance that should be moved, + * this instance is invalidated afterwards. + */ + Variant(Variant &&v) { move(std::move(v)); } + + /** + * Default constructor. Type is set to Type:null. + */ + Variant() { setNull(); } + + /** + * Default destructor, frees any memory that was allocated on the heap. + */ + ~Variant() { destroy(); } + + /** + * Constructor for boolean values. + * + * @param b boolean value. + */ + Variant(boolType b) { setBool(b); } + + /** + * Constructor for integer values. + * + * @param i integer value. + */ + Variant(intType i) { setInt(i); } + + /** + * Constructor for double values. + * + * @param d double value. + */ + Variant(doubleType d) { setDouble(d); } + + /** + * Constructor for string values. The given string is copied and managed by + * the new Variant instance. + * + * @param s is a reference to a C-Style string used as string value. + */ + Variant(const char *s) { setString(s); } + + /** + * Constructor for array values. The given array is copied and managed by + * the new Variant instance. + * + * @param a is a reference to the array + */ + Variant(std::vector a) { setArray(std::move(a)); } + + /** + * Constructor for map values. The given map is copied and managed by the + * new Variant instance. + * + * @param m is a reference to the map. + */ + Variant(std::map m) { setMap(std::move(m)); } + + /** + * Copy assignment operator. + */ + Variant &operator=(const Variant &v) + { + copy(v); + return *this; + } + + /** + * Move assignment operator. + */ + Variant &operator=(Variant &&v) + { + move(std::move(v)); + return *this; + } + + /** + * Assign nullptr_t operator (allows to write Variant v = nullptr). + * + * @param p is an instance of std::nullptr_t. + */ + Variant &operator=(std::nullptr_t) + { + setNull(); + return *this; + } + + /** + * Assign a boolean value. + * + * @param b is the boolean value to which the variant should be set. + */ + Variant &operator=(boolType b) + { + setBool(b); + return *this; + } + + /** + * Assign an integer value. + * + * @param i is the integer value to which the variant should be set. + */ + Variant &operator=(intType i) + { + setInt(i); + return *this; + } + + /** + * Assign a double value. + * + * @param i is the integer value to which the variant should be set. + */ + Variant &operator=(doubleType d) + { + setInt(d); + return *this; + } + + /** + * Checks whether this Variant instance represents the nullptr. + * + * @return true if the Variant instance represents the nullptr, false + * otherwise. + */ + bool isNull() const { return type == Type::NULLPTR; } + + /** + * Checks whether this Variant instance is a boolean. + * + * @return true if the Variant instance is a boolean, false otherwise. + */ + bool isBool() const { return type == Type::BOOL; } + + /** + * Checks whether this Variant instance is an integer. + * + * @return true if the Variant instance is an integer, false otherwise. + */ + bool isInt() const { return type == Type::INT; } + + /** + * Checks whether this Variant instance is a double. + * + * @return true if the Variant instance is a double, false otherwise. + */ + bool isDouble() const { return type == Type::DOUBLE; } + + /** + * Checks whether this Variant instance is a string. + * + * @return true if the Variant instance is a string, false otherwise. + */ + bool isString() const { return type == Type::STRING; } + + /** + * Checks whether this Variant instance is an array. + * + * @return true if the Variant instance is an array, false otherwise. + */ + bool isArray() const { return type == Type::ARRAY; } + + /** + * Checks whether this Variant instance is a map. + * + * @return true if the Variant instance is a map, false otherwise. + */ + bool isMap() const { return type == Type::MAP; } + + /** + * Returns the Variant boolean value. Performs no type conversion. Throws an + * exception if the underlying type is not a boolean. + * + * @return the boolean value. + */ + boolType asBool() const + { + if (isBool()) { + return boolVal; + } + throw TypeException{getType(), Type::BOOL}; + } + + /** + * Returns the Variant integer value. Performs no type conversion. Throws an + * exception if the underlying type is not an integer. + * + * @return the integer value. + */ + intType asInt() const + { + if (isInt()) { + return intVal; + } + throw TypeException{getType(), Type::INT}; + } + + /** + * Returns the Variant double value. Performs no type conversion. Throws an + * exception if the underlying type is not a double. + * + * @return the double value. + */ + doubleType asDouble() const + { + if (isDouble()) { + return doubleVal; + } + throw TypeException{getType(), Type::DOUBLE}; + } + + /** + * Returns a const reference to the string value. Performs no type + * conversion. Throws an exception if the underlying type is not a string. + * + * @return the string value as const reference. + */ + const stringType &asString() const + { + return asObj(Type::STRING); + } + + /** + * Returns a const reference to the string value. Performs no type + * conversion. Throws an exception if the underlying type is not a string. + * + * @return the string value as reference. + */ + stringType &asString() { return asObj(Type::STRING); } + + /** + * Returns a const reference to the array value. Performs no type + * conversion. Throws an exception if the underlying type is not an array. + * + * @return the array value as const reference. + */ + const arrayType &asArray() const { return asObj(Type::ARRAY); } + + /** + * Returns a const reference to the array value. Performs no type + * conversion. Throws an exception if the underlying type is not an array. + * + * @return the array value as reference. + */ + arrayType &asArray() { return asObj(Type::ARRAY); } + + /** + * Returns a const reference to the map value. Performs no type + * conversion. Throws an exception if the underlying type is not a map. + * + * @return the map value as const reference. + */ + const mapType &asMap() const { return asObj(Type::MAP); } + + /** + * Returns a reference to the map value. Performs no type conversion. + * Throws an exception if the underlying type is not a map. + * + * @return the map value as reference. + */ + mapType &asMap() { return asObj(Type::MAP); } + + /** + * Sets the variant to null. + */ + void setNull() + { + destroy(); + type = Type::NULLPTR; + ptrVal = nullptr; + } + + /** + * Sets the variant to the given boolean value. + * + * @param b is the new boolean value. + */ + void setBool(boolType b) + { + destroy(); + type = Type::BOOL; + boolVal = b; + } + + /** + * Sets the variant to the given integer value. + * + * @param i is the new integer value. + */ + void setInt(intType i) + { + destroy(); + type = Type::INT; + intVal = i; + } + + /** + * Sets the variant to the given double value. + * + * @param d is the new double value. + */ + void setDouble(doubleType d) + { + destroy(); + type = Type::DOUBLE; + doubleVal = d; + } + + /** + * Sets the variant to the given string value. + * + * @param d is the new string value. + */ + void setString(const char *s) + { + if (isString()) { + asString().assign(s); + } else { + destroy(); + type = Type::STRING; + ptrVal = new stringType{s}; + } + } + + /** + * Sets the variant to the given array value. + * + * @param a is the new array value. + */ + void setArray(arrayType a) + { + if (isArray()) { + asArray().swap(a); + } else { + destroy(); + type = Type::ARRAY; + ptrVal = new arrayType{std::move(a)}; + } + } + + /** + * Sets the variant to the given map value. + * + * @param a is the new map value. + */ + void setMap(mapType m) + { + if (isMap()) { + asMap().swap(m); + } else { + destroy(); + type = Type::MAP; + ptrVal = new mapType{std::move(m)}; + } + } + + /** + * Returns the current type of the Variant. + * + * @return the current type of the Variant. + */ + Type getType() const { return type; } + + /** + * Returns the name of the given variant type as C-style string. + */ + static const char *getTypeName(Type type); + + /** + * Returns the name of the type of this variant instance. + */ + const char *getTypeName() { return Variant::getTypeName(getType()); } +}; +} + +#endif /* _OUSIA_VARIANT_HPP_ */ + -- cgit v1.2.3 From 65778eb19e1b4d7d5d145bb2167df0eb01935da7 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Tue, 2 Dec 2014 15:58:48 +0100 Subject: added new unit test for the Variant class and fixed some bugs --- CMakeLists.txt | 1 + src/core/variant/Variant.hpp | 48 +++++++++++----- test/core/variant/VariantTest.cpp | 118 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 test/core/variant/VariantTest.cpp (limited to 'src/core/variant/Variant.hpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a52b6e..cb4d073 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,7 @@ IF(TEST) # test/core/script/FunctionTest # test/core/script/ObjectTest # test/core/script/VariantTest + test/core/variant/VariantTest ) TARGET_LINK_LIBRARIES(ousia_test_core diff --git a/src/core/variant/Variant.hpp b/src/core/variant/Variant.hpp index 26b053a..6b5d03f 100644 --- a/src/core/variant/Variant.hpp +++ b/src/core/variant/Variant.hpp @@ -128,7 +128,7 @@ private: * valid if type is one of Type::STRING, Type::ARRAY, * Type::MAP. */ - void *ptrVal = nullptr; + void *ptrVal; }; /** @@ -153,6 +153,7 @@ private: */ void copy(const Variant &v) { + destroy(); type = v.type; switch (type) { case Type::NULLPTR: @@ -186,6 +187,7 @@ private: */ void move(Variant &&v) { + destroy(); type = v.type; switch (type) { case Type::NULLPTR: @@ -237,7 +239,7 @@ public: * * @param v is the Variant instance that should be cloned. */ - Variant(const Variant &v) { copy(v); } + Variant(const Variant &v) : ptrVal(nullptr) { copy(v); } /** * Move constructor of the Variant class. @@ -245,12 +247,12 @@ public: * @param v is the reference to the Variant instance that should be moved, * this instance is invalidated afterwards. */ - Variant(Variant &&v) { move(std::move(v)); } + Variant(Variant &&v) : ptrVal(nullptr) { move(std::move(v)); } /** * Default constructor. Type is set to Type:null. */ - Variant() { setNull(); } + Variant() : ptrVal(nullptr) { setNull(); } /** * Default destructor, frees any memory that was allocated on the heap. @@ -262,21 +264,21 @@ public: * * @param b boolean value. */ - Variant(boolType b) { setBool(b); } + Variant(boolType b) : ptrVal(nullptr) { setBool(b); } /** * Constructor for integer values. * * @param i integer value. */ - Variant(intType i) { setInt(i); } + Variant(intType i) : ptrVal(nullptr) { setInt(i); } /** * Constructor for double values. * * @param d double value. */ - Variant(doubleType d) { setDouble(d); } + Variant(doubleType d) : ptrVal(nullptr) { setDouble(d); } /** * Constructor for string values. The given string is copied and managed by @@ -284,7 +286,7 @@ public: * * @param s is a reference to a C-Style string used as string value. */ - Variant(const char *s) { setString(s); } + Variant(const char *s) : ptrVal(nullptr) { setString(s); } /** * Constructor for array values. The given array is copied and managed by @@ -292,7 +294,10 @@ public: * * @param a is a reference to the array */ - Variant(std::vector a) { setArray(std::move(a)); } + Variant(arrayType a) : ptrVal(nullptr) + { + setArray(std::move(a)); + } /** * Constructor for map values. The given map is copied and managed by the @@ -300,7 +305,10 @@ public: * * @param m is a reference to the map. */ - Variant(std::map m) { setMap(std::move(m)); } + Variant(mapType m) : ptrVal(nullptr) + { + setMap(std::move(m)); + } /** * Copy assignment operator. @@ -356,11 +364,23 @@ public: /** * Assign a double value. * - * @param i is the integer value to which the variant should be set. + * @param d is the double value to which the variant should be set. */ Variant &operator=(doubleType d) { - setInt(d); + setDouble(d); + return *this; + } + + /** + * Assign a zero terminated const char array. + * + * @param s is the zero terminated const char array to which the variant + * should be set. + */ + Variant &operator=(const char *s) + { + setString(s); return *this; } @@ -581,7 +601,7 @@ public: } else { destroy(); type = Type::ARRAY; - ptrVal = new arrayType{std::move(a)}; + ptrVal = new arrayType(std::move(a)); } } @@ -597,7 +617,7 @@ public: } else { destroy(); type = Type::MAP; - ptrVal = new mapType{std::move(m)}; + ptrVal = new mapType(std::move(m)); } } diff --git a/test/core/variant/VariantTest.cpp b/test/core/variant/VariantTest.cpp new file mode 100644 index 0000000..dfa2f1b --- /dev/null +++ b/test/core/variant/VariantTest.cpp @@ -0,0 +1,118 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program 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. + + This program 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 this program. If not, see . +*/ + +#include + +#include + +namespace ousia { + +TEST(Variant, nullValue) +{ + Variant v; + ASSERT_TRUE(v.isNull()); + + v = 1; + ASSERT_FALSE(v.isNull()); + + v = nullptr; + ASSERT_TRUE(v.isNull()); +} + +TEST(Variant, booleanValue) +{ + Variant v{true}; + ASSERT_TRUE(v.isBool()); + ASSERT_TRUE(v.asBool()); + + v = false; + ASSERT_TRUE(v.isBool()); + ASSERT_FALSE(v.asBool()); + + v.setBool(true); + ASSERT_TRUE(v.isBool()); + ASSERT_TRUE(v.asBool()); + + v = nullptr; + ASSERT_FALSE(v.isBool()); +} + +TEST(Variant, intValue) +{ + Variant v{42}; + ASSERT_TRUE(v.isInt()); + ASSERT_EQ(42, v.asInt()); + + v = 43; + ASSERT_TRUE(v.isInt()); + ASSERT_EQ(43, v.asInt()); + + v = false; + ASSERT_FALSE(v.isInt()); +} + +TEST(Variant, doubleValue) +{ + Variant v{42.5}; + ASSERT_TRUE(v.isDouble()); + ASSERT_EQ(42.5, v.asDouble()); + + v = 42; + ASSERT_FALSE(v.isDouble()); + + v = 43.5; + ASSERT_TRUE(v.isDouble()); + ASSERT_EQ(43.5, v.asDouble()); +} + +TEST(Variant, stringValue) +{ + Variant v{"Hello World"}; + ASSERT_TRUE(v.isString()); + ASSERT_EQ("Hello World", v.asString()); + + v = "Goodbye World"; + ASSERT_TRUE(v.isString()); + ASSERT_EQ("Goodbye World", v.asString()); + + v = 42; + ASSERT_FALSE(v.isString()); +} + +TEST(Variant, arrayValue) +{ + const Variant v{{"test1", 42}}; + ASSERT_EQ(2, v.asArray().size()); + ASSERT_EQ("test1", v.asArray()[0].asString()); + ASSERT_EQ(42, v.asArray()[1].asInt()); +} + +TEST(Variant, mapValue) +{ + const Variant v{{{"key1", "entry1"}, {"key2", "entry2"}}}; + + auto map = v.asMap(); + ASSERT_EQ(2, map.size()); + + ASSERT_EQ("entry1", map.find("key1")->second.asString()); + ASSERT_EQ("entry2", map.find("key2")->second.asString()); +} + + +} + -- cgit v1.2.3 From 80c32c744807afa81178f45af1867fb7c3366c81 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Tue, 2 Dec 2014 16:21:23 +0100 Subject: expanded unit test and fixed further stack overflow caused by missuse of braced initializer list --- src/core/variant/Variant.hpp | 8 ++++---- test/core/variant/VariantTest.cpp | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src/core/variant/Variant.hpp') diff --git a/src/core/variant/Variant.hpp b/src/core/variant/Variant.hpp index 6b5d03f..f438d3e 100644 --- a/src/core/variant/Variant.hpp +++ b/src/core/variant/Variant.hpp @@ -168,13 +168,13 @@ private: doubleVal = v.doubleVal; break; case Type::STRING: - ptrVal = new stringType{v.asString()}; + ptrVal = new stringType(v.asString()); break; case Type::ARRAY: - ptrVal = new arrayType{v.asArray()}; + ptrVal = new arrayType(v.asArray()); break; case Type::MAP: - ptrVal = new mapType{v.asMap()}; + ptrVal = new mapType(v.asMap()); break; } } @@ -585,7 +585,7 @@ public: } else { destroy(); type = Type::STRING; - ptrVal = new stringType{s}; + ptrVal = new stringType(s); } } diff --git a/test/core/variant/VariantTest.cpp b/test/core/variant/VariantTest.cpp index dfa2f1b..3a23887 100644 --- a/test/core/variant/VariantTest.cpp +++ b/test/core/variant/VariantTest.cpp @@ -111,6 +111,8 @@ TEST(Variant, mapValue) ASSERT_EQ("entry1", map.find("key1")->second.asString()); ASSERT_EQ("entry2", map.find("key2")->second.asString()); + + const Variant v2{{{"key1", Variant::arrayType{1, 2, 3}}, {"key2", "entry2"}}}; } -- cgit v1.2.3 From 2ca83f15d5ca81ce8b45fd99d959aee49a6f2eea Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Wed, 3 Dec 2014 00:03:01 +0100 Subject: added type conversion functions and creation from nullptr --- src/core/variant/Variant.cpp | 115 +++++++++++++++++++++++++++++++++++--- src/core/variant/Variant.hpp | 69 +++++++++++++++++++---- test/core/variant/VariantTest.cpp | 8 ++- 3 files changed, 172 insertions(+), 20 deletions(-) (limited to 'src/core/variant/Variant.hpp') diff --git a/src/core/variant/Variant.cpp b/src/core/variant/Variant.cpp index c86905c..d33cd4f 100644 --- a/src/core/variant/Variant.cpp +++ b/src/core/variant/Variant.cpp @@ -16,10 +16,26 @@ along with this program. If not, see . */ +#include + +#include + #include "Variant.hpp" namespace ousia { +/* Class Variant::TypeException */ + +Variant::TypeException::TypeException(Type actualType, Type requestedType) + : OusiaException(std::string("Variant: Requested \"") + + Variant::getTypeName(requestedType) + + std::string("\" but is \"") + + Variant::getTypeName(actualType) + std::string("\"")), + actualType(actualType), + requestedType(requestedType) +{ +} + /* Class Variant */ const char *Variant::getTypeName(Type type) @@ -32,7 +48,7 @@ const char *Variant::getTypeName(Type type) case Type::INT: return "integer"; case Type::DOUBLE: - return "number"; + return "double"; case Type::STRING: return "string"; case Type::ARRAY: @@ -43,16 +59,97 @@ const char *Variant::getTypeName(Type type) return "unknown"; } -/* Class VariantTypeException */ +Variant::boolType Variant::toBool() const +{ + switch (getType()) { + case Type::NULLPTR: + return false; + case Type::BOOL: + return asBool(); + case Type::INT: + return asInt() != 0; + case Type::DOUBLE: + return asDouble() != 0.0; + case Type::STRING: + return true; + case Type::ARRAY: + return true; + case Type::MAP: + return true; + } + return false; +} -Variant::TypeException::TypeException(Type actualType, Type requestedType) - : OusiaException(std::string("Variant: Requested \"") + - Variant::getTypeName(actualType) + - std::string("\" but is \"") + - Variant::getTypeName(requestedType) + std::string("\"")), - actualType(actualType), - requestedType(requestedType) +Variant::intType Variant::toInt() const { + switch (getType()) { + case Type::NULLPTR: + return 0; + case Type::BOOL: + return asBool() ? 1 : 0; + case Type::INT: + return asInt(); + case Type::DOUBLE: + return asDouble(); + case Type::STRING: + return 0; // TODO: Parse string as int + case Type::ARRAY: { + const arrayType &a = asArray(); + return (a.size() == 1) ? a[0].toInt() : 0; + } + case Type::MAP: + return 0; + } + return false; } + +Variant::doubleType Variant::toDouble() const +{ + switch (getType()) { + case Type::NULLPTR: + return 0.0; + case Type::BOOL: + return asBool() ? 1.0 : 0.0; + case Type::INT: + return asInt(); + case Type::DOUBLE: + return asDouble(); + case Type::STRING: + return 0.0; // TODO: Parse string as double + case Type::ARRAY: { + const arrayType &a = asArray(); + return (a.size() == 1) ? a[0].toDouble() : 0; + } + case Type::MAP: + return 0; + } + return false; +} + +Variant::stringType Variant::toString(bool escape) const +{ + switch (getType()) { + case Type::NULLPTR: + return "null"; + case Type::BOOL: + return asBool() ? "true" : "false"; + case Type::INT: + return std::to_string(asInt()); + case Type::DOUBLE: + return std::to_string(asDouble()); + case Type::STRING: { + // TODO: Use proper serialization function + std::stringstream ss; + ss << "\"" << asString() << "\""; + return ss.str(); + } + case Type::ARRAY: + return Utils::join(asArray(), ", ", "[", "]"); + case Type::MAP: + return Utils::join(asMap(), ", ", "{", "}"); + } + return ""; +} + } diff --git a/src/core/variant/Variant.hpp b/src/core/variant/Variant.hpp index f438d3e..d65e14a 100644 --- a/src/core/variant/Variant.hpp +++ b/src/core/variant/Variant.hpp @@ -20,8 +20,8 @@ * @file Variant.hpp * * The Variant class is used to efficiently represent a variables of varying - * type. Variant instances are used to represent user data and to exchange - * information between the host application and the script clients. + * type. Variant instances are used to represent data given by the end user and + * to exchange information between the host application and the script clients. * * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) */ @@ -33,6 +33,7 @@ #include #include #include +#include // TODO: Use // http://nikic.github.io/2012/02/02/Pointer-magic-for-efficient-dynamic-value-representations.html @@ -259,6 +260,11 @@ public: */ ~Variant() { destroy(); } + /** + * Constructor for null values. Initializes the variant as null value. + */ + Variant(std::nullptr_t) : ptrVal(nullptr) { setNull(); } + /** * Constructor for boolean values. * @@ -294,10 +300,7 @@ public: * * @param a is a reference to the array */ - Variant(arrayType a) : ptrVal(nullptr) - { - setArray(std::move(a)); - } + Variant(arrayType a) : ptrVal(nullptr) { setArray(std::move(a)); } /** * Constructor for map values. The given map is copied and managed by the @@ -305,10 +308,7 @@ public: * * @param m is a reference to the map. */ - Variant(mapType m) : ptrVal(nullptr) - { - setMap(std::move(m)); - } + Variant(mapType m) : ptrVal(nullptr) { setMap(std::move(m)); } /** * Copy assignment operator. @@ -527,6 +527,36 @@ public: */ mapType &asMap() { return asObj(Type::MAP); } + /** + * Returns the value of the Variant as boolean, performs type conversion. + * + * @return the Variant value converted to a boolean value. + */ + boolType toBool() const; + + /** + * Returns the value of the Variant as integer, performs type conversion. + * + * @return the Variant value converted to an integer value. + */ + intType toInt() const; + + /** + * Returns the value of the Variant as double, performs type conversion. + * + * @return the Variant value converted to a double value. + */ + doubleType toDouble() const; + + /** + * Returns the value of the Variant as string, performs type conversion. + * + * @return the value of the variant as string. + * @param escape if set to true, adds double quotes to strings and escapes + * them properly (resulting in a more or less JSONesque output). + */ + stringType toString(bool escape = false) const; + /** * Sets the variant to null. */ @@ -637,7 +667,26 @@ public: * Returns the name of the type of this variant instance. */ const char *getTypeName() { return Variant::getTypeName(getType()); } + + /** + * Prints the Variant to the output stream. + */ + friend std::ostream &operator<<(std::ostream &os, const Variant &v) + { + return os << v.toString(true); + } + + /** + * Prints a key value pair to the output stream. + */ + friend std::ostream &operator<<(std::ostream &os, + const mapType::value_type &v) + { + // TODO: Use proper serialization function + return os << "\"" << v.first << "\": " << v.second.toString(true); + } }; + } #endif /* _OUSIA_VARIANT_HPP_ */ diff --git a/test/core/variant/VariantTest.cpp b/test/core/variant/VariantTest.cpp index 3a23887..270c350 100644 --- a/test/core/variant/VariantTest.cpp +++ b/test/core/variant/VariantTest.cpp @@ -16,6 +16,8 @@ along with this program. If not, see . */ +#include + #include #include @@ -32,6 +34,9 @@ TEST(Variant, nullValue) v = nullptr; ASSERT_TRUE(v.isNull()); + + Variant v2{nullptr}; + ASSERT_TRUE(v.isNull()); } TEST(Variant, booleanValue) @@ -112,7 +117,8 @@ TEST(Variant, mapValue) ASSERT_EQ("entry1", map.find("key1")->second.asString()); ASSERT_EQ("entry2", map.find("key2")->second.asString()); - const Variant v2{{{"key1", Variant::arrayType{1, 2, 3}}, {"key2", "entry2"}}}; + const Variant v2{{{"key1", Variant::arrayType{1, 2}}, {"key2", "entry2"}}}; + ASSERT_EQ(2, v2.asMap().find("key1")->second.asArray()[1].asInt()); } -- cgit v1.2.3