cppbind: allow calling methods with 'me' object from C++.

This commit is contained in:
Thomas Geymayer
2014-03-22 12:31:03 +01:00
parent ff53792e4f
commit 13a3ea3503
7 changed files with 141 additions and 46 deletions

View File

@@ -542,6 +542,11 @@ 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
#define BOOST_PP_ITERATION_LIMITS (0, 9)
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/functor_templates.hxx>
#include BOOST_PP_ITERATE()
@@ -732,7 +737,7 @@ namespace nasal
(
c,
"method called on object of wrong type: is '%s' expected '%s'",
ghost_type ? ghost_type->name : "unknown",
naIsNil(me) ? "nil" : (ghost_type ? ghost_type->name : "unknown"),
_ghost_type.name
);

View File

@@ -69,7 +69,7 @@ namespace nasal
}
//----------------------------------------------------------------------------
const naRef Hash::get_naRef() const
naRef Hash::get_naRef() const
{
return _hash;
}

View File

@@ -118,7 +118,7 @@ namespace nasal
/**
* Get Nasal representation of Hash
*/
const naRef get_naRef() const;
naRef get_naRef() const;
protected:

View File

@@ -38,6 +38,8 @@ struct Base
std::string var;
const std::string& getVar() const { return var; }
void setVar(const std::string v) { var = v; }
unsigned long getThis() const { return (unsigned long)this; }
};
void baseVoidFunc(Base& b) {}
@@ -173,6 +175,19 @@ int main(int argc, char* argv[])
VERIFY( fma );
VERIFY( fma("test", 3, .5) == "test" );
typedef boost::function<naRef (naRef)> naRefMemFunc;
naRefMemFunc fmem = hash.get<naRefMemFunc>("func");
VERIFY( fmem );
naRef ret = fmem(hash.get_naRef()),
hash_ref = hash.get_naRef();
VERIFY( memcmp(&ret, &hash_ref, sizeof(naRef)) == 0 );
// Check if nasal::Me gets passed as self/me and remaining arguments are
// passed on to function
typedef boost::function<int (Me, int)> MeIntFunc;
MeIntFunc fmeint = hash.get<MeIntFunc>("func");
VERIFY( fmeint(naNil(), 5) == 5 );
//----------------------------------------------------------------------------
// Test exposing classes to Nasal
//----------------------------------------------------------------------------
@@ -190,7 +205,8 @@ int main(int argc, char* argv[])
.method("void_c", &baseConstVoidFunc)
.method("int2args", &baseFunc2Args)
.method("bool2args", &Base::test2Args)
.method("str_ptr", &testPtr);
.method("str_ptr", &testPtr)
.method("this", &Base::getThis);
Ghost<DerivedPtr>::init("DerivedPtr")
.bases<BasePtr>()
.member("x", &Derived::getX, &Derived::setX)
@@ -216,6 +232,17 @@ int main(int argc, char* argv[])
VERIFY( naIsGhost(derived) );
VERIFY( std::string("DerivedPtr") == naGhost_type(derived)->name );
// Get member function from ghost...
naRef thisGetter = naNil();
VERIFY( naMember_get(c, derived, to_nasal(c, "this"), &thisGetter) );
VERIFY( naIsFunc(thisGetter) );
// ...and check if it really gets passed the correct instance
typedef boost::function<unsigned long (Me)> MemFunc;
MemFunc fGetThis = from_nasal<MemFunc>(c, thisGetter);
VERIFY( fGetThis );
VERIFY( fGetThis(derived) == (unsigned long)d.get() );
BasePtr d2( new DoubleDerived );
derived = to_nasal(c, d2);
VERIFY( naIsGhost(derived) );
@@ -292,8 +319,6 @@ int main(int argc, char* argv[])
VERIFY( objects[1] == d2 );
VERIFY( objects[2] == d3 );
// TODO actually do something with the ghosts...
//----------------------------------------------------------------------------
// Test nasal::CallContext
//----------------------------------------------------------------------------

View File

