summaryrefslogtreecommitdiff
path: root/src/core/common
diff options
context:
space:
mode:
authorAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-01-11 23:59:46 +0100
committerAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-01-11 23:59:46 +0100
commit57346af125d4274187bf4af424d13fde072155de (patch)
tree0b5ee3fd53b433ff92653239c453a59056910ae8 /src/core/common
parent68f693eedc1c2674d1535553bb519dfac07fdc5e (diff)
Implemented conversion to more Variant types, implemented VariantConverter::convert method
Diffstat (limited to 'src/core/common')
-rw-r--r--src/core/common/Variant.cpp34
-rw-r--r--src/core/common/Variant.hpp90
-rw-r--r--src/core/common/VariantConverter.cpp156
-rw-r--r--src/core/common/VariantConverter.hpp137
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);
};
}