diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-11 23:59:46 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-11 23:59:46 +0100 |
commit | 57346af125d4274187bf4af424d13fde072155de (patch) | |
tree | 0b5ee3fd53b433ff92653239c453a59056910ae8 /src/core | |
parent | 68f693eedc1c2674d1535553bb519dfac07fdc5e (diff) |
Implemented conversion to more Variant types, implemented VariantConverter::convert method
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/common/Variant.cpp | 34 | ||||
-rw-r--r-- | src/core/common/Variant.hpp | 90 | ||||
-rw-r--r-- | src/core/common/VariantConverter.cpp | 156 | ||||
-rw-r--r-- | src/core/common/VariantConverter.hpp | 137 |
4 files changed, 398 insertions, 19 deletions
diff --git a/src/core/common/Variant.cpp b/src/core/common/Variant.cpp index 44a77d8..e199bc7 100644 --- a/src/core/common/Variant.cpp +++ b/src/core/common/Variant.cpp @@ -96,7 +96,7 @@ Variant::doubleType Variant::toDouble() const return res.asDouble(); } -Variant::stringType Variant::toString(bool escape) const +Variant::stringType Variant::toString() const { ExceptionLogger logger; Variant res{*this}; @@ -104,6 +104,38 @@ Variant::stringType Variant::toString(bool escape) const return res.asString(); } +Variant::arrayType Variant::toArray() const +{ + ExceptionLogger logger; + Variant res{*this}; + VariantConverter::toArray(res, RttiTypes::None, logger, VariantConverter::Mode::ALL); + return res.asArray(); +} + +Variant::arrayType Variant::toArray(const RttiType &innerType) const +{ + ExceptionLogger logger; + Variant res{*this}; + VariantConverter::toArray(res, innerType, logger, VariantConverter::Mode::ALL); + return res.asArray(); +} + +Variant::mapType Variant::toMap() const +{ + ExceptionLogger logger; + Variant res{*this}; + VariantConverter::toMap(res, RttiTypes::None, logger, VariantConverter::Mode::ALL); + return res.asMap(); +} + +Variant::mapType Variant::toMap(const RttiType &innerType) const +{ + ExceptionLogger logger; + Variant res{*this}; + VariantConverter::toMap(res, innerType, logger, VariantConverter::Mode::ALL); + return res.asMap(); +} + /* Type management */ const RttiType& Variant::getRttiType() const diff --git a/src/core/common/Variant.hpp b/src/core/common/Variant.hpp index da88449..6bd5160 100644 --- a/src/core/common/Variant.hpp +++ b/src/core/common/Variant.hpp @@ -342,6 +342,30 @@ public: Variant(mapType m) : ptrVal(nullptr) { setMap(std::move(m)); } /** + * Named constructor for function values. + * + * @param f is a shared pointer pointing at the Function instance. + */ + static Variant fromFunction(const functionType &f) + { + Variant res; + res.setFunction(f); + return res; + } + + /** + * Named constructor for strings values. + * + * @param s is the std::string from which the variant should be constructed. + */ + static Variant fromString(const stringType &s) + { + Variant res; + res.setString(s.c_str()); + return res; + } + + /** * Constructor for storing managed objects. The reference at the managed * object is stored as a Rooted object. * @@ -620,7 +644,10 @@ public: * * @return the array value as const reference. */ - const arrayType &asArray() const { return asObj<arrayType>(VariantType::ARRAY); } + const arrayType &asArray() const + { + return asObj<arrayType>(VariantType::ARRAY); + } /** * Returns a const reference to the array value. Performs no type @@ -674,7 +701,10 @@ public: * * @return pointer at the stored managed object. */ - functionType &asFunction() { return asObj<functionType>(VariantType::FUNCTION); } + functionType &asFunction() + { + return asObj<functionType>(VariantType::FUNCTION); + } /** * Returns a shared pointer pointing at the stored function object. Performs @@ -713,10 +743,45 @@ public: * 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; + stringType toString() const; + + /** + * Returns the value of the Variant as array, performs type conversion. If + * the variant is not an array yet, the current value is inserted into a + * one-element array. + * + * @return the value of the variant as array. + */ + arrayType toArray() const; + + /** + * Returns the value of the Variant as array, performs type conversion. If + * the variant is not an array yet, the current value is inserted into a + * one-element array. + * + * @param innerType is the inner type the array entries should be converted + * to. + * @return the value of the variant as array. + */ + arrayType toArray(const RttiType &innerType) const; + + /** + * Returns the value of the Variant as map. + * + * @return the value of the variant as map. + */ + mapType toMap() const; + + /** + * Returns the value of the Variant as map, performs type conversion of the + * map entries to the given inner type. + * + * @param innerType is the inner type the map entries should be converted + * to. + * @return the value of the variant as map. + */ + mapType toMap(const RttiType &innerType) const; /** * Sets the variant to null. @@ -843,6 +908,19 @@ public: } /** + * Sets the variant to the given function. + * + * @param f is a std::shared_ptr pointing at a instance of the Function + * class the Variant should be set to. + */ + void setFunction(functionType f) + { + destroy(); + type = VariantType::FUNCTION; + ptrVal = new functionType(f); + } + + /** * Returns the current type of the Variant. * * @return the current type of the Variant. @@ -863,7 +941,7 @@ public: * or RttiTypes::Function or -- in case an object is stored inside the * variant -- the RttiType of that object. */ - const RttiType& getRttiType() const; + const RttiType &getRttiType() const; /** * Returns the name of the given variant type as C-style string. diff --git a/src/core/common/VariantConverter.cpp b/src/core/common/VariantConverter.cpp index 795612b..2d6d286 100644 --- a/src/core/common/VariantConverter.cpp +++ b/src/core/common/VariantConverter.cpp @@ -16,9 +16,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <cstdint> +#include <memory> #include <string> #include <sstream> +#include "Function.hpp" #include "Logger.hpp" #include "Number.hpp" #include "Rtti.hpp" @@ -35,6 +38,13 @@ static std::string msgUnexpectedType(VariantType actualType, std::string(" to ") + Variant::getTypeName(requestedType); } +static std::string msgImplicitConversion(VariantType actualType, + VariantType requestedType) +{ + return std::string("Implicit conversion from ") + Variant::getTypeName(actualType) + + std::string(" to ") + Variant::getTypeName(requestedType); +} + bool VariantConverter::toBool(Variant &var, Logger &logger, Mode mode) { // Perform safe conversions @@ -101,10 +111,9 @@ bool VariantConverter::toInt(Variant &var, Logger &logger, Mode mode) n.parse(var.asString(), logger); if (n.isInt()) { var = (Variant::intType)n.intValue(); - } else { - var = (Variant::doubleType)n.doubleValue(); + return true; } - return true; + break; } case VariantType::ARRAY: { try { @@ -188,18 +197,22 @@ bool VariantConverter::toString(Variant &var, Logger &logger, Mode mode) const VariantType type = var.getType(); switch (type) { case VariantType::NULLPTR: + logger.warning(msgImplicitConversion(type, VariantType::STRING)); var = "null"; return true; case VariantType::BOOL: + logger.warning(msgImplicitConversion(type, VariantType::STRING)); var = var.asBool() ? "true" : "false"; return true; case VariantType::INT: { + logger.warning(msgImplicitConversion(type, VariantType::STRING)); std::stringstream ss; ss << var.asInt(); var = ss.str().c_str(); return true; } case VariantType::DOUBLE: { + logger.warning(msgImplicitConversion(type, VariantType::STRING)); std::stringstream ss; ss << var.asDouble(); var = ss.str().c_str(); @@ -251,5 +264,142 @@ bool VariantConverter::toString(Variant &var, Logger &logger, Mode mode) var = ""; return false; } + +bool VariantConverter::toArray(Variant &var, const RttiType &innerType, + Logger &logger, Mode mode) +{ + // If unsafe conversions are allowed, encapsulate the given variant in an + // array if it is not an array now. + if (!var.isArray() && mode == Mode::ALL) { + var.setArray(Variant::arrayType{var}); + } + + // Make sure the variant is an array + if (var.isArray()) { + // If no specific inner type is given, conversion is successful at this + // point + if (&innerType == &RttiTypes::None) { + return true; + } + + // Convert all entries of the array to the specified inner type, log all + // failures to do so + bool res = true; + for (Variant &v : var.asArray()) { + res = convert(v, innerType, RttiTypes::None, logger, mode) & res; + } + + // Return on successful conversion, otherwise output the default value + if (res) { + return true; + } + } + + // No conversion possible, assign the default value and log an error + logger.error(msgUnexpectedType(var.getType(), VariantType::ARRAY)); + var.setArray(Variant::arrayType{}); + return false; +} + +bool VariantConverter::toMap(Variant &var, const RttiType &innerType, + Logger &logger, Mode mode) +{ + // Make sure the variant is a map + if (var.isMap()) { + // If no specific inner type is given, conversion is successful at this + // point + if (&innerType == &RttiTypes::None) { + return true; + } + + // Convert the inner type of the map to the specified inner type, log + // all failures to do so + bool res = true; + for (auto &e : var.asMap()) { + res = convert(e.second, innerType, RttiTypes::None, logger, mode) & + res; + } + + // Return on successful conversion, otherwise output the default value + if (res) { + return true; + } + } + + // No conversion possible, assign the default value and log an error + logger.error(msgUnexpectedType(var.getType(), VariantType::MAP)); + var.setMap(Variant::mapType{}); + return false; +} + +bool VariantConverter::toFunction(Variant &var, Logger &logger) +{ + if (var.isFunction()) { + return true; + } + + // No conversion possible, assign the default value and log an error + logger.error(msgUnexpectedType(var.getType(), VariantType::MAP)); + var.setFunction(std::shared_ptr<Function>{new Method<void>([]( + const Variant::arrayType &args, void *thisRef) { return Variant{}; })}); + return false; +} + +bool VariantConverter::convert(Variant &var, const RttiType &type, + const RttiType &innerType, Logger &logger, + Mode mode) +{ + // Check for simple Variant types + if (&type == &RttiTypes::None) { + return true; // Everything is fine if no specific type was + // requested + } else if (&type == &RttiTypes::Nullptr) { + // Make sure the variant is set to null + if (!var.isNull()) { + logger.error( + msgUnexpectedType(var.getType(), VariantType::NULLPTR)); + var.setNull(); + return false; + } + return true; + } else if (&type == &RttiTypes::Bool) { + return toBool(var, logger, mode); + } else if (&type == &RttiTypes::Int) { + return toInt(var, logger, mode); + } else if (&type == &RttiTypes::Double) { + return toDouble(var, logger, mode); + } else if (&type == &RttiTypes::String) { + return toString(var, logger, mode); + } else if (&type == &RttiTypes::Array) { + return toArray(var, innerType, logger, mode); + } else if (&type == &RttiTypes::Map) { + return toMap(var, innerType, logger, mode); + } else if (&type == &RttiTypes::Function) { + return toFunction(var, logger); + } + + // If none of the above primitive types is requested, we were + // obviously asked for a managed object. + if (!var.isObject()) { + logger.error(msgUnexpectedType(var.getType(), VariantType::OBJECT)); + var.setNull(); + return false; + } + + // Make sure the object type is correct + if (!var.getRttiType().isa(type)) { + logger.error(std::string("Expected object of type ") + type.name + + " but got object of type " + var.getRttiType().name); + var.setNull(); + return false; + } + return true; +} + +bool VariantConverter::convert(Variant &var, const RttiType &type, + Logger &logger, Mode mode) +{ + return convert(var, type, RttiTypes::None, logger, mode); +} } diff --git a/src/core/common/VariantConverter.hpp b/src/core/common/VariantConverter.hpp index 22ead7a..6becbc4 100644 --- a/src/core/common/VariantConverter.hpp +++ b/src/core/common/VariantConverter.hpp @@ -16,6 +16,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +/** + * @file VariantConverter.hpp + * + * Contains the VariantConverter class which contains code commonly used by + * the Variant, Typesystem and Argument classes to cast Variants to other + * Variant types. + * + * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) + */ + #ifndef _OUSIA_VARIANT_CONVERTER_HPP_ #define _OUSIA_VARIANT_CONVERTER_HPP_ @@ -25,7 +35,6 @@ namespace ousia { class Logger; class RttiType; class Variant; -enum class VariantType : int16_t; /** * The VariantConverter class is used to convert a variant to a certain @@ -39,14 +48,25 @@ public: * conversions (without any data loss) are performed, or all possible * conversions are tried (with possible data loss). */ - enum class Mode { SAFE, ALL }; + enum class Mode { + /** + * Performs only lossless and sane conversions. + */ + SAFE, + + /** + * Performs possibly lossy and probably unintuitive conversions. + */ + ALL + }; /** * Converts the given variant to a boolean. If the "mode" parameter is set * to Mode::SAFE, only booleans can be converted to booleans. For all other * types the conversion fails. If "mode" is set to Mode::ALL, nullptr * values and zero numeric values are treated as "false", all other values - * are treated as "true". + * are treated as "true". If the conversion fails, false is returned as + * default value. * * @param var is instance of the Variant class that should be converted to * the requested type. @@ -54,6 +74,8 @@ public: * should be logged. * @param mode is the conversion mode. See method description for the exact * effect. + * @return true if the operation was successful, false otherwise. In any + * case the input/output parameter "var" will have the requested type. */ static bool toBool(Variant &var, Logger &logger, Mode mode = Mode::SAFE); @@ -64,7 +86,8 @@ public: * converted to 0, 1, nullptr is converted to 0, doubles are truncated, * strings are parsed and truncated, arrays with one element are converted * to an integer. Conversion fails for objects, functions, maps and arrays - * with zero or more than one entry. + * with zero or more than one entry. If the conversion fails, 0 is returned + * as default value. * * @param var is instance of the Variant class that should be converted to * the requested type. @@ -72,6 +95,8 @@ public: * should be logged. * @param mode is the conversion mode. See method description for the exact * effect. + * @return true if the operation was successful, false otherwise. In any + * case the input/output parameter "var" will have the requested type. */ static bool toInt(Variant &var, Logger &logger, Mode mode = Mode::SAFE); @@ -82,7 +107,8 @@ public: * booleans are converted to 0.0, 1.0, nullptr is converted to 0.0, strings * are parsed, arrays with one element are converted to a double. * Conversion fails for objects, functions, maps and arrays with zero or - * more than one entry. + * more than one entry. If the conversion fails, 0.0 is returned as default + * value. * * @param var is instance of the Variant class that should be converted to * the requested type. @@ -90,6 +116,8 @@ public: * should be logged. * @param mode is the conversion mode. See method description for the exact * effect. + * @return true if the operation was successful, false otherwise. In any + * case the input/output parameter "var" will have the requested type. */ static bool toDouble(Variant &var, Logger &logger, Mode mode = Mode::SAFE); @@ -99,7 +127,8 @@ public: * all other types the conversion fails. If "mode" is set to Mode::ALL, * maps and arrays are converted to a JSON representation, objects and * functions are converted to an informative string containing their pointer - * and type. + * and type. If the conversion fails, an empty string is returned as default + * value. * * @param var is instance of the Variant class that should be converted to * the requested type. @@ -107,14 +136,104 @@ public: * should be logged. * @param mode is the conversion mode. See method description for the exact * effect. + * @return true if the operation was successful, false otherwise. In any + * case the input/output parameter "var" will have the requested type. */ static bool toString(Variant &var, Logger &logger, Mode mode = Mode::SAFE); - static bool convert(Variant &var, VariantType requestedType, - const RttiType &rttiType, Logger &logger, + /** + * Converts the given variant to an array with the given inner type. If the + * "mode" parameter is set to Mode::SAFE, the given variant must be an + * array, If mode is set to Mode::ALL, other variant values are encapsulated + * in array with one entry. In both cases, if "innerType" points at a + * primitive Rtti type, conversion to that type is tried (using the + * specified conversion mode). + * + * @param var is instance of the Variant class that should be converted to + * the requested type. + * @param innerType is the inner type of the array entries. Should be set to + * RttiTypes::None in case the inner type of the array does not matter. + * @param logger is a reference to the logger instance into which messages + * should be logged. + * @param mode is the conversion mode. See method description for the exact + * effect. + * @return true if the operation was successful, false otherwise. In any + * case the input/output parameter "var" will have the requested type. + */ + static bool toArray(Variant &var, const RttiType &innerType, Logger &logger, Mode mode = Mode::SAFE); - static bool convert(Variant &var, VariantType requestedType, + + /** + * Converts the given variant to an map with the given inner type. The given + * variant must be a map. If "innerType" points at a primitive Rtti type, + * conversion to that type is tried with the specified conversion mode. + * + * @param var is instance of the Variant class that should be converted to + * the requested type. + * @param innerType is the inner type of the map entries. Should be set to + * RttiTypes::None in case the inner type of the map does not matter. + * @param logger is a reference to the logger instance into which messages + * should be logged. + * @param mode is the conversion mode used for converting the entries of the + * map to the inner type. + * @return true if the operation was successful, false otherwise. In any + * case the input/output parameter "var" will have the requested type. + */ + static bool toMap(Variant &var, const RttiType &innerType, Logger &logger, + Mode mode = Mode::SAFE); + + /** + * Makes sure the given variant is a function. If it is not, it is replaced + * by a dummy function which takes no arguments and returns nullptr. + * + * @param var is the variant that should be converted to a function. + * @param logger is the logger to which error messages should be written. + * @return true if the operation was successful, false otherwise. In any + * case the input/output parameter "var" will have the requested type. + */ + static bool toFunction(Variant &var, Logger &logger); + + /** + * Tries conversion to the given RttiType with the given optional inner + * type. + * + * @param type describes the type to which the variant should be converted. + * This might either be a variant type such as RttiType::Bool, + * RttiType::Int, RttiType::Double, RttiType::String, RttiType::Array, + * RttiType::Map or RttiType::Function. All other types are regarded as + * managed object of this type. If RttiType::None is given, all types are + * accepted. + * @param innerType is used in case of maps or arrays to check the type of + * the elements of these containers. If RttiType::None is given, no special + * type is required. + * @param logger is a reference at the logger instance to which error + * messages are forwarded. + * @param mode is the conversion mode that is being enforced. + * @return true if the operation was successful, false otherwise. In any + * case the input/output parameter "var" will have the requested type. + */ + static bool convert(Variant &var, const RttiType &type, + const RttiType &innerType, Logger &logger, + Mode mode = Mode::SAFE); + + /** + * Tries conversion to the given RttiType without any enforcement regarding + * the inner type of container types. + * + * @param type describes the type to which the variant should be converted. + * This might either be a variant type such as RttiType::Bool, + * RttiType::Int, RttiType::Double, RttiType::String, RttiType::Array, + * RttiType::Map or RttiType::Function. All other types are regarded as + * managed object of this type. If RttiType::None is given, all types are + * accepted. + * @param logger is a reference at the logger instance to which error + * messages are forwarded. + * @param mode is the conversion mode that is being enforced. + * @return true if the operation was successful, false otherwise. In any + * case the input/output parameter "var" will have the requested type. + */ + static bool convert(Variant &var, const RttiType &type, Logger &logger, Mode mode = Mode::SAFE); }; } |