cppbind: refactor Ghost::method to use variadic templates
This commit is contained in:
parent
b989f4085d
commit
4cedd0a346
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user