diff options
-rw-r--r-- | CMakeLists.txt | 48 | ||||
-rw-r--r-- | Doxyfile.in | 8 | ||||
-rw-r--r-- | dependencies.txt | 38 | ||||
-rw-r--r-- | src/core/script/Function.hpp | 243 | ||||
-rw-r--r-- | src/core/script/Object.cpp | 83 | ||||
-rw-r--r-- | src/core/script/Object.hpp | 132 | ||||
-rw-r--r-- | src/core/script/Variant.cpp | 254 | ||||
-rw-r--r-- | src/core/script/Variant.hpp | 229 | ||||
-rw-r--r-- | test/core/script/Function.cpp | 47 | ||||
-rw-r--r-- | test/core/script/Variant.cpp | 17 |
10 files changed, 802 insertions, 297 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index df4155f..268a391 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,15 +18,47 @@ # Basic Project Definitions and Dependencies # ################################################################################ -PROJECT(basicwriter) +PROJECT(ousia) CMAKE_MINIMUM_REQUIRED(VERSION 2.8.9) # Option for enabling testing. Turn on with 'cmake -Dtest=ON'. OPTION(test "Build all tests." OFF) # Makes boolean 'test' available. +# Option for building the documentation. +FIND_PACKAGE(Doxygen) +OPTION(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND}) + # Enable C++11 and all warnings SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -Wall -pedantic -std=c++11") +# Use PkgConfig to find mozjs-24 +FIND_PACKAGE(PkgConfig REQUIRED) +PKG_CHECK_MODULES(MOZJS REQUIRED mozjs-24) + +################################################################################ +# Inclusion of doxygen # +################################################################################ + +IF(BUILD_DOCUMENTATION) + IF(NOT DOXYGEN_FOUND) + MESSAGE(FATAL_ERROR "Doxygen is needed to build the documentation.") + ENDIF() + + SET(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) + SET(DOXYFILE ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + CONFIGURE_FILE(${DOXYFILE_IN} ${DOXYFILE} @ONLY) + + ADD_CUSTOM_TARGET(doc + COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM) + + INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc) +ENDIF() + + ################################################################################ # Inclusion of gtest # ################################################################################ @@ -46,9 +78,19 @@ ENDIF() # Commands for building Ousía # ################################################################################ -# Includ directories +# Include directories INCLUDE_DIRECTORIES( src/ + ${MOZJS_INCLUDE_DIRS} +) + +# Link directories +LINK_DIRECTORIES( + ${MOZJS_LIBRARY_DIRS} +) + +ADD_DEFINITIONS( + ${MOZJS_CFLAGS_OTHER} ) # ousia_utils library @@ -61,7 +103,7 @@ ADD_LIBRARY(ousia_script src/core/script/Function.cpp src/core/script/Variant.cpp src/core/script/ScriptEngine.cpp - src/core/script/ + src/core/script/Object.cpp ) # Link the ousia executable against ousia_core diff --git a/Doxyfile.in b/Doxyfile.in new file mode 100644 index 0000000..9e9c812 --- /dev/null +++ b/Doxyfile.in @@ -0,0 +1,8 @@ +PROJECT_NAME = "@CMAKE_PROJECT_NAME@" +PROJECT_NUMBER = @VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@ +STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@/src +INPUT = @PROJECT_SOURCE_DIR@/src +OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/doc +FILE_PATTERNS = *.hpp \ + *.cpp +RECURSIVE = YES diff --git a/dependencies.txt b/dependencies.txt index 9957d04..d77be63 100644 --- a/dependencies.txt +++ b/dependencies.txt @@ -1,6 +1,40 @@ +Hard dependencies +================= + The following packages have to be installed (for Fedora) - cmake -# - qt5-qtbase-devel (not needed for now) -# - qt5-qtscript-devel (not needed for now) +- mozjs24-devel + +Documentation +============= + +In order to build the documentation (make doc) + +- doxygen + +Tools +===== + +Automated code formating (clang-format) +--------------------------------------- + +Download Pre-built binary of "LLVM" from + +http://llvm.org/releases/download.html + +use the "clang-format" program in the bin/ directory. The following gedit tool can be used for automated code formating: + +(Active "External Tools" plugin, add the following lines to ./config/gedit/tools/, without indentation) + + #!/bin/sh + # [Gedit Tool] + # Input=document + # Shortcut=<Shift><Alt>f + # Applicability=all + # Save-files=nothing + # Output=replace-document + # Name=Clang Format + + cd $GEDIT_CURRENT_DOCUMENT_DIR && <INSERT CORRECT PATH HERE>/clang-format diff --git a/src/core/script/Function.hpp b/src/core/script/Function.hpp index 4c13dbc..2e92f08 100644 --- a/src/core/script/Function.hpp +++ b/src/core/script/Function.hpp @@ -29,12 +29,23 @@ namespace ousia { namespace script { /** - * The abstract Function class is most basic version of a function handle -- - * just a virtual "call" function which calls the underlying code. + * The abstract Function class is most basic version of a function handle, + * maintaining a "call" function and basic virtual functions for lifecyle + * management. */ class Function { - public: + /** + * Virtual clone function (e.g. used in the variant class). + */ + virtual Function *clone() const = 0; + + /** + * Virtual destructor. + */ + virtual ~Function() + { + } /** * Abstract function which is meant to call the underlying function (be it @@ -51,26 +62,27 @@ public: * * @return a Variant containing the return value. */ - Variant call() const { return call({}); } + Variant call() const + { + return call({}); + } + // TODO: Use () operator instead of the call function }; /** - * The ArgumentDescriptor class is used to describe the type of a function + * The Argument class is used to describe the type of a function * argument. */ -struct ArgumentDescriptor { - +struct Argument { const VariantType type; const bool hasDefault; const Variant defaultValue; - ArgumentDescriptor(VariantType type) : - type(type), hasDefault(false) {}; - - ArgumentDescriptor(VariantType type, const Variant &defaultValue) : - type(type), hasDefault(true), defaultValue(defaultValue) {}; + Argument(VariantType type) : type(type), hasDefault(false){}; + Argument(VariantType type, const Variant &defaultValue) + : type(type), hasDefault(true), defaultValue(defaultValue){}; }; /** @@ -78,21 +90,18 @@ struct ArgumentDescriptor { * validator errors. */ class ArgumentValidatorError : public std::exception { - public: - const int index; const std::string msg; - ArgumentValidatorError(int index, const std::string &msg) : - index(index), msg(msg) {}; + ArgumentValidatorError(int index, const std::string &msg) + : index(index), msg(msg){}; - virtual const char* what() const noexcept override + virtual const char *what() const noexcept override { return msg.c_str(); } - }; /** @@ -100,12 +109,11 @@ public: * arguments passed to a function match the description. */ class ArgumentValidator { - private: /** * List containing the argument descriptors. */ - const std::vector<ArgumentDescriptor> descriptors; + const std::vector<Argument> descriptors; /** * Argument index in the input array, at which the last error occured. @@ -118,20 +126,22 @@ private: std::string errorMessage; std::pair<bool, std::vector<Variant>> setError(int idx, - const std::string &msg, std::vector<Variant> &res); + const std::string &msg, + std::vector<Variant> &res); void resetError(); public: - /** * Constructor of the argument validator class. * - * @param descriptors is a list of ArgumentDescriptors which should be used + * @param descriptors is a list of Arguments which should be used * for the validation. */ - ArgumentValidator(const std::vector<ArgumentDescriptor> &descriptors) : - descriptors(descriptors) {} + ArgumentValidator(const std::vector<Argument> &descriptors) + : descriptors(descriptors) + { + } /** * Validates and augments the given argument list (e.g. adds the default @@ -143,7 +153,8 @@ public: * list of arguments. If false is returned, use the error function to get * more information about the error. */ - std::pair<bool, std::vector<Variant>> validate(const std::vector<Variant> &args); + std::pair<bool, std::vector<Variant>> validate( + const std::vector<Variant> &args); /** * Returns an ArgumentValidatorError instance containing the argument index @@ -157,68 +168,174 @@ public: */ ArgumentValidatorError error() { - return ArgumentValidatorError(errorIndex, errorMessage); + return ArgumentValidatorError{errorIndex, errorMessage}; } - }; /** - * The HostFunction class represents a function that resides in the script host. + * A validating function */ -template<class T> -class HostFunction : public Function { - +class ValidatingFunction : public Function { private: - T callback; ArgumentValidator *validator; - void *data; - -public: - - HostFunction(T callback, std::vector<ArgumentDescriptor> signature, - void *data = nullptr) : - callback(callback), validator(new ArgumentValidator(signature)), - data(data) {} - HostFunction(T callback, void *data = nullptr) : - callback(callback), validator(nullptr), data(data) {} - - ~HostFunction() - { - delete validator; - } +protected: + virtual Variant validatedCall(const std::vector<Variant> &args) const = 0; virtual Variant call(const std::vector<Variant> &args) const override { if (validator) { - std::pair<bool, std::vector<Variant>> res = validator->validate(args); + std::pair<bool, std::vector<Variant>> res = + validator->validate(args); if (!res.first) { throw validator->error(); } - return callback(res.second, data); - } else { - return callback(args, data); + return validatedCall(res.second); } + return validatedCall(args); } using Function::call; +public: + ValidatingFunction() : validator(nullptr) + { + } + + ValidatingFunction(std::vector<Argument> signature) + : validator(new ArgumentValidator(signature)) + { + } + + ~ValidatingFunction() override + { + delete validator; + } }; -template<class T> -static HostFunction<T> createHostFunction(T callback, - std::vector<ArgumentDescriptor> signature, void *data = nullptr) -{ - return HostFunction<T>(callback, signature, data); -} +using HostFunctionCallback = Variant (*)(const std::vector<Variant> &args, + void *data); +using GetterCallback = Variant (*)(void *data); +using SetterCallback = void (*)(Variant arg, void *data); -template<class T> -static HostFunction<T> createHostFunction(T callback, void *data = nullptr) -{ - return HostFunction<T>(callback, data); -} +class HostFunction : public ValidatingFunction { +private: + HostFunctionCallback callback; + void *data; + +protected: + virtual Variant validatedCall( + const std::vector<Variant> &args) const override + { + return callback(args, data); + } + +public: + HostFunction(HostFunctionCallback callback, std::vector<Argument> signature, + void *data = nullptr) + : ValidatingFunction(signature), callback(callback), data(data) + { + } + HostFunction(HostFunctionCallback callback, void *data = nullptr) + : ValidatingFunction(), callback(callback), data(data) + { + } + + Function *clone() const override + { + return new HostFunction(*this); + } + + using ValidatingFunction::call; +}; + +class Getter : public ValidatingFunction { +private: + GetterCallback callback; + void *data; + +protected: + virtual Variant validatedCall( + const std::vector<Variant> &args) const override + { + if (!callback) { + // TODO: Use another exception class here + throw "Getter not defined"; + } + return callback(data); + } + +public: + Getter(GetterCallback callback, void *data = nullptr) + : ValidatingFunction(std::vector<Argument>{}), + callback(callback), + data(data){}; + + Function *clone() const override + { + return new Getter(*this); + } + + Variant call() const + { + return ValidatingFunction::call(); + } + + Variant operator()() const + { + return call(); + } + bool exists() + { + return callback != nullptr; + } +}; + +class Setter : public ValidatingFunction { +private: + SetterCallback callback; + void *data; + +protected: + virtual Variant validatedCall( + const std::vector<Variant> &args) const override + { + if (!callback) { + // TODO: Use another exception class here + throw "Setter not defined"; + } + callback(args[0], data); + return VarNull; + } + +public: + Setter(VariantType type, SetterCallback callback, void *data = nullptr) + : ValidatingFunction({Argument{type}}), + callback(callback), + data(data){}; + + Function *clone() const override + { + return new Setter(*this); + } + + void call(Variant arg) const + { + ValidatingFunction::call({arg}); + } + + void operator()(Variant arg) const + { + return call(arg); + } + + bool exists() + { + return callback != nullptr; + } +}; } } diff --git a/src/core/script/Object.cpp b/src/core/script/Object.cpp new file mode 100644 index 0000000..8d858bc --- /dev/null +++ b/src/core/script/Object.cpp @@ -0,0 +1,83 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "Object.hpp" + +namespace ousia { +namespace script { + +bool Object::hasElement(std::string name) const +{ + return (properties.find(name) != properties.end()) || + (methods.find(name) != methods.end()); +} + +void Object::addProperty(std::string name, const Property &property) +{ + if (hasElement(name)) { + // TODO Throw another exception class here + throw "Element already exists"; + } + properties.emplace(name, property); +} + +void Object::addProperty(std::string name, const Getter &get, const Setter &set) +{ + addProperty(name, Property{get, set}); +} + +void Object::addProperty(std::string name, VariantType type, + const GetterCallback get, const SetterCallback set) +{ + addProperty(name, Property{type, get, set, data}); +} + +void Object::addReadonlyProperty(std::string name, const Getter &get) +{ + addProperty(name, Property{get, Setter{VariantType::null, nullptr}}); +} + +void Object::addReadonlyProperty(std::string name, const GetterCallback get) +{ + addProperty( + name, Property{Getter{get, data}, Setter{VariantType::null, nullptr}}); +} + +void Object::addMethod(std::string name, const HostFunction &fun) +{ + if (hasElement(name)) { + // TODO Throw another exception class here + throw "Element already exists"; + } + methods.emplace(name, fun); +} + +void Object::addMethod(std::string name, const HostFunctionCallback fun) +{ + addMethod(name, HostFunction{fun, data}); +} + +void Object::addMethod(std::string name, const HostFunctionCallback fun, + const std::vector<Argument> &signature) +{ + addMethod(name, HostFunction{fun, signature, data}); +} + +} +} + diff --git a/src/core/script/Object.hpp b/src/core/script/Object.hpp new file mode 100644 index 0000000..20e0f0f --- /dev/null +++ b/src/core/script/Object.hpp @@ -0,0 +1,132 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _OBJECT_HPP_ +#define _OBJECT_HPP_ + +#include <string> +#include <map> + +#include "Function.hpp" + +namespace ousia { +namespace script { + +/** + * The Property struct represents an object property with corresponding getter + * and setter function. + */ +struct Property { + /** + * Constructor of the Property struct. Copies the given getter and setter. + * + * @param get is the getter that should be used for the property. + * @param set is the setter that should be used for the property. + */ + Property(const Getter &get, const Setter &set) : get(get), set(set){}; + + /** + * Constructor of the Property struct. Creates new Getter and Setter + * instances from the given parameters. + * + * @param type is the VariantType used within the getter function. + * @param get is the pointer to the getter function. + * @param set is the pointer to the setter function. + * @param data is the used-defined data that should be used. + */ + Property(VariantType type, const GetterCallback get, + const SetterCallback set, void *data = nullptr) + : get(get, data), set(type, set, data){}; + + /** + * Getter function. + */ + const Getter get; + + /** + * Setter function. + */ + const Setter set; +}; + +/** + * The Object type represents an object on the script host. An object consits of + * properties with corresponding getter and setter functions and a number of + * methods which can be called on the object. + */ +class Object { +private: + /** + * Pointer to user defined data that is automatically passed to the + * underlying functions. + */ + void *data; + + /** + * Map used internally for storing all properties along with their + * corresponding + * name. + */ + std::map<std::string, Property> properties; + + /** + * Map used internally for storing all methods along with their + * corresponding name. + */ + std::map<std::string, HostFunction> methods; + +public: + Object() : data(nullptr){}; + + Object(void *data) : data(data){}; + + bool hasElement(std::string name) const; + + void addProperty(std::string name, const Property &property); + + void addProperty(std::string name, const Getter &get, const Setter &set); + + void addProperty(std::string name, VariantType type, + const GetterCallback get, const SetterCallback set); + + void addReadonlyProperty(std::string name, const Getter &get); + + void addReadonlyProperty(std::string name, const GetterCallback get); + + void addMethod(std::string name, const HostFunction &fun); + + void addMethod(std::string name, const HostFunctionCallback fun); + + void addMethod(std::string name, const HostFunctionCallback fun, + const std::vector<Argument> &signature); + + const std::map<std::string, Property> &getProperties() + { + return properties; + } + + const std::map<std::string, HostFunction> &getMethods() + { + return methods; + } +}; +} +} + +#endif /* _OBJECT_HPP_ */ + diff --git a/src/core/script/Variant.cpp b/src/core/script/Variant.cpp index 3858b68..75f46ba 100644 --- a/src/core/script/Variant.cpp +++ b/src/core/script/Variant.cpp @@ -17,13 +17,242 @@ */ #include "Variant.hpp" +#include "Function.hpp" +#include "Object.hpp" namespace ousia { namespace script { -/* Class VariantTypeException */ +/* Class Variant */ + +Variant::Variant(const Variant &v) : type(v.type) +{ + switch (v.type) { + case VariantType::null: + break; + case VariantType::boolean: + booleanValue = v.booleanValue; + break; + case VariantType::integer: + integerValue = v.integerValue; + break; + case VariantType::number: + numberValue = v.numberValue; + break; + case VariantType::string: + objectValue = + new std::string(*static_cast<std::string *>(v.objectValue)); + break; + case VariantType::array: + objectValue = new std::vector<Variant>( + *static_cast<std::vector<Variant> *>(v.objectValue)); + break; + case VariantType::map: + objectValue = new std::map<std::string, Variant>( + *static_cast<std::map<std::string, Variant> *>(v.objectValue)); + break; + case VariantType::function: + objectValue = static_cast<Function *>(v.objectValue)->clone(); + break; + case VariantType::object: + objectValue = new Object(*static_cast<Object *>(v.objectValue)); + break; + case VariantType::buffer: + // TODO + break; + } +} + +Variant::Variant(Variant &&v) : type(v.type) +{ + switch (type) { + case VariantType::null: + break; + case VariantType::boolean: + booleanValue = v.booleanValue; + break; + case VariantType::integer: + integerValue = v.integerValue; + break; + case VariantType::number: + numberValue = v.numberValue; + break; + case VariantType::string: + case VariantType::array: + case VariantType::map: + case VariantType::function: + case VariantType::object: + case VariantType::buffer: + objectValue = v.objectValue; + v.objectValue = nullptr; + break; + } +} + +Variant::Variant() : type(VariantType::null) +{ +} + +Variant::Variant(bool b) : type(VariantType::boolean), booleanValue(b) +{ +} + +Variant::Variant(int64_t i) : type(VariantType::integer), integerValue(i) +{ +} + +Variant::Variant(double d) : type(VariantType::number), numberValue(d) +{ +} + +Variant::Variant(const char *s) + : type(VariantType::string), objectValue(new std::string(s)) +{ +} + +Variant::Variant(const std::vector<Variant> &a) + : type(VariantType::array), objectValue(new std::vector<Variant>(a)) +{ +} + +Variant::Variant(const std::map<std::string, Variant> &m) + : type(VariantType::map), objectValue(new std::map<std::string, Variant>(m)) +{ +} + +Variant::Variant(const Function *f) + : type(VariantType::function), objectValue(f->clone()) +{ +} + +Variant::Variant(const Object &o) + : type(VariantType::object), objectValue(new Object(o)) +{ +} + +Variant::~Variant() +{ + switch (type) { + case VariantType::string: + delete static_cast<std::string *>(objectValue); + break; + case VariantType::array: + delete static_cast<std::vector<Variant> *>(objectValue); + break; + case VariantType::map: + delete static_cast<std::map<std::string, Variant> *>(objectValue); + break; + case VariantType::function: + delete static_cast<Function*>(objectValue); + break; + case VariantType::object: + delete static_cast<Object*>(objectValue); + break; + default: + break; + } +} -const char* Variant::getTypeName(VariantType type) +bool Variant::getBooleanValue() const +{ + switch (type) { + case VariantType::null: + return false; + case VariantType::boolean: + return booleanValue; + case VariantType::integer: + return integerValue != 0; + case VariantType::number: + return numberValue != 0.0; + case VariantType::string: + return !getStringValue().empty(); + case VariantType::array: + return !getArrayValue().empty(); + case VariantType::map: + return !getMapValue().empty(); + default: + throw VariantTypeException{type, VariantType::boolean}; + } +} + +int64_t Variant::getIntegerValue() const +{ + switch (type) { + case VariantType::boolean: + return booleanValue ? 1 : 0; + case VariantType::integer: + return integerValue; + case VariantType::number: + return static_cast<int64_t>(numberValue); + default: + throw VariantTypeException{type, VariantType::integer}; + } +} + +double Variant::getNumberValue() const +{ + switch (type) { + case VariantType::boolean: + return booleanValue ? 1.0 : 0.0; + case VariantType::integer: + return static_cast<double>(integerValue); + case VariantType::number: + return numberValue; + default: + throw VariantTypeException{type, VariantType::number}; + } +} + +const std::string &Variant::getStringValue() const +{ + switch (type) { + case VariantType::string: + return *(static_cast<std::string *>(objectValue)); + default: + throw VariantTypeException{type, VariantType::string}; + } +} + +const std::vector<Variant> &Variant::getArrayValue() const +{ + switch (type) { + case VariantType::array: + return *(static_cast<std::vector<Variant> *>(objectValue)); + default: + throw VariantTypeException{type, VariantType::array}; + } +} + +const std::map<std::string, Variant> &Variant::getMapValue() const +{ + switch (type) { + case VariantType::map: + return *(static_cast<std::map<std::string, Variant> *>( + objectValue)); + default: + throw VariantTypeException{type, VariantType::map}; + } +} + +const Function *Variant::getFunctionValue() const +{ + switch (type) { + case VariantType::function: return static_cast<Function *>(objectValue); + default: + throw VariantTypeException{type, VariantType::function}; + } +} + +const Object &Variant::getObjectValue() const +{ + switch (type) { + case VariantType::object: return *(static_cast<Object *>(objectValue)); + default: + throw VariantTypeException{type, VariantType::function}; + } +} + +const char *Variant::getTypeName(VariantType type) { switch (type) { case VariantType::null: @@ -50,22 +279,26 @@ const char* Variant::getTypeName(VariantType type) return "unknown"; } +/* Class VariantTypeException */ + VariantTypeException::VariantTypeException(VariantType actualType, - VariantType requestedType) : - msg(std::string("Cannot get value of variant of type \"") - + Variant::getTypeName(actualType) - + std::string("\" as \"") + Variant::getTypeName(requestedType) - + std::string("\"")), - actualType(actualType), requestedType(requestedType) {} + VariantType requestedType) + : msg(std::string("Cannot get value of variant of type \"") + + Variant::getTypeName(actualType) + std::string("\" as \"") + + Variant::getTypeName(requestedType) + std::string("\"")), + actualType(actualType), + requestedType(requestedType) +{ +} -const char* VariantTypeException::what() const noexcept +const char *VariantTypeException::what() const noexcept { return msg.c_str(); } /* Global scope operator */ -std::ostream& operator<< (std::ostream& os, const Variant &v) +std::ostream &operator<<(std::ostream &os, const Variant &v) { switch (v.type) { case VariantType::null: @@ -121,7 +354,6 @@ std::ostream& operator<< (std::ostream& os, const Variant &v) } return os; } - } } diff --git a/src/core/script/Variant.hpp b/src/core/script/Variant.hpp index 923c8ca..295c044 100644 --- a/src/core/script/Variant.hpp +++ b/src/core/script/Variant.hpp @@ -29,6 +29,10 @@ namespace ousia { namespace script { +/* Class forward declarations */ +class Object; +class Function; + /** * Enum containing the possible types a variant may have. */ @@ -50,7 +54,6 @@ enum class VariantType : int16_t { * is not supported for the current variant type. */ class VariantTypeException : public std::exception { - private: /** * Internally used string holding the exception message. @@ -82,8 +85,7 @@ public: * * @return the error message as C string. */ - virtual const char* what() const noexcept override; - + virtual const char *what() const noexcept override; }; /** @@ -91,9 +93,7 @@ public: * between the host application and the script engine. Variants are immutable. */ class Variant { - private: - const VariantType type; union { @@ -104,213 +104,46 @@ private: }; public: - - Variant(const Variant &v) : - type(v.type) - { - switch (v.type) { - case VariantType::null: - break; - case VariantType::boolean: - booleanValue = v.booleanValue; - break; - case VariantType::integer: - integerValue = v.integerValue; - break; - case VariantType::number: - numberValue = v.numberValue; - break; - case VariantType::string: - objectValue = new std::string( - *static_cast<std::string*>(v.objectValue)); - break; - case VariantType::array: - objectValue = new std::vector<Variant>( - *static_cast<std::vector<Variant>*>(v.objectValue)); - break; - case VariantType::map: - objectValue = new std::map<std::string, Variant>( - *static_cast<std::map<std::string, Variant>*>(v.objectValue)); - break; - case VariantType::function: - case VariantType::object: - case VariantType::buffer: - // TODO - break; - } - } - - Variant(Variant &&v) : - type(v.type) - { - switch (type) { - case VariantType::null: - break; - case VariantType::boolean: - booleanValue = v.booleanValue; - break; - case VariantType::integer: - integerValue = v.integerValue; - break; - case VariantType::number: - numberValue = v.numberValue; - break; - case VariantType::string: - case VariantType::array: - case VariantType::map: - case VariantType::function: - case VariantType::object: - case VariantType::buffer: - objectValue = v.objectValue; - v.objectValue = nullptr; - break; - } - } - - ~Variant() - { - switch (type) { - case VariantType::string: - delete static_cast<std::string*>(objectValue); - break; - case VariantType::array: - delete static_cast<std::vector<Variant>*>(objectValue); - break; - case VariantType::map: - delete static_cast<std::map<std::string, Variant>*>(objectValue); - break; - default: - break; - } - } - - Variant& operator=(const Variant &v) = delete; - Variant& operator=(Variant &&v) = delete; - - Variant() : - type(VariantType::null) {} - - Variant(bool b) : - type(VariantType::boolean), - booleanValue(b) {} - - Variant(int64_t i) : - type(VariantType::integer), - integerValue(i) {} - - Variant(double d) : - type(VariantType::number), - numberValue(d) {} - - Variant(const char *s) : - type(VariantType::string), - objectValue(new std::string(s)) {} - - Variant(const std::vector<Variant> &a) : - type(VariantType::array), - objectValue(new std::vector<Variant>(a)) {} - - Variant(const std::map<std::string, Variant> &m) : - type(VariantType::map), - objectValue(new std::map<std::string, Variant>(m)) {} + Variant(const Variant &v); + Variant(Variant &&v); + + Variant(); + Variant(bool b); + Variant(int64_t i); + Variant(double d); + Variant(const char *s); + Variant(const std::vector<Variant> &a); + Variant(const std::map<std::string, Variant> &m); + Variant(const Function *f); + Variant(const Object &o); + ~Variant(); + + Variant &operator=(const Variant &v) = delete; + Variant &operator=(Variant &&v) = delete; VariantType getType() const { return type; } - bool getBooleanValue() const - { - switch (type) { - case VariantType::null: - return false; - case VariantType::boolean: - return booleanValue; - case VariantType::integer: - return integerValue != 0; - case VariantType::number: - return numberValue != 0.0; - case VariantType::string: - return !getStringValue().empty(); - case VariantType::array: - return !getArrayValue().empty(); - case VariantType::map: - return !getMapValue().empty(); - default: - throw VariantTypeException{type, VariantType::boolean}; - } - } - - int64_t getIntegerValue() const - { - switch (type) { - case VariantType::boolean: - return booleanValue ? 1 : 0; - case VariantType::integer: - return integerValue; - case VariantType::number: - return static_cast<int64_t>(numberValue); - default: - throw VariantTypeException{type, VariantType::integer}; - } - } + bool getBooleanValue() const; + int64_t getIntegerValue() const; + double getNumberValue() const; + const std::string &getStringValue() const; + const std::vector<Variant> &getArrayValue() const; + const std::map<std::string, Variant> &getMapValue() const; + const Function *getFunctionValue() const; + const Object &getObjectValue() const; - double getNumberValue() const - { - switch (type) { - case VariantType::boolean: - return booleanValue ? 1.0 : 0.0; - case VariantType::integer: - return static_cast<double>(integerValue); - case VariantType::number: - return numberValue; - default: - throw VariantTypeException{type, VariantType::number}; - } - } - - const std::string& getStringValue() const - { - switch (type) { - case VariantType::string: - return *(static_cast<std::string*>(objectValue)); - default: - throw VariantTypeException{type, VariantType::string}; - } - } - - const std::vector<Variant>& getArrayValue() const - { - switch (type) { - case VariantType::array: - return *(static_cast<std::vector<Variant>*>(objectValue)); - default: - throw VariantTypeException{type, VariantType::array}; - } - } - - const std::map<std::string, Variant>& getMapValue() const - { - switch (type) { - case VariantType::map: - return *(static_cast<std::map<std::string, Variant>*>(objectValue)); - default: - throw VariantTypeException{type, VariantType::map}; - } - } - - static const char* getTypeName(VariantType type); - - friend std::ostream& operator<< (std::ostream& os, const Variant &v); + static const char *getTypeName(VariantType type); + friend std::ostream &operator<<(std::ostream &os, const Variant &v); }; - /** * Shorthand for a constant representing a "null" as a variant. */ static const Variant VarNull; - } } diff --git a/test/core/script/Function.cpp b/test/core/script/Function.cpp index bd2c9d6..cd6bd84 100644 --- a/test/core/script/Function.cpp +++ b/test/core/script/Function.cpp @@ -25,38 +25,45 @@ namespace script { TEST(HostFunction, callDirect) { - // Local variable int v = 0; - - // Host function which sets the local variable - auto f = createHostFunction( - [&v](const std::vector<Variant> &args, void *data) { - v = args[0].getIntegerValue(); - return VarNull; - }, {ArgumentDescriptor{VariantType::integer}}); - - // Call the host function + HostFunction f{[](const std::vector<Variant> &args, void *data) { + *(static_cast<int*>(data)) = args[0].getIntegerValue(); + return VarNull; + }, {Argument{VariantType::integer}}, &v}; ASSERT_EQ(VariantType::null, f.call({{(int64_t)42}}).getType()); ASSERT_EQ(42, v); } TEST(HostFunction, callDefaults) { - // Local variable int v = 0; - - // Host function which sets the local variable - auto f = createHostFunction( - [&v](const std::vector<Variant> &args, void *data) { - v = args[0].getIntegerValue(); - return Variant{"Hallo Welt"}; - }, {ArgumentDescriptor{VariantType::integer, {(int64_t)42}}}); - - // Call the host function + HostFunction f{[](const std::vector<Variant> &args, void *data) { + *(static_cast<int*>(data)) = args[0].getIntegerValue(); + return Variant{"Hallo Welt"}; + }, {Argument{VariantType::integer, {(int64_t)42}}}, &v}; ASSERT_EQ("Hallo Welt", f.call().getStringValue()); ASSERT_EQ(42, v); } +TEST(Setter, call) +{ + int v = 0; + Setter f{VariantType::integer, [](Variant arg, void *data) { + *(static_cast<int*>(data)) = arg.getIntegerValue(); + }, &v}; + f.call({(int64_t)42}); + ASSERT_EQ(42, v); +} + +TEST(Getter, call) +{ + int v = 42; + Getter f{[](void *data) { + return Variant{int64_t(*(static_cast<int*>(data)))}; + }, &v}; + ASSERT_EQ(v, f.call().getIntegerValue()); +} + } } diff --git a/test/core/script/Variant.cpp b/test/core/script/Variant.cpp index cf8f3c7..f229e3a 100644 --- a/test/core/script/Variant.cpp +++ b/test/core/script/Variant.cpp @@ -19,6 +19,8 @@ #include <gtest/gtest.h> #include <core/script/Variant.hpp> +#include <core/script/Function.hpp> +#include <core/script/Object.hpp> namespace ousia { namespace script { @@ -81,6 +83,21 @@ TEST(Variant, getMapValue) ASSERT_EQ("entry2", (*map.find("key2")).second.getStringValue()); } +TEST(Variant, getFunctionValue) +{ + int64_t i = 0; + HostFunction f{[](const std::vector<Variant> &args, void *data) { + *((int64_t*)data) = args[0].getIntegerValue(); + return Variant{"Hello World"}; + }, &i}; + + Variant v{&f}; + ASSERT_TRUE(v.getFunctionValue() != nullptr); + ASSERT_EQ("Hello World", v.getFunctionValue()->call({{(int64_t)42}}).getStringValue()); + ASSERT_EQ(42, i); +} + + } } |