cppbind: refactor Ghost::method to use variadic templates

This commit is contained in:
Thomas Geymayer 2018-01-12 08:32:57 +01:00
parent b989f4085d
commit 4cedd0a346
4 changed files with 115 additions and 111 deletions

View File

@ -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
)

View File

@ -24,6 +24,7 @@
#include "NasalObjectHolder.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/integer_sequence.hxx>
#include <simgear/structure/SGWeakReferenced.hxx>
#include <simgear/structure/SGWeakPtr.hxx>
@ -32,7 +33,6 @@
#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/mpl/has_xxx.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility/enable_if.hpp>
@ -259,6 +259,9 @@ namespace nasal
const std::string&,
naRef )> fallback_setter_t;
template<class Ret, class... Args>
using method_variadic_t = boost::function<Ret (raw_type&, Args...)>;
class MethodHolder:
public internal::MethodHolder
{
@ -791,14 +794,73 @@ namespace nasal
return method(name, boost::bind(method_invoker<Ret>, func, _1, _2));
}
// Build dependency for CMake, gcc, etc.
#define SG_DONT_DO_ANYTHING
# include <simgear/nasal/cppbind/detail/functor_templates.hxx>
#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<class Ret, class... Args, std::size_t... Indices>
Ghost& method( const std::string& name,
const method_variadic_t<Ret, Args...>& func,
std::index_sequence<Indices...> )
{
return method<Ret>
(
name,
typename boost::function<Ret (raw_type&, const CallContext&)>
( boost::bind(
func,
_1,
boost::bind(&Ghost::arg_from_nasal<Args>, _2, Indices)...
))
);
}
#define BOOST_PP_ITERATION_LIMITS (0, 9)
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/functor_templates.hxx>
#include BOOST_PP_ITERATE()
template<class Ret, class... Args>
Ghost& method( const std::string& name,
const method_variadic_t<Ret, Args...>& func )
{
return method(name, func, std::index_sequence_for<Args...>{});
}
/**\
* Bind a member function with an arbitrary number of arguments as method.
*/
template<class Ret, class... Args>
Ghost& method( const std::string& name,
Ret (raw_type::*fn)(Args...) )
{
return method(name, method_variadic_t<Ret, Args...>(fn));
}
template<class Ret, class... Args>
Ghost& method( const std::string& name,
Ret (raw_type::*fn)(Args...) const )
{
return method(name, method_variadic_t<Ret, Args...>(fn));
}
/**
* Bind free function accepting an instance of raw_type and an arbitrary
* number of arguments as method.
*/
template<class Ret, class Type, class... Args>
Ghost& method
(
const std::string& name,
Ret (*fn)(Type, Args ... args)
)
{
static_assert(
boost::is_convertible<raw_type&, Type>::value,
//|| boost::is_convertible<raw_type*, Type>::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<Ret, Args...>(fn));
}
/**
* Create a shared pointer on the heap to handle the reference counting

View File

@ -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<Ret (raw_type& BOOST_PP_ENUM_TRAILING_PARAMS(n,A))>
/**
* 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<A##n>, _2, n)
#else
// VS (2008, 2010, ... ?) only allow this version.
# define SG_GHOST_REQUIRE_ARG(z, n, dummy)\
boost::bind(&Ghost::arg_from_nasal<A##n>, _2, n)
#endif
return method<Ret>
(
name,
typename boost::function<Ret (raw_type&, const CallContext&)>
( 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<raw_type&, Type>::value
//|| boost::is_convertible<raw_type*, Type>::value
// TODO check how to do it with pointer...
));
return method<Ret>(name, SG_GHOST_FUNC_TYPE(fn));
}
#undef n
#undef SG_GHOST_TYPEDEF_FUNC_TYPE
#endif // SG_DONT_DO_ANYTHING

View File

@ -1,9 +1,12 @@
#define BOOST_TEST_MODULE cppbind
#include <BoostTestTargetConfig.h>
#include "TestContext.hxx"
#include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
@ -186,3 +189,44 @@ BOOST_AUTO_TEST_CASE( storage_traits )
nasal::shared_ptr_storage<DerivedWeakPtr>::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<TestClass>;
auto set_func = boost::function<
void (TestClass&, int, const std::string&, const std::string&, int)
>(&TestClass::set);
nasal::Ghost<TestClassPtr>::init("TestClass")
.method("set", set_func)
.method("setReverse", set_func, std::index_sequence<3,2,1,0>{});
TestContext ctx;
auto test = boost::make_shared<TestClass>();
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);
}