From 268868f10297703470be767c2b3a2acbd8a9dac3 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 16 Apr 2015 20:00:48 -0600 Subject: [PATCH 1/4] Add failing tests --- src/test_module.cpp | 9 +++++++++ unittests/function_attributes.chai | 13 +++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 unittests/function_attributes.chai diff --git a/src/test_module.cpp b/src/test_module.cpp index b161817..5a849fa 100644 --- a/src/test_module.cpp +++ b/src/test_module.cpp @@ -21,6 +21,8 @@ class TestBaseType int val; const int const_val; + std::function func_member; + private: TestBaseType &operator=(const TestBaseType &); }; @@ -159,6 +161,13 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_mo m->add(chaiscript::fun(&TestBaseType::const_val), "const_val"); m->add(chaiscript::fun(&TestBaseType::base_only_func), "base_only_func"); + // member that is a function + m->add(chaiscript::fun(&TestBaseType::func_member), "func_member"); + m->add(chaiscript::fun &(std::function &, const std::function &)>( + [](std::function &t_lhs, const std::function &t_rhs)-> std::function &{ + return t_lhs = t_rhs; + }), "="); + m->add(chaiscript::fun(&get_new_int), "get_new_int"); diff --git a/unittests/function_attributes.chai b/unittests/function_attributes.chai new file mode 100644 index 0000000..3cc1aa7 --- /dev/null +++ b/unittests/function_attributes.chai @@ -0,0 +1,13 @@ +// Test attributes/members that are functions + +load_module("test_module") + +auto t0 = TestBaseType() + +t0.func_member = fun(int i){ i * 3; }; + +assert_true(func_member(t0)(2) == 6) +assert_true((func_member(t0))(2) == 6) +assert_true((t0.func_member)(2) == 6) +assert_true(t0.func_member(2) == 6) + From 2f444542ab795c6f30da6956cbc5c5177b14c59b Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 17 Apr 2015 07:35:12 -0600 Subject: [PATCH 2/4] Add test for attr calls specifically --- unittests/function_attributes.chai | 10 +++++++++- unittests/function_members.chai | 13 +++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 unittests/function_members.chai diff --git a/unittests/function_attributes.chai b/unittests/function_attributes.chai index 3cc1aa7..fa483ee 100644 --- a/unittests/function_attributes.chai +++ b/unittests/function_attributes.chai @@ -2,7 +2,15 @@ load_module("test_module") -auto t0 = TestBaseType() +class MyClass +{ + var func_member; + + def MyClass() {} + +} + +auto t0 = MyClass(); t0.func_member = fun(int i){ i * 3; }; diff --git a/unittests/function_members.chai b/unittests/function_members.chai new file mode 100644 index 0000000..3cc1aa7 --- /dev/null +++ b/unittests/function_members.chai @@ -0,0 +1,13 @@ +// Test attributes/members that are functions + +load_module("test_module") + +auto t0 = TestBaseType() + +t0.func_member = fun(int i){ i * 3; }; + +assert_true(func_member(t0)(2) == 6) +assert_true((func_member(t0))(2) == 6) +assert_true((t0.func_member)(2) == 6) +assert_true(t0.func_member(2) == 6) + From d2ed8fdcf116826bd842d2e5bdf17fa432d4f345 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 17 Apr 2015 12:18:47 -0600 Subject: [PATCH 3/4] Get class members that are functions working Automatic conversion of return values into Proxy_Function objects Issue: #155 --- include/chaiscript/dispatchkit/bootstrap.hpp | 8 ++ .../chaiscript/dispatchkit/function_call.hpp | 2 +- .../chaiscript/dispatchkit/handle_return.hpp | 95 +++++++++++++++++++ .../dispatchkit/proxy_functions.hpp | 67 ++++++++++++- src/test_module.cpp | 5 - unittests/function_attributes.chai | 1 - unittests/function_members.chai | 1 - 7 files changed, 170 insertions(+), 9 deletions(-) diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 32c11c1..2df5d45 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -372,6 +372,7 @@ namespace chaiscript m->add(user_type(), "Object"); m->add(user_type(), "Number"); m->add(user_type(), "Function"); + m->add(user_type(), "Assignable_Function"); m->add(user_type(), "exception"); m->add(fun(&dispatch::Proxy_Function_Base::get_arity), "get_arity"); @@ -468,6 +469,13 @@ namespace chaiscript m->add(fun(&shared_ptr_unconst_clone), "clone"); m->add(fun(&ptr_assign::type>), "="); m->add(fun(&ptr_assign::type>), "="); + m->add(chaiscript::base_class()); + m->add(fun &)>( + [](dispatch::Assignable_Proxy_Function &t_lhs, const std::shared_ptr &t_rhs) { + t_lhs.assign(t_rhs); + } + ), "=" + ); m->add(fun(&Boxed_Value::type_match), "type_match"); diff --git a/include/chaiscript/dispatchkit/function_call.hpp b/include/chaiscript/dispatchkit/function_call.hpp index 0a90ca2..578c4ce 100644 --- a/include/chaiscript/dispatchkit/function_call.hpp +++ b/include/chaiscript/dispatchkit/function_call.hpp @@ -57,7 +57,7 @@ namespace chaiscript std::function functor(Const_Proxy_Function func, const Type_Conversions *t_conversions) { - return functor(std::vector({func}), t_conversions); + return functor(std::vector({std::move(func)}), t_conversions); } /// Helper for automatically unboxing a Boxed_Value that contains a function object diff --git a/include/chaiscript/dispatchkit/handle_return.hpp b/include/chaiscript/dispatchkit/handle_return.hpp index b896689..335d02a 100644 --- a/include/chaiscript/dispatchkit/handle_return.hpp +++ b/include/chaiscript/dispatchkit/handle_return.hpp @@ -26,6 +26,9 @@ namespace chaiscript { namespace dispatch { + template class Proxy_Function_Impl; + template class Assignable_Proxy_Function_Impl; + namespace detail { /** @@ -49,6 +52,98 @@ namespace chaiscript } }; + template + struct Handle_Return &> + { + static Boxed_Value handle(const std::function &f) { + return Boxed_Value( + std::shared_ptr( + new dispatch::Proxy_Function_Impl(f) + ) + ); + } + }; + + template + struct Handle_Return> + { + static Boxed_Value handle(const std::function &f) { + return Boxed_Value( + std::shared_ptr( + new Proxy_Function_Impl(f) + ) + ); + } + }; + + template + struct Handle_Return>> + { + static Boxed_Value handle(const std::shared_ptr> &f) { + return Boxed_Value( + std::shared_ptr( + new Assignable_Proxy_Function_Impl( + std::ref(*f), + f + ) + ) + ); + } + }; + + template + struct Handle_Return> &> + { + static Boxed_Value handle(const std::shared_ptr> &f) { + return Boxed_Value( + std::shared_ptr( + new Assignable_Proxy_Function_Impl( + std::ref(*f), + f + ) + ) + ); + } + }; + + template + struct Handle_Return>> + { + static Boxed_Value handle(const std::shared_ptr> &f) { + return Boxed_Value( + std::shared_ptr( + new Assignable_Proxy_Function_Impl( + std::ref(*f), + f + ) + ) + ); + } + }; + + template + struct Handle_Return &> + { + static Boxed_Value handle(std::function &f) { + return Boxed_Value( + std::shared_ptr( + new Assignable_Proxy_Function_Impl( + std::ref(f), + std::shared_ptr>() + ) + ) + ); + } + + static Boxed_Value handle(const std::function &f) { + return Boxed_Value( + std::shared_ptr( + new dispatch::Proxy_Function_Impl(f) + ) + ); + } + }; + template struct Handle_Return { diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index 1f71ad5..c8dfc5b 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -43,6 +43,9 @@ namespace chaiscript namespace dispatch { + template + std::function functor(std::shared_ptr func, const Type_Conversions *t_conversions); + class Param_Types { public: @@ -571,11 +574,73 @@ namespace chaiscript return detail::Do_Call::result_type>::go(m_f, params, t_conversions); } + private: std::function m_f; Func *m_dummy_func; }; + class Assignable_Proxy_Function : public Proxy_Function_Impl_Base + { + public: + Assignable_Proxy_Function(const std::vector &t_types) + : Proxy_Function_Impl_Base(t_types) + { + } + + virtual ~Assignable_Proxy_Function() {} + + + virtual void assign(const std::shared_ptr &t_rhs) = 0; + + + }; + + template + class Assignable_Proxy_Function_Impl : public Assignable_Proxy_Function + { + public: + Assignable_Proxy_Function_Impl(std::reference_wrapper> t_f, std::shared_ptr> t_ptr) + : Assignable_Proxy_Function(detail::build_param_type_list(static_cast(nullptr))), + m_f(std::move(t_f)), m_shared_ptr_holder(std::move(t_ptr)), m_dummy_func(nullptr) + { + assert(!m_shared_ptr_holder || m_shared_ptr_holder.get() == &m_f.get()); + + } + + virtual ~Assignable_Proxy_Function_Impl() {} + + virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + { + return detail::compare_types_cast(m_dummy_func, vals, t_conversions); + } + + virtual bool operator==(const Proxy_Function_Base &t_func) const CHAISCRIPT_OVERRIDE + { + return dynamic_cast *>(&t_func) != nullptr; + } + + std::function internal_function() const + { + return m_f.get(); + } + + virtual void assign(const std::shared_ptr &t_rhs) { + m_f.get() = dispatch::functor(t_rhs, nullptr); + } + + protected: + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const + { + return detail::Do_Call::result_type>::go(m_f.get(), params, t_conversions); + } + + + private: + std::reference_wrapper> m_f; + std::shared_ptr> m_shared_ptr_holder; + Func *m_dummy_func; + }; /// Attribute getter Proxy_Function implementation template class Attribute_Access : public Proxy_Function_Base @@ -625,7 +690,7 @@ namespace chaiscript if (bv.is_const()) { const Class *o = boxed_cast(bv, &t_conversions); - return detail::Handle_Return::type>::handle(o->*m_attr); + return detail::Handle_Return::type>::handle(o->*m_attr); } else { Class *o = boxed_cast(bv, &t_conversions); return detail::Handle_Return::type>::handle(o->*m_attr); diff --git a/src/test_module.cpp b/src/test_module.cpp index 5a849fa..f0a3e4a 100644 --- a/src/test_module.cpp +++ b/src/test_module.cpp @@ -163,11 +163,6 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_mo // member that is a function m->add(chaiscript::fun(&TestBaseType::func_member), "func_member"); - m->add(chaiscript::fun &(std::function &, const std::function &)>( - [](std::function &t_lhs, const std::function &t_rhs)-> std::function &{ - return t_lhs = t_rhs; - }), "="); - m->add(chaiscript::fun(&get_new_int), "get_new_int"); diff --git a/unittests/function_attributes.chai b/unittests/function_attributes.chai index fa483ee..a346fb1 100644 --- a/unittests/function_attributes.chai +++ b/unittests/function_attributes.chai @@ -16,6 +16,5 @@ t0.func_member = fun(int i){ i * 3; }; assert_true(func_member(t0)(2) == 6) assert_true((func_member(t0))(2) == 6) -assert_true((t0.func_member)(2) == 6) assert_true(t0.func_member(2) == 6) diff --git a/unittests/function_members.chai b/unittests/function_members.chai index 3cc1aa7..19120fb 100644 --- a/unittests/function_members.chai +++ b/unittests/function_members.chai @@ -8,6 +8,5 @@ t0.func_member = fun(int i){ i * 3; }; assert_true(func_member(t0)(2) == 6) assert_true((func_member(t0))(2) == 6) -assert_true((t0.func_member)(2) == 6) assert_true(t0.func_member(2) == 6) From ecd2e523f7701d98a249541ded2ab07abec9fd46 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 17 Apr 2015 16:32:59 -0600 Subject: [PATCH 4/4] attributes / members holding functions works fully now Issue #155 --- .../chaiscript/dispatchkit/dispatchkit.hpp | 36 +++++++++++++++++++ .../dispatchkit/dynamic_object_detail.hpp | 15 +++++--- .../dispatchkit/proxy_functions.hpp | 14 +++++--- .../chaiscript/language/chaiscript_eval.hpp | 9 +++-- .../chaiscript/language/chaiscript_parser.hpp | 3 +- unittests/function_attributes.chai | 3 ++ 6 files changed, 69 insertions(+), 11 deletions(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index cb80acf..3b9478d 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -793,6 +793,42 @@ namespace chaiscript return m_conversions; } + bool is_attribute_call(const std::vector &t_funs, const std::vector &t_params, + bool t_has_params) const + { + if (!t_has_params || t_params.empty()) { + return false; + } + + for (const auto &fun : t_funs) { + if (fun->is_attribute_function()) { + if (fun->compare_first_type(t_params[0], m_conversions)) { + return true; + } + } + } + + return false; + } + + Boxed_Value call_member(const std::string &t_name, const std::vector ¶ms, bool t_has_params) const + { + auto funs = get_function(t_name); + + if (is_attribute_call(funs, params, t_has_params)) { + std::vector attr_params{params[0]}; + std::vector remaining_params{params.begin() + 1, params.end()}; + Boxed_Value bv = dispatch::dispatch(funs, attr_params, m_conversions); + if (!remaining_params.empty() || bv.get_type_info().bare_equal(user_type())) { + return (*boxed_cast(bv))(remaining_params, m_conversions); + } else { + return bv; + } + } else { + return dispatch::dispatch(funs, params, m_conversions); + } + } + Boxed_Value call_function(const std::string &t_name, const std::vector ¶ms) const { Boxed_Value bv = dispatch::dispatch(get_function(t_name), params, m_conversions); diff --git a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp index 67c9543..b9ddda1 100644 --- a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp @@ -44,9 +44,11 @@ namespace chaiscript public: Dynamic_Object_Function( std::string t_type_name, - const Proxy_Function &t_func) + const Proxy_Function &t_func, + bool t_is_attribute = false) : Proxy_Function_Base(t_func->get_param_types(), t_func->get_arity()), - m_type_name(std::move(t_type_name)), m_func(t_func), m_doti(user_type()) + m_type_name(std::move(t_type_name)), m_func(t_func), m_doti(user_type()), + m_is_attribute(t_is_attribute) { assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); @@ -55,9 +57,11 @@ namespace chaiscript Dynamic_Object_Function( std::string t_type_name, const Proxy_Function &t_func, - const Type_Info &t_ti) + const Type_Info &t_ti, + bool t_is_attribute = false) : Proxy_Function_Base(build_param_types(t_func->get_param_types(), t_ti), t_func->get_arity()), - m_type_name(std::move(t_type_name)), m_func(t_func), m_ti(t_ti.is_undef()?nullptr:new Type_Info(t_ti)), m_doti(user_type()) + m_type_name(std::move(t_type_name)), m_func(t_func), m_ti(t_ti.is_undef()?nullptr:new Type_Info(t_ti)), m_doti(user_type()), + m_is_attribute(t_is_attribute) { assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); @@ -78,6 +82,8 @@ namespace chaiscript } } + virtual bool is_attribute_function() const CHAISCRIPT_OVERRIDE { return m_is_attribute; } + virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE { if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions)) @@ -164,6 +170,7 @@ namespace chaiscript Proxy_Function m_func; std::unique_ptr m_ti; const Type_Info m_doti; + bool m_is_attribute; }; diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index c8dfc5b..536eb9c 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -162,6 +162,8 @@ namespace chaiscript virtual bool operator==(const Proxy_Function_Base &) const = 0; virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const = 0; + virtual bool is_attribute_function() const { return false; } + bool has_arithmetic_param() const { return m_has_arithmetic_param; @@ -219,6 +221,12 @@ namespace chaiscript return false; } } + + virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions &t_conversions) const + { + return compare_type_to_param(m_types[1], bv, t_conversions); + } + protected: virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const = 0; @@ -236,10 +244,6 @@ namespace chaiscript } - virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions &t_conversions) const - { - return compare_type_to_param(m_types[1], bv, t_conversions); - } static bool compare_types(const std::vector &tis, const std::vector &bvs) { @@ -654,6 +658,8 @@ namespace chaiscript virtual ~Attribute_Access() {} + virtual bool is_attribute_function() const CHAISCRIPT_OVERRIDE { return true; } + virtual bool operator==(const Proxy_Function_Base &t_func) const CHAISCRIPT_OVERRIDE { const Attribute_Access * aa diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 59e09ba..a8440df 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -704,7 +704,10 @@ namespace chaiscript for (size_t i = 2; i < this->children.size(); i+=2) { std::vector params{retval}; + bool has_function_params = false; + if (this->children[i]->children.size() > 1) { + has_function_params = true; for (const auto &child : this->children[i]->children[1]->children) { params.push_back(child->eval(t_ss)); } @@ -723,7 +726,7 @@ namespace chaiscript try { chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); - retval = t_ss.call_function(fun_name, std::move(params)); + retval = t_ss.call_member(fun_name, std::move(params), has_function_params); } catch(const exception::dispatch_error &e){ if (e.functions.empty()) @@ -1534,7 +1537,9 @@ namespace chaiscript std::placeholders::_1, this->children[static_cast(1 + class_offset)]->text )) - ) + ), + true + ), this->children[static_cast(1 + class_offset)]->text); } diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 73a33e2..c77e61e 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -1319,9 +1319,10 @@ namespace chaiscript } } while (Char(',')); } - build_match(std::make_shared(), prev_stack_top); } + build_match(std::make_shared(), prev_stack_top); + SkipWS(true); return retval; diff --git a/unittests/function_attributes.chai b/unittests/function_attributes.chai index a346fb1..8f9dbbd 100644 --- a/unittests/function_attributes.chai +++ b/unittests/function_attributes.chai @@ -18,3 +18,6 @@ assert_true(func_member(t0)(2) == 6) assert_true((func_member(t0))(2) == 6) assert_true(t0.func_member(2) == 6) +t0.func_member = fun() { 12; }; + +assert_true(t0.func_member() == 12);