@@ -2,6 +2,7 @@
# error Nasal cppbind - do not include this file!
#endif
#ifndef SG_DONT_DO_ANYTHING
#define n BOOST_PP_ITERATION()
#ifndef SG_BOOST_FUNCTION_FROM_NASAL_FWD
@@ -12,14 +13,12 @@
template<
class Ret
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_PARAMS(n, class A)
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::disable_if<boost::is_void<Ret>, Ret>::type
callNasalFunction( const ObjectHolder<SGReferenced>* holder
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM(n, SG_CALL_TRAITS_PARAM, 0)
)
callNasalMethod( const ObjectHolder<SGReferenced>* holder,
Me self
BOOST_PP_ENUM_TRAILING(n, SG_CALL_TRAITS_PARAM, 0) )
{
naContext ctx = naNewContext();
naRef args[] = {
@@ -28,33 +27,43 @@
const int num_args = sizeof(args)/sizeof(args[0]);
naRef result =
naCallMethod(holder->get_naRef(), naNil(), num_args, args, naNil());
naCallMethodCtx(ctx, holder->get_naRef(), self, num_args, args, naNil());
const char* error = naGetError(ctx);
std::string error_str(error ? error : "");
Ret r = Ret();
if( !error )
r = from_nasal_helper(ctx, result, static_cast<Ret*>(0));
Ret r = from_nasal_helper(ctx, result, static_cast<Ret*>(0));
naFreeContext(ctx);
if( error )
throw std::runtime_error(error_str);
return r;
}
template<
class Ret
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_PARAMS(n, class A)
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::enable_if<boost::is_void<Ret>, Ret>::type
callNasalFunction( const ObjectHolder<SGReferenced>* holder
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM(n, SG_CALL_TRAITS_PARAM, 0)
)
callNasalMethod( const ObjectHolder<SGReferenced>* holder,
Me self
BOOST_PP_ENUM_TRAILING(n, SG_CALL_TRAITS_PARAM, 0) )
{
naContext ctx = naNewContext();
naRef args[] = {
BOOST_PP_ENUM(n, SG_CALL_ARG, 0)
};
const int num_args = sizeof(args)/sizeof(args[0]);
naCallMethod(holder->get_naRef(), naNil(), num_args, args, naNil());
naFreeContext(ctx);
callNasalMethod<
naRef // do not do any conversion and just ignore the return value
// TODO warn if something different to nil is returned?
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_PARAMS(n, A)
>
(
holder,
self
BOOST_PP_ENUM_TRAILING_PARAMS(n, a)
);
}
# undef SG_CALL_TRAITS_PARAM
@@ -63,10 +72,13 @@
template<
class Ret
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_PARAMS(n, class A)
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
typename boost::disable_if<
// free function if first argument is not nasal::Me or no argument at all
boost::is_same<BOOST_PP_IF(n, A0, void), Me>,
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
>::type
boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A)))
#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD
;
@@ -74,12 +86,43 @@
{
return boost::bind
(
&callNasalFunction<Ret BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, A)>,
ObjectHolder<SGReferenced>::makeShared(code)
&callNasalMethod<Ret BOOST_PP_ENUM_TRAILING_PARAMS(n, A)>,
ObjectHolder<SGReferenced>::makeShared(code),
boost::bind(naNil)
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _)
);
}
#endif
#if n > 0
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::enable_if<
// method if type of first argument is nasal::Me
boost::is_same<A0, Me>,
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
>::type
boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A)))
#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD
;
#else
{
return boost::bind
(
&callNasalMethod<
Ret
BOOST_PP_COMMA_IF(BOOST_PP_DEC(n))
BOOST_PP_ENUM_SHIFTED_PARAMS(n, A)
>,
ObjectHolder<SGReferenced>::makeShared(code),
BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _)
);
}
#endif
#endif
#undef n
#endif // SG_DONT_DO_ANYTHING

View File

@@ -31,7 +31,10 @@
#include <boost/bind.hpp>
#include <boost/call_traits.hpp>
#include <boost/function.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum_trailing.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/preprocessor/repetition/enum_shifted_params.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
@@ -71,6 +74,21 @@ namespace nasal
std::string _msg;
};
/**
* Wrap a naRef to indicate it references the self/me object in Nasal method
* calls.
*/
struct Me
{
naRef _ref;
Me(naRef ref):
_ref(ref)
{}
operator naRef() { return _ref; }
};
/**
* Simple pass through for unified handling also of naRef.
*/
@@ -193,7 +211,14 @@ namespace nasal
// Helpers for wrapping calls to Nasal functions into boost::function
namespace detail
{
#define BOOST_PP_ITERATION_LIMITS (0, 9)
// Dummy include to add a build dependency on this file for gcc/CMake/etc.
#define SG_DONT_DO_ANYTHING
# include <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
#undef SG_DONT_DO_ANYTHING
// Now the actual include (we are limited to 8 arguments (+me) here because
// boost::bind has an upper limit of 9)
#define BOOST_PP_ITERATION_LIMITS (0, 8)
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
#include BOOST_PP_ITERATE()
}

View File

@@ -2,10 +2,11 @@
# 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_COMMA_IF(n)BOOST_PP_ENUM_PARAMS(n,A))>
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
@@ -13,8 +14,7 @@
*/
template<
class Ret
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_PARAMS(n, class A)
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
Ghost& method(const std::string& name, const SG_GHOST_FUNC_TYPE& func)
{
@@ -36,8 +36,7 @@
( boost::bind(
func,
_1
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM(n, SG_GHOST_REQUIRE_ARG, 0)
BOOST_PP_ENUM_TRAILING(n, SG_GHOST_REQUIRE_ARG, 0)
))
);
@@ -50,8 +49,7 @@
*/\
template<\
class Ret\
BOOST_PP_COMMA_IF(n)\
BOOST_PP_ENUM_PARAMS(n, class A)\
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)\
>\
Ghost& method\
(\
@@ -61,8 +59,7 @@
{\
return method<\
Ret\
BOOST_PP_COMMA_IF(n)\
BOOST_PP_ENUM_PARAMS(n,A)\
BOOST_PP_ENUM_TRAILING_PARAMS(n,A)\
>(name, SG_GHOST_FUNC_TYPE(fn));\
}
@@ -83,13 +80,12 @@
template<
class Ret,
class Type
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_PARAMS(n, class A)
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
Ghost& method
(
const std::string& name,
Ret (*fn)(Type BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,A))
Ret (*fn)(Type BOOST_PP_ENUM_TRAILING_PARAMS(n,A))
)
{
BOOST_STATIC_ASSERT
@@ -102,3 +98,4 @@
#undef n
#undef SG_GHOST_TYPEDEF_FUNC_TYPE
#endif // SG_DONT_DO_ANYTHING