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/common | |
| parent | 68f693eedc1c2674d1535553bb519dfac07fdc5e (diff) | |
Implemented conversion to more Variant types, implemented VariantConverter::convert method
Diffstat (limited to 'src/core/common')
| -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);  };  } | 
