diff --git a/simgear/nasal/cppbind/CMakeLists.txt b/simgear/nasal/cppbind/CMakeLists.txt index 5ef4a5aa..d25679a6 100644 --- a/simgear/nasal/cppbind/CMakeLists.txt +++ b/simgear/nasal/cppbind/CMakeLists.txt @@ -15,7 +15,6 @@ set(HEADERS set(DETAIL_HEADERS detail/from_nasal_function_templates.hxx detail/from_nasal_helper.hxx - detail/functor_templates.hxx detail/nasal_traits.hxx detail/to_nasal_helper.hxx ) @@ -51,4 +50,4 @@ add_boost_test(nasal_gc_test add_boost_test(nasal_num SOURCES test/nasal_num_test.cxx LIBRARIES ${TEST_LIBS} -) \ No newline at end of file +) diff --git a/simgear/nasal/cppbind/Ghost.hxx b/simgear/nasal/cppbind/Ghost.hxx index bab88891..f09e79c6 100644 --- a/simgear/nasal/cppbind/Ghost.hxx +++ b/simgear/nasal/cppbind/Ghost.hxx @@ -24,6 +24,7 @@ #include "NasalObjectHolder.hxx" #include +#include #include #include @@ -32,7 +33,6 @@ #include #include #include -#include #include #include @@ -259,6 +259,9 @@ namespace nasal const std::string&, naRef )> fallback_setter_t; + template + using method_variadic_t = boost::function; + class MethodHolder: public internal::MethodHolder { @@ -791,14 +794,73 @@ namespace nasal return method(name, boost::bind(method_invoker, func, _1, _2)); } - // Build dependency for CMake, gcc, etc. -#define SG_DONT_DO_ANYTHING -# include -#undef SG_DONT_DO_ANYTHING + /** + * Bind any callable entity accepting an instance of raw_type and an + * arbitrary number of arguments as method. + * + * The std::index_sequence specifies the order of the arguments + */ + template + Ghost& method( const std::string& name, + const method_variadic_t& func, + std::index_sequence ) + { + return method + ( + name, + typename boost::function + ( boost::bind( + func, + _1, + boost::bind(&Ghost::arg_from_nasal, _2, Indices)... + )) + ); + } -#define BOOST_PP_ITERATION_LIMITS (0, 9) -#define BOOST_PP_FILENAME_1 -#include BOOST_PP_ITERATE() + template + Ghost& method( const std::string& name, + const method_variadic_t& func ) + { + return method(name, func, std::index_sequence_for{}); + } + + /**\ + * Bind a member function with an arbitrary number of arguments as method. + */ + template + Ghost& method( const std::string& name, + Ret (raw_type::*fn)(Args...) ) + { + return method(name, method_variadic_t(fn)); + } + + template + Ghost& method( const std::string& name, + Ret (raw_type::*fn)(Args...) const ) + { + return method(name, method_variadic_t(fn)); + } + + /** + * Bind free function accepting an instance of raw_type and an arbitrary + * number of arguments as method. + */ + template + Ghost& method + ( + const std::string& name, + Ret (*fn)(Type, Args ... args) + ) + { + static_assert( + boost::is_convertible::value, + //|| boost::is_convertible::value + // TODO check how to do it with pointer... + "First parameter can not be converted from the Ghost raw_type" + ); + + return method(name, method_variadic_t(fn)); + } /** * Create a shared pointer on the heap to handle the reference counting diff --git a/simgear/nasal/cppbind/detail/functor_templates.hxx b/simgear/nasal/cppbind/detail/functor_templates.hxx deleted file mode 100644 index e17b7d49..00000000 --- a/simgear/nasal/cppbind/detail/functor_templates.hxx +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef SG_NASAL_GHOST_HXX_ -# error Nasal cppbind - do not include this file! -#endif - -#ifndef SG_DONT_DO_ANYTHING -#define n BOOST_PP_ITERATION() - -#define SG_GHOST_FUNC_TYPE\ - boost::function - - /** - * Bind any callable entity accepting an instance of raw_type and an arbitrary - * number of arguments as method. - */ - template< - class Ret - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A) - > - Ghost& method(const std::string& name, const SG_GHOST_FUNC_TYPE& func) - { -#if defined(SG_GCC_VERSION) && SG_GCC_VERSION < 40407 - // The old version of g++ used on Jenkins (16.11.2012) only compiles this - // version. -# define SG_GHOST_REQUIRE_ARG(z, n, dummy)\ - boost::bind(&arg_from_nasal, _2, n) -#else - // VS (2008, 2010, ... ?) only allow this version. -# define SG_GHOST_REQUIRE_ARG(z, n, dummy)\ - boost::bind(&Ghost::arg_from_nasal, _2, n) -#endif - - return method - ( - name, - typename boost::function - ( boost::bind( - func, - _1 - BOOST_PP_ENUM_TRAILING(n, SG_GHOST_REQUIRE_ARG, 0) - )) - ); - -#undef SG_GHOST_REQUIRE_ARG - } - -#define SG_GHOST_MEM_FN(cv)\ - /**\ - * Bind a member function with an arbitrary number of arguments as method.\ - */\ - template<\ - class Ret\ - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)\ - >\ - Ghost& method\ - (\ - const std::string& name,\ - Ret (raw_type::*fn)(BOOST_PP_ENUM_PARAMS(n,A)) cv\ - )\ - {\ - return method<\ - Ret\ - BOOST_PP_ENUM_TRAILING_PARAMS(n,A)\ - >(name, SG_GHOST_FUNC_TYPE(fn));\ - } - -// Work around MSVC warning C4003: not enough actual parameters for macro -// We really do not want to pass a parameter, even if MSVC can not believe it. -#define SG_GHOST_NO_CV - - SG_GHOST_MEM_FN(const) - SG_GHOST_MEM_FN(SG_GHOST_NO_CV) - -#undef SG_GHOST_MEM_FN -#undef SG_GHOST_NO_CV - - /** - * Bind free function accepting an instance of raw_type and an arbitrary - * number of arguments as method. - */ - template< - class Ret, - class Type - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A) - > - Ghost& method - ( - const std::string& name, - Ret (*fn)(Type BOOST_PP_ENUM_TRAILING_PARAMS(n,A)) - ) - { - BOOST_STATIC_ASSERT - (( boost::is_convertible::value - //|| boost::is_convertible::value - // TODO check how to do it with pointer... - )); - return method(name, SG_GHOST_FUNC_TYPE(fn)); - } - -#undef n -#undef SG_GHOST_TYPEDEF_FUNC_TYPE -#endif // SG_DONT_DO_ANYTHING diff --git a/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx b/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx index d74f6a14..85546946 100644 --- a/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx +++ b/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx @@ -1,9 +1,12 @@ #define BOOST_TEST_MODULE cppbind #include +#include "TestContext.hxx" + #include #include +#include #include #include @@ -186,3 +189,44 @@ BOOST_AUTO_TEST_CASE( storage_traits ) nasal::shared_ptr_storage::unref(d_weak); } + +BOOST_AUTO_TEST_CASE( bind_methods ) +{ + struct TestClass + { + int arg1; + std::string arg2; + std::string arg3; + int arg4; + + void set(int a1, const std::string& a2, const std::string& a3, int a4) + { + arg1 = a1; + arg2 = a2; + arg3 = a3; + arg4 = a4; + } + }; + using TestClassPtr = boost::shared_ptr; + auto set_func = boost::function< + void (TestClass&, int, const std::string&, const std::string&, int) + >(&TestClass::set); + nasal::Ghost::init("TestClass") + .method("set", set_func) + .method("setReverse", set_func, std::index_sequence<3,2,1,0>{}); + + TestContext ctx; + auto test = boost::make_shared(); + + ctx.exec("me.set(1, \"s2\", \"s3\", 4);", ctx.to_nasal(test)); + BOOST_CHECK_EQUAL(test->arg1, 1); + BOOST_CHECK_EQUAL(test->arg2, "s2"); + BOOST_CHECK_EQUAL(test->arg3, "s3"); + BOOST_CHECK_EQUAL(test->arg4, 4); + + ctx.exec("me.setReverse(1, \"s2\", \"s3\", 4);", ctx.to_nasal(test)); + BOOST_CHECK_EQUAL(test->arg1, 4); + BOOST_CHECK_EQUAL(test->arg2, "s3"); + BOOST_CHECK_EQUAL(test->arg3, "s2"); + BOOST_CHECK_EQUAL(test->arg4, 1); +}