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
|
set(DETAIL_HEADERS
|
||||||
detail/from_nasal_function_templates.hxx
|
detail/from_nasal_function_templates.hxx
|
||||||
detail/from_nasal_helper.hxx
|
detail/from_nasal_helper.hxx
|
||||||
detail/functor_templates.hxx
|
|
||||||
detail/nasal_traits.hxx
|
detail/nasal_traits.hxx
|
||||||
detail/to_nasal_helper.hxx
|
detail/to_nasal_helper.hxx
|
||||||
)
|
)
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "NasalObjectHolder.hxx"
|
#include "NasalObjectHolder.hxx"
|
||||||
|
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
#include <simgear/misc/integer_sequence.hxx>
|
||||||
#include <simgear/structure/SGWeakReferenced.hxx>
|
#include <simgear/structure/SGWeakReferenced.hxx>
|
||||||
#include <simgear/structure/SGWeakPtr.hxx>
|
#include <simgear/structure/SGWeakPtr.hxx>
|
||||||
|
|
||||||
@ -32,7 +33,6 @@
|
|||||||
#include <boost/function.hpp>
|
#include <boost/function.hpp>
|
||||||
#include <boost/lambda/lambda.hpp>
|
#include <boost/lambda/lambda.hpp>
|
||||||
#include <boost/mpl/has_xxx.hpp>
|
#include <boost/mpl/has_xxx.hpp>
|
||||||
#include <boost/preprocessor/iteration/iterate.hpp>
|
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <boost/utility/enable_if.hpp>
|
#include <boost/utility/enable_if.hpp>
|
||||||
|
|
||||||
@ -259,6 +259,9 @@ namespace nasal
|
|||||||
const std::string&,
|
const std::string&,
|
||||||
naRef )> fallback_setter_t;
|
naRef )> fallback_setter_t;
|
||||||
|
|
||||||
|
template<class Ret, class... Args>
|
||||||
|
using method_variadic_t = boost::function<Ret (raw_type&, Args...)>;
|
||||||
|
|
||||||
class MethodHolder:
|
class MethodHolder:
|
||||||
public internal::MethodHolder
|
public internal::MethodHolder
|
||||||
{
|
{
|
||||||
@ -791,14 +794,73 @@ namespace nasal
|
|||||||
return method(name, boost::bind(method_invoker<Ret>, func, _1, _2));
|
return method(name, boost::bind(method_invoker<Ret>, func, _1, _2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build dependency for CMake, gcc, etc.
|
/**
|
||||||
#define SG_DONT_DO_ANYTHING
|
* Bind any callable entity accepting an instance of raw_type and an
|
||||||
# include <simgear/nasal/cppbind/detail/functor_templates.hxx>
|
* arbitrary number of arguments as method.
|
||||||
#undef SG_DONT_DO_ANYTHING
|
*
|
||||||
|
* 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)
|
template<class Ret, class... Args>
|
||||||
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/functor_templates.hxx>
|
Ghost& method( const std::string& name,
|
||||||
#include BOOST_PP_ITERATE()
|
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
|
* 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
|
#define BOOST_TEST_MODULE cppbind
|
||||||
#include <BoostTestTargetConfig.h>
|
#include <BoostTestTargetConfig.h>
|
||||||
|
|
||||||
|
#include "TestContext.hxx"
|
||||||
|
|
||||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||||
#include <simgear/nasal/cppbind/NasalContext.hxx>
|
#include <simgear/nasal/cppbind/NasalContext.hxx>
|
||||||
|
|
||||||
|
#include <boost/make_shared.hpp>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <boost/weak_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);
|
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