diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 4773834f..f8217ea4 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1103,22 +1103,30 @@ namespace chaiscript { const auto params = f.second->get_param_types(); + std::vector> typed_params; + + auto func(std::dynamic_pointer_cast(f.second)); + if (func) { + typed_params = func->get_dynamic_param_types().types(); + } + dump_type(params.front()); std::cout << " " << f.first << "("; - for (auto itr = params.begin() + 1; - itr != params.end(); - ) + for (size_t i = 1; i < params.size(); ++i) { - dump_type(*itr); - ++itr; + if (!typed_params.empty() && !typed_params[i-1].first.empty()) { + std::cout << typed_params[i-1].first; + } else { + dump_type(params[i]); + } - if (itr != params.end()) - { + if (i != params.size() - 1) { std::cout << ", "; } } + std::cout << ") \n"; } @@ -1322,88 +1330,157 @@ namespace chaiscript return m_state.m_functions; } - static bool function_less_than(const Proxy_Function &lhs, const Proxy_Function &rhs) noexcept + + static std::vector param_types(const Proxy_Function &t_f) { + assert(t_f); + return t_f->get_param_types(); + } - auto dynamic_lhs(std::dynamic_pointer_cast(lhs)); - auto dynamic_rhs(std::dynamic_pointer_cast(rhs)); + static std::vector> param_types(const std::shared_ptr &t_f) + { + assert(t_f); + const auto types = t_f->get_dynamic_param_types().types(); + std::vector> ret(1); + ret.insert(ret.end(), types.begin(), types.end()); + return ret; + } - if (dynamic_lhs && dynamic_rhs) + static Type_Info type_info(const std::pair &t_ti) + { + return t_ti.second; + } + + static Type_Info type_info(const Type_Info &t_ti) + { + return t_ti; + } + + static std::string dynamic_type_name(const std::pair &t_ti) + { + return t_ti.first.empty()?t_ti.second.name():t_ti.first; + } + + static std::string dynamic_type_name(const Type_Info &ti) + { + return ti.name(); + } + + + template + static bool params_less_than(const LHS &t_lhs, const RHS &t_rhs) { - if (dynamic_lhs->get_guard()) + assert(t_lhs); + assert(t_rhs); + const auto lhsparamtypes = param_types(t_lhs); + const auto rhsparamtypes = param_types(t_rhs); + + const auto lhssize = lhsparamtypes.size(); + const auto rhssize = rhsparamtypes.size(); + + constexpr auto boxed_type = user_type(); + constexpr auto boxed_pod_type = user_type(); + constexpr auto dynamic_type = user_type(); + + for (size_t i = 1; i < lhssize && i < rhssize; ++i) { - return dynamic_rhs->get_guard() ? false : true; - } else { - return false; - } - } + const Type_Info lt = type_info(lhsparamtypes[i]); + const Type_Info rt = type_info(rhsparamtypes[i]); + const std::string ln = dynamic_type_name(lhsparamtypes[i]); + const std::string rn = dynamic_type_name(rhsparamtypes[i]); + + if ( (lt.bare_equal(dynamic_type) || lt.is_undef()) + && (rt.bare_equal(dynamic_type) || rt.is_undef())) + { + + if (!ln.empty() && rn.empty()) { + return true; + } else if (ln.empty() && !rn.empty()) { + return false; + } else if (!ln.empty() && !rn.empty()) { + if (ln < rn) { + return true; + } else if (rn < ln) { + return false; + } + + // the remaining cases are handled by the is_const rules below + } + } + + if (lt.bare_equal(rt) && lt.is_const() == rt.is_const()) + { + continue; // The first two types are essentially the same, next iteration + } + + + // const is after non-const for the same type + if (lt.bare_equal(rt) && lt.is_const() && !rt.is_const()) + { + return false; + } + + if (lt.bare_equal(rt) && !lt.is_const()) + { + return true; + } + + // boxed_values are sorted last + if (lt.bare_equal(boxed_type)) + { + return false; + } + + if (rt.bare_equal(boxed_type)) + { + return true; + } + + if (lt.bare_equal(boxed_pod_type)) + { + return false; + } + + if (rt.bare_equal(boxed_pod_type)) + { + return true; + } + + // otherwise, we want to sort by typeid + return lt < rt; + } + + // if everything else checks out, sort on guard + // + auto dynamic_lhs(std::dynamic_pointer_cast(t_lhs)); + auto dynamic_rhs(std::dynamic_pointer_cast(t_rhs)); + + if (dynamic_lhs && dynamic_rhs) { + if (dynamic_lhs->get_guard() && !dynamic_rhs->get_guard()) { + return true; + } else if (dynamic_rhs->get_guard()) { + return false; + } + } - if (dynamic_lhs && !dynamic_rhs) - { return false; } - if (!dynamic_lhs && dynamic_rhs) + static bool function_less_than(const Proxy_Function &lhs, const Proxy_Function &rhs) noexcept + { + auto dynamic_lhs(std::dynamic_pointer_cast(lhs)); + auto dynamic_rhs(std::dynamic_pointer_cast(rhs)); + + if (dynamic_lhs && dynamic_rhs) { - return true; + return params_less_than(dynamic_lhs, dynamic_rhs); + } else if (dynamic_lhs) { + return params_less_than(dynamic_lhs, rhs); + } else if (dynamic_rhs) { + return params_less_than(lhs, dynamic_rhs); + } else { + return params_less_than(lhs, rhs); } - - const auto &lhsparamtypes = lhs->get_param_types(); - const auto &rhsparamtypes = rhs->get_param_types(); - - const auto lhssize = lhsparamtypes.size(); - const auto rhssize = rhsparamtypes.size(); - - constexpr const auto boxed_type = user_type(); - constexpr const auto boxed_pod_type = user_type(); - - for (size_t i = 1; i < lhssize && i < rhssize; ++i) - { - const Type_Info < = lhsparamtypes[i]; - const Type_Info &rt = rhsparamtypes[i]; - - if (lt.bare_equal(rt) && lt.is_const() == rt.is_const()) - { - continue; // The first two types are essentially the same, next iteration - } - - // const is after non-const for the same type - if (lt.bare_equal(rt) && lt.is_const() && !rt.is_const()) - { - return false; - } - - if (lt.bare_equal(rt) && !lt.is_const()) - { - return true; - } - - // boxed_values are sorted last - if (lt.bare_equal(boxed_type)) - { - return false; - } - - if (rt.bare_equal(boxed_type)) - { - return true; - } - - if (lt.bare_equal(boxed_pod_type)) - { - return false; - } - - if (rt.bare_equal(boxed_pod_type)) - { - return true; - } - - // otherwise, we want to sort by typeid - return lt < rt; - } - - return false; } diff --git a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp index 0a443ff8..eacfb51a 100644 --- a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp @@ -39,7 +39,7 @@ namespace chaiscript /// A Proxy_Function implementation designed for calling a function /// that is automatically guarded based on the first param based on the /// param's type name - class Dynamic_Object_Function final : public Proxy_Function_Base + class Dynamic_Object_Function : public Proxy_Function_Base, public Dynamic_Function_Interface { public: Dynamic_Object_Function( @@ -71,6 +71,17 @@ namespace chaiscript Dynamic_Object_Function &operator=(const Dynamic_Object_Function) = delete; Dynamic_Object_Function(Dynamic_Object_Function &) = delete; + Param_Types get_dynamic_param_types() const noexcept override { + auto dynamic(std::dynamic_pointer_cast(m_func)); + + if (dynamic) { + return dynamic->get_dynamic_param_types(); + } else { + return Param_Types(get_param_types()); + } + } + + bool operator==(const Proxy_Function_Base &f) const noexcept override { if (const auto *df = dynamic_cast(&f)) @@ -173,7 +184,7 @@ namespace chaiscript * that is automatically guarded based on the first param based on the * param's type name */ - class Dynamic_Object_Constructor final : public Proxy_Function_Base + class Dynamic_Object_Constructor final : public Proxy_Function_Base, public Dynamic_Function_Interface { public: Dynamic_Object_Constructor( @@ -182,6 +193,7 @@ namespace chaiscript : Proxy_Function_Base(build_type_list(t_func->get_param_types()), t_func->get_arity() - 1), m_type_name(std::move(t_type_name)), m_func(t_func) { + assert( t_func ); assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); } @@ -199,6 +211,17 @@ namespace chaiscript return std::vector(begin, end); } + Param_Types get_dynamic_param_types() const noexcept override { + auto dynamic(std::dynamic_pointer_cast(m_func)); + + if (dynamic) { + return dynamic->get_dynamic_param_types(); + } else { + return Param_Types(get_param_types()); + } + } + + bool operator==(const Proxy_Function_Base &f) const noexcept override { const Dynamic_Object_Constructor *dc = dynamic_cast(&f); diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index b2383ce1..f1797417 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -55,6 +55,12 @@ namespace chaiscript : m_has_types(false) {} + explicit Param_Types(const std::vector &t_types) + : m_types(build_param_types(t_types)), + m_has_types(false) + { + } + explicit Param_Types(std::vector> t_types) : m_types(std::move(t_types)), m_has_types(false) @@ -62,6 +68,18 @@ namespace chaiscript update_has_types(); } + static std::vector> build_param_types(const std::vector &t_types) + { + std::vector> retval; + std::transform(t_types.begin(), t_types.end(), std::back_inserter(retval), + [](const Type_Info &ti){ + return std::make_pair(std::string(), ti); + } + ); + + return retval; + } + void push_front(std::string t_name, Type_Info t_ti) { m_types.emplace(m_types.begin(), std::move(t_name), t_ti); @@ -342,11 +360,18 @@ namespace chaiscript namespace dispatch { + class Dynamic_Function_Interface + { + public: + virtual ~Dynamic_Function_Interface() {} + virtual Param_Types get_dynamic_param_types() const = 0; + }; + /** * A Proxy_Function implementation that is not type safe, the called function * is expecting a vector that it works with how it chooses. */ - class Dynamic_Proxy_Function : public Proxy_Function_Base + class Dynamic_Proxy_Function : public Proxy_Function_Base, public Dynamic_Function_Interface { public: Dynamic_Proxy_Function( @@ -401,6 +426,9 @@ namespace chaiscript } } + Param_Types get_dynamic_param_types() const override { + return m_param_types; + } protected: bool test_guard(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const @@ -943,6 +971,9 @@ namespace chaiscript std::vector> ordered_funcs; ordered_funcs.reserve(funcs.size()); + const constexpr auto boxed_type = user_type(); + const constexpr auto dynamic_type = user_type(); + for (const auto &func : funcs) { const auto arity = func->get_arity(); @@ -954,7 +985,10 @@ namespace chaiscript size_t numdiffs = 0; for (size_t i = 0; i < plist.size(); ++i) { - if (!func->get_param_types()[i+1].bare_equal(plist[i].get_type_info())) + const auto &p_type = plist[i].get_type_info(); + const auto &f_type = func->get_param_types()[i+1]; + + if (!(f_type.bare_equal(boxed_type) && p_type.bare_equal(dynamic_type)) && !f_type.bare_equal(p_type)) { ++numdiffs; } diff --git a/unittests/clone_object.chai b/unittests/clone_object.chai new file mode 100644 index 00000000..78c38a29 --- /dev/null +++ b/unittests/clone_object.chai @@ -0,0 +1,38 @@ +GLOBAL clone_count = 0; + +class Cloneable +{ + def Cloneable() { + } + +} + + +def clone(Cloneable c) +{ + print("Clone called"); + ++clone_count; + return c; +} + + +class MyObject +{ + def MyObject() { + this.data = Cloneable(); + } + + var data; +} + + +assert_equal(0, clone_count); + +var o = MyObject(); + +assert_equal(0, clone_count); + +var p = o; + +assert_equal(1, clone_count); + diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 6e241572..48e5b378 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -321,7 +321,8 @@ TEST_CASE("Function ordering") chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.eval("def test_fun(x) { return 3; }"); chai.eval("def test_fun(x) : x == \"hi\" { return 4; }"); -// chai.eval("def test_fun(x) { return 5; }"); + chai.eval("def test_fun(double d) { return 5; }"); + chai.add(chaiscript::fun(&function_ordering_test_one), "test_fun"); chai.add(chaiscript::fun(&function_ordering_test_two), "test_fun"); @@ -329,6 +330,7 @@ TEST_CASE("Function ordering") CHECK(chai.eval("auto i = 1; test_fun(i)") == 2); CHECK(chai.eval("test_fun(\"bob\")") == 3); CHECK(chai.eval("test_fun(\"hi\")") == 4); + CHECK(chai.eval("test_fun(5.0)") == 5); }