From 7b42d5307a08f230f53a8524ea4f29cc3a9f6734 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 28 Oct 2014 14:52:24 -0600 Subject: [PATCH] Add ability to register a user defined type conversion Currently this adds a fair bit of overhead. It will need to be evaluated further before it's merged. --- CMakeLists.txt | 2 +- include/chaiscript/dispatchkit/bootstrap.hpp | 2 +- include/chaiscript/dispatchkit/boxed_cast.hpp | 4 +- .../chaiscript/dispatchkit/dispatchkit.hpp | 2 +- .../dispatchkit/function_call_detail.hpp | 2 +- ...st_conversion.hpp => type_conversions.hpp} | 43 ++++++++++++++++--- .../chaiscript/language/chaiscript_engine.hpp | 9 +++- unittests/user_defined_conversions.chai | 11 +++++ 8 files changed, 62 insertions(+), 13 deletions(-) rename include/chaiscript/dispatchkit/{dynamic_cast_conversion.hpp => type_conversions.hpp} (89%) create mode 100644 unittests/user_defined_conversions.chai diff --git a/CMakeLists.txt b/CMakeLists.txt index c8ef4b9..720cb73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,7 +154,7 @@ endif() include_directories(include) -set(Chai_INCLUDES include/chaiscript/chaiscript.hpp include/chaiscript/chaiscript_threading.hpp include/chaiscript/dispatchkit/bad_boxed_cast.hpp include/chaiscript/dispatchkit/bind_first.hpp include/chaiscript/dispatchkit/bootstrap.hpp include/chaiscript/dispatchkit/bootstrap_stl.hpp include/chaiscript/dispatchkit/boxed_cast.hpp include/chaiscript/dispatchkit/boxed_cast_helper.hpp include/chaiscript/dispatchkit/boxed_number.hpp include/chaiscript/dispatchkit/boxed_value.hpp include/chaiscript/dispatchkit/dispatchkit.hpp include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp include/chaiscript/dispatchkit/dynamic_object.hpp include/chaiscript/dispatchkit/exception_specification.hpp include/chaiscript/dispatchkit/function_call.hpp include/chaiscript/dispatchkit/function_call_detail.hpp include/chaiscript/dispatchkit/handle_return.hpp include/chaiscript/dispatchkit/operators.hpp include/chaiscript/dispatchkit/proxy_constructors.hpp include/chaiscript/dispatchkit/proxy_functions.hpp include/chaiscript/dispatchkit/proxy_functions_detail.hpp include/chaiscript/dispatchkit/register_function.hpp include/chaiscript/dispatchkit/type_info.hpp include/chaiscript/language/chaiscript_algebraic.hpp include/chaiscript/language/chaiscript_common.hpp include/chaiscript/language/chaiscript_engine.hpp include/chaiscript/language/chaiscript_eval.hpp include/chaiscript/language/chaiscript_parser.hpp include/chaiscript/language/chaiscript_prelude.chai include/chaiscript/language/chaiscript_prelude_docs.hpp include/chaiscript/utility/utility.hpp) +set(Chai_INCLUDES include/chaiscript/chaiscript.hpp include/chaiscript/chaiscript_threading.hpp include/chaiscript/dispatchkit/bad_boxed_cast.hpp include/chaiscript/dispatchkit/bind_first.hpp include/chaiscript/dispatchkit/bootstrap.hpp include/chaiscript/dispatchkit/bootstrap_stl.hpp include/chaiscript/dispatchkit/boxed_cast.hpp include/chaiscript/dispatchkit/boxed_cast_helper.hpp include/chaiscript/dispatchkit/boxed_number.hpp include/chaiscript/dispatchkit/boxed_value.hpp include/chaiscript/dispatchkit/dispatchkit.hpp include/chaiscript/dispatchkit/type_conversions.hpp include/chaiscript/dispatchkit/dynamic_object.hpp include/chaiscript/dispatchkit/exception_specification.hpp include/chaiscript/dispatchkit/function_call.hpp include/chaiscript/dispatchkit/function_call_detail.hpp include/chaiscript/dispatchkit/handle_return.hpp include/chaiscript/dispatchkit/operators.hpp include/chaiscript/dispatchkit/proxy_constructors.hpp include/chaiscript/dispatchkit/proxy_functions.hpp include/chaiscript/dispatchkit/proxy_functions_detail.hpp include/chaiscript/dispatchkit/register_function.hpp include/chaiscript/dispatchkit/type_info.hpp include/chaiscript/language/chaiscript_algebraic.hpp include/chaiscript/language/chaiscript_common.hpp include/chaiscript/language/chaiscript_engine.hpp include/chaiscript/language/chaiscript_eval.hpp include/chaiscript/language/chaiscript_parser.hpp include/chaiscript/language/chaiscript_prelude.chai include/chaiscript/language/chaiscript_prelude_docs.hpp include/chaiscript/utility/utility.hpp) set_source_files_properties(${Chai_INCLUDES} PROPERTIES HEADER_FILE_ONLY TRUE) diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index c4fd18a..98648da 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -24,7 +24,7 @@ #include "boxed_number.hpp" #include "boxed_value.hpp" #include "dispatchkit.hpp" -#include "dynamic_cast_conversion.hpp" +#include "type_conversions.hpp" #include "dynamic_object.hpp" #include "operators.hpp" #include "proxy_constructors.hpp" diff --git a/include/chaiscript/dispatchkit/boxed_cast.hpp b/include/chaiscript/dispatchkit/boxed_cast.hpp index b33d659..f2074d9 100644 --- a/include/chaiscript/dispatchkit/boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast.hpp @@ -14,7 +14,7 @@ #include "bad_boxed_cast.hpp" #include "boxed_cast_helper.hpp" #include "boxed_value.hpp" -#include "dynamic_cast_conversion.hpp" +#include "type_conversions.hpp" #include "type_info.hpp" namespace chaiscript { @@ -86,7 +86,7 @@ namespace chaiscript #pragma warning(disable : 4127) #endif - if (std::is_polymorphic::type>::value && t_conversions) + if ( /*std::is_polymorphic::type>::value && */ t_conversions) { try { // std::cout << "trying an up conversion " << typeid(Type).name() << std::endl; diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index b8d4e19..910987e 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -27,7 +27,7 @@ #include "boxed_cast.hpp" #include "boxed_cast_helper.hpp" #include "boxed_value.hpp" -#include "dynamic_cast_conversion.hpp" +#include "type_conversions.hpp" #include "dynamic_object.hpp" #include "proxy_constructors.hpp" #include "proxy_functions.hpp" diff --git a/include/chaiscript/dispatchkit/function_call_detail.hpp b/include/chaiscript/dispatchkit/function_call_detail.hpp index 2da9b7b..ee1d1bd 100644 --- a/include/chaiscript/dispatchkit/function_call_detail.hpp +++ b/include/chaiscript/dispatchkit/function_call_detail.hpp @@ -17,7 +17,7 @@ #include "boxed_cast.hpp" #include "boxed_number.hpp" #include "boxed_value.hpp" -#include "dynamic_cast_conversion.hpp" +#include "type_conversions.hpp" #include "proxy_functions.hpp" namespace chaiscript diff --git a/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp b/include/chaiscript/dispatchkit/type_conversions.hpp similarity index 89% rename from include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp rename to include/chaiscript/dispatchkit/type_conversions.hpp index 9a90e7c..7036fe5 100644 --- a/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp +++ b/include/chaiscript/dispatchkit/type_conversions.hpp @@ -45,10 +45,7 @@ namespace chaiscript virtual ~bad_boxed_dynamic_cast() CHAISCRIPT_NOEXCEPT {} }; - } - namespace exception - { class bad_boxed_type_cast : public bad_boxed_cast { public: @@ -174,6 +171,32 @@ namespace chaiscript return Dynamic_Caster::cast(t_derived); } }; + + + class Type_Conversion_Impl : public Type_Conversion_Base + { + public: + Type_Conversion_Impl(Type_Info t_from, Type_Info t_to, std::function t_func) + : Type_Conversion_Base(std::move(t_to), std::move(t_from)), + m_func(std::move(t_func)) + { + } + + virtual Boxed_Value convert_down(const Boxed_Value &) const CHAISCRIPT_OVERRIDE + { + throw chaiscript::exception::bad_boxed_type_cast("No conversion exists"); + } + + virtual Boxed_Value convert(const Boxed_Value &t_from) const CHAISCRIPT_OVERRIDE + { + /// \todo better handling of errors from the conversion function + return m_func(t_from); + } + + private: + std::function m_func; + }; + } class Type_Conversions @@ -191,6 +214,7 @@ namespace chaiscript void add_conversion(const std::shared_ptr &conversion) { chaiscript::detail::threading::unique_lock l(m_mutex); + /// \todo error if a conversion already exists m_conversions.insert(conversion); } @@ -240,8 +264,7 @@ namespace chaiscript { chaiscript::detail::threading::shared_lock l(m_mutex); - auto itr = - find(to, from); + auto itr = find(to, from); if (itr != m_conversions.end()) { @@ -283,7 +306,7 @@ namespace chaiscript /// Currently, due to limitations in module loading on Windows, and for the sake of portability, /// if you have a type that is introduced in a loadable module and is used by multiple modules /// (through a tertiary dll that is shared between the modules, static linking the new type - /// into both loadable modules would not be portable), you need to register the to type + /// into both loadable modules would not be portable), you need to register the type /// relationship in all modules that use the newly added type in a polymorphic way. /// /// Example: @@ -309,6 +332,14 @@ namespace chaiscript return std::shared_ptr(new detail::Dynamic_Conversion_Impl()); } + namespace { + Type_Conversion type_conversion(const Type_Info &t_from, const Type_Info &t_to, + const std::function &t_func) + { + return std::shared_ptr(new detail::Type_Conversion_Impl(t_from, t_to, t_func)); + } + } + } diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index b476d29..95c4932 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -26,7 +26,7 @@ #include "../dispatchkit/boxed_cast_helper.hpp" #include "../dispatchkit/boxed_value.hpp" #include "../dispatchkit/dispatchkit.hpp" -#include "../dispatchkit/dynamic_cast_conversion.hpp" +#include "../dispatchkit/type_conversions.hpp" #include "../dispatchkit/proxy_functions.hpp" #include "chaiscript_common.hpp" @@ -353,6 +353,13 @@ namespace chaiscript m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::get_type_name, std::ref(m_engine)), "name"); + m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::get_type, std::ref(m_engine)), "type"); + m_engine.add(fun &)> ( + [=](const Type_Info &t_from, const Type_Info &t_to, const std::function &t_func) { + m_engine.add(chaiscript::type_conversion(t_from, t_to, t_func)); + } + ), "add_type_conversion"); + typedef std::string (ChaiScript::*load_mod_1)(const std::string&); typedef void (ChaiScript::*load_mod_2)(const std::string&, const std::string&); diff --git a/unittests/user_defined_conversions.chai b/unittests/user_defined_conversions.chai new file mode 100644 index 0000000..e73e92a --- /dev/null +++ b/unittests/user_defined_conversions.chai @@ -0,0 +1,11 @@ + +add_type_conversion(type("string"), type("Type_Info"), fun(s) { return type(s); }); + +// This looks simple, but it takes the string "string" and using the registered +// conversion above, automatically converts that into a Type_Info object, which then +// allows the Type_Info.name() function to be called + +assert_equal("string".name(), "string"); + + +