From f894bca836c0bbb0d2318ad18a6e0aff52e77908 Mon Sep 17 00:00:00 2001 From: leftibot Date: Sat, 2 May 2026 12:48:28 -0600 Subject: [PATCH] Fix #612: Allow fun(&overloaded) for overload disambiguation The cheatsheet's "preferred" syntax for binding overloaded free functions, fun(&overloaded), only worked on MSVC because the existing fun(T&&) overload sets T to a function type when called explicitly, which produces a function rvalue-reference parameter that does not match a function pointer argument. Standards-conforming compilers (clang, GCC in some modes) correctly rejected it. Added two new fun overloads, parameterized on the explicitly-supplied function-type Signature and using std::type_identity_t to keep that parameter non-deducible, so they only participate in overload resolution when Signature is given explicitly. One accepts a free function pointer, the other a pointer-to-member, enabling both fun(&free_fn) and fun(&Class::method) to disambiguate overloads without resorting to static_cast. --- .../dispatchkit/register_function.hpp | 37 ++++++++++ unittests/compiled_tests.cpp | 68 +++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/include/chaiscript/dispatchkit/register_function.hpp b/include/chaiscript/dispatchkit/register_function.hpp index a22280a9..b8c2b00a 100644 --- a/include/chaiscript/dispatchkit/register_function.hpp +++ b/include/chaiscript/dispatchkit/register_function.hpp @@ -82,6 +82,43 @@ namespace chaiscript { return dispatch::detail::make_callable(std::forward(t), dispatch::detail::function_signature(t)); } + /// \brief Creates a new Proxy_Function object from an overloaded free function, with the + /// signature specified explicitly to disambiguate the overload. + /// + /// \b Example: + /// \code + /// int overloaded(int); + /// double overloaded(double); + /// + /// chai.add(chaiscript::fun(&overloaded), "overloaded"); + /// chai.add(chaiscript::fun(&overloaded), "overloaded"); + /// \endcode + template, int> = 0> + Proxy_Function fun(std::type_identity_t *f) { + return dispatch::detail::make_callable(f, dispatch::detail::function_signature(f)); + } + + /// \brief Creates a new Proxy_Function object from an overloaded member function, with the + /// signature specified explicitly to disambiguate the overload. + /// + /// \b Example: + /// \code + /// class MyClass { + /// public: + /// int overloaded(int); + /// double overloaded(double); + /// int const_overloaded() const; + /// }; + /// + /// chai.add(chaiscript::fun(&MyClass::overloaded), "overloaded"); + /// chai.add(chaiscript::fun(&MyClass::overloaded), "overloaded"); + /// chai.add(chaiscript::fun(&MyClass::const_overloaded), "const_overloaded"); + /// \endcode + template, int> = 0> + Proxy_Function fun(std::type_identity_t Class::*f) { + return dispatch::detail::make_callable(f, dispatch::detail::function_signature(f)); + } + /// \brief Creates a new Proxy_Function object from a free function, member function or data member and binds the first parameter of it /// \param[in] t Function / member to expose /// \param[in] q Value to bind to first parameter diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index e4b14b4e..ce54cf5b 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -581,6 +581,74 @@ TEST_CASE("Utility_Test utility class wrapper") { chai.eval("t = Utility_Test();"); } +///// Issue 612: fun(&overloaded) should work for free and member function overloads + +namespace issue_612 { + std::string free_overload(int) { return "int"; } + std::string free_overload(double) { return "double"; } + std::string free_overload(const std::string &, bool) { return "string,bool"; } + + class Issue_612_Class { + public: + std::string member_overload(int) { return "int"; } + std::string member_overload(double) { return "double"; } + std::string const_member_overload(int) const { return "const_int"; } + std::string const_member_overload(double) const { return "const_double"; } + }; +} // namespace issue_612 + +TEST_CASE("Issue 612: fun for overloaded free functions") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + + chai.add(chaiscript::fun(&issue_612::free_overload), "free_overload"); + chai.add(chaiscript::fun(&issue_612::free_overload), "free_overload"); + chai.add(chaiscript::fun(&issue_612::free_overload), "free_overload"); + + CHECK(chai.eval("free_overload(1)") == "int"); + CHECK(chai.eval("free_overload(1.5)") == "double"); + CHECK(chai.eval("free_overload(\"hi\", true)") == "string,bool"); +} + +TEST_CASE("Issue 612: fun for overloaded member functions (pointer-to-member form)") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + + chai.add(chaiscript::user_type(), "Issue_612_Class"); + chai.add(chaiscript::constructor(), "Issue_612_Class"); + + chai.add(chaiscript::fun(&issue_612::Issue_612_Class::member_overload), + "member_overload"); + chai.add(chaiscript::fun(&issue_612::Issue_612_Class::member_overload), + "member_overload"); + chai.add(chaiscript::fun(&issue_612::Issue_612_Class::const_member_overload), + "const_member_overload"); + chai.add(chaiscript::fun(&issue_612::Issue_612_Class::const_member_overload), + "const_member_overload"); + + chai.eval("var t = Issue_612_Class();"); + CHECK(chai.eval("t.member_overload(1)") == "int"); + CHECK(chai.eval("t.member_overload(1.5)") == "double"); + CHECK(chai.eval("t.const_member_overload(1)") == "const_int"); + CHECK(chai.eval("t.const_member_overload(1.5)") == "const_double"); +} + +TEST_CASE("Issue 612: fun for overloaded member functions (function-type form)") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + + chai.add(chaiscript::user_type(), "Issue_612_Class"); + chai.add(chaiscript::constructor(), "Issue_612_Class"); + + chai.add(chaiscript::fun(&issue_612::Issue_612_Class::member_overload), "member_overload"); + chai.add(chaiscript::fun(&issue_612::Issue_612_Class::member_overload), "member_overload"); + chai.add(chaiscript::fun(&issue_612::Issue_612_Class::const_member_overload), "const_member_overload"); + chai.add(chaiscript::fun(&issue_612::Issue_612_Class::const_member_overload), "const_member_overload"); + + chai.eval("var t = Issue_612_Class();"); + CHECK(chai.eval("t.member_overload(1)") == "int"); + CHECK(chai.eval("t.member_overload(1.5)") == "double"); + CHECK(chai.eval("t.const_member_overload(1)") == "const_int"); + CHECK(chai.eval("t.const_member_overload(1.5)") == "const_double"); +} + enum Utility_Test_Numbers { ONE, TWO,