cppbind: rework to allow binding nearly everything.
It is now possible to register all types of member function and free functions as methods for nasal::Ghost objects. The return value and arguments are converte automatically to the required types. Also usage is simplified by removing replacing the old method and method_func with a single method function which only needs a name for the method and something callable.
This commit is contained in:
@@ -10,6 +10,10 @@ set(HEADERS
|
||||
to_nasal.hxx
|
||||
)
|
||||
|
||||
set(DETAIL_HEADERS
|
||||
detail/functor_templates.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
NasalHash.cxx
|
||||
NasalString.cxx
|
||||
@@ -18,6 +22,7 @@ set(SOURCES
|
||||
)
|
||||
|
||||
simgear_component(nasal/cppbind nasal/cppbind "${SOURCES}" "${HEADERS}")
|
||||
simgear_component(nasal/cppbind/detail nasal/cppbind/detail "" "${DETAIL_HEADERS}")
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#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>
|
||||
|
||||
#include <map>
|
||||
@@ -108,6 +110,16 @@ namespace nasal
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hold callable method and convert to Nasal function if required.
|
||||
*/
|
||||
class MethodHolder
|
||||
{
|
||||
public:
|
||||
virtual ~MethodHolder() {}
|
||||
virtual naRef get_naRef(naContext c) = 0;
|
||||
};
|
||||
|
||||
BOOST_MPL_HAS_XXX_TRAIT_DEF(element_type)
|
||||
}
|
||||
|
||||
@@ -131,12 +143,13 @@ namespace nasal
|
||||
* @param def Default value returned if too few arguments available
|
||||
*/
|
||||
template<class T>
|
||||
T getArg(size_t index, const T& def = T()) const
|
||||
typename from_nasal_ptr<T>::return_type
|
||||
getArg(size_t index, const T& def = T()) const
|
||||
{
|
||||
if( index >= argc )
|
||||
return def;
|
||||
|
||||
return from_nasal<T>(c, args[index]);
|
||||
return (*from_nasal_ptr<T>::get())(c, args[index]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,12 +157,13 @@ namespace nasal
|
||||
* are to few arguments available.
|
||||
*/
|
||||
template<class T>
|
||||
T requireArg(size_t index) const
|
||||
typename from_nasal_ptr<T>::return_type
|
||||
requireArg(size_t index) const
|
||||
{
|
||||
if( index >= argc )
|
||||
naRuntimeError(c, "Missing required arg #%d", index);
|
||||
|
||||
return from_nasal<T>(c, args[index]);
|
||||
return (*from_nasal_ptr<T>::get())(c, args[index]);
|
||||
}
|
||||
|
||||
naContext c;
|
||||
@@ -168,10 +182,13 @@ namespace nasal
|
||||
* void setX(int x);
|
||||
* int getX() const;
|
||||
*
|
||||
* naRef myMember(naContext c, int argc, naRef* args);
|
||||
* int myMember();
|
||||
* void doSomethingElse(const nasal::CallContext& ctx);
|
||||
* }
|
||||
* typedef boost::shared_ptr<MyClass> MyClassPtr;
|
||||
*
|
||||
* std::string myOtherFreeMember(int num);
|
||||
*
|
||||
* void exposeClasses()
|
||||
* {
|
||||
* // Register a nasal ghost type for MyClass. This needs to be done only
|
||||
@@ -184,9 +201,12 @@ namespace nasal
|
||||
* .member("x_readonly", &MyClass::getX)
|
||||
* // It is also possible to expose writeonly members
|
||||
* .member("x_writeonly", &MyClass::setX)
|
||||
* // Methods use a slightly different syntax - The pointer to the member
|
||||
* // function has to be passed as template argument
|
||||
* .method<&MyClass::myMember>("myMember");
|
||||
* // Methods can be nearly anything callable and accepting a reference
|
||||
* // to an instance of the class type. (member functions, free functions
|
||||
* // and anything else bindable using boost::function and boost::bind)
|
||||
* .method("myMember", &MyClass::myMember)
|
||||
* .method("doSomething", &MyClass::doSomethingElse)
|
||||
* .method("other", &myOtherFreeMember);
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
@@ -204,7 +224,52 @@ namespace nasal
|
||||
typedef boost::function<naRef(naContext, raw_type&)> getter_t;
|
||||
typedef boost::function<void(naContext, raw_type&, naRef)> setter_t;
|
||||
typedef boost::function<naRef(raw_type&, const CallContext&)> method_t;
|
||||
typedef boost::shared_ptr<method_t> method_ptr;
|
||||
typedef boost::shared_ptr<internal::MethodHolder> MethodHolderPtr;
|
||||
|
||||
class MethodHolder:
|
||||
public internal::MethodHolder
|
||||
{
|
||||
public:
|
||||
MethodHolder():
|
||||
_naRef(naNil())
|
||||
{}
|
||||
|
||||
explicit MethodHolder(const method_t& method):
|
||||
_method(method),
|
||||
_naRef(naNil())
|
||||
{}
|
||||
|
||||
virtual naRef get_naRef(naContext c)
|
||||
{
|
||||
if( naIsNil(_naRef) )
|
||||
{
|
||||
_naRef = naNewFunc(c, naNewCCodeU(c, &MethodHolder::call, this));
|
||||
naSave(c, _naRef);
|
||||
}
|
||||
return _naRef;
|
||||
}
|
||||
|
||||
protected:
|
||||
method_t _method;
|
||||
naRef _naRef;
|
||||
|
||||
static naRef call( naContext c,
|
||||
naRef me,
|
||||
int argc,
|
||||
naRef* args,
|
||||
void* user_data )
|
||||
{
|
||||
MethodHolder* holder = static_cast<MethodHolder*>(user_data);
|
||||
if( !holder )
|
||||
naRuntimeError(c, "invalid method holder!");
|
||||
|
||||
return holder->_method
|
||||
(
|
||||
requireObject(c, me),
|
||||
CallContext(c, argc, args)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A ghost member. Can consist either of getter and/or setter functions
|
||||
@@ -212,25 +277,24 @@ namespace nasal
|
||||
*/
|
||||
struct member_t
|
||||
{
|
||||
member_t():
|
||||
func(0)
|
||||
member_t()
|
||||
{}
|
||||
|
||||
member_t( const getter_t& getter,
|
||||
const setter_t& setter,
|
||||
naCFunction func = 0 ):
|
||||
const MethodHolderPtr& func = MethodHolderPtr() ):
|
||||
getter( getter ),
|
||||
setter( setter ),
|
||||
func( func )
|
||||
{}
|
||||
|
||||
member_t(naCFunction func):
|
||||
explicit member_t(const MethodHolderPtr& func):
|
||||
func( func )
|
||||
{}
|
||||
|
||||
getter_t getter;
|
||||
setter_t setter;
|
||||
naCFunction func;
|
||||
getter_t getter;
|
||||
setter_t setter;
|
||||
MethodHolderPtr func;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, member_t> MemberMap;
|
||||
@@ -367,22 +431,24 @@ namespace nasal
|
||||
member_t m;
|
||||
if( getter )
|
||||
{
|
||||
typedef typename boost::call_traits<Ret>::param_type param_type;
|
||||
naRef (*to_nasal_)(naContext, param_type) = &nasal::to_nasal;
|
||||
|
||||
// Getter signature: naRef(naContext, raw_type&)
|
||||
m.getter = boost::bind(to_nasal_, _1, boost::bind(getter, _2));
|
||||
m.getter = boost::bind
|
||||
(
|
||||
to_nasal_ptr<Ret>::get(),
|
||||
_1,
|
||||
boost::bind(getter, _2)
|
||||
);
|
||||
}
|
||||
|
||||
if( setter )
|
||||
{
|
||||
typename boost::remove_const
|
||||
< typename boost::remove_reference<Param>::type
|
||||
>::type
|
||||
(*from_nasal_)(naContext, naRef) = &nasal::from_nasal;
|
||||
|
||||
// Setter signature: void(naContext, raw_type&, naRef)
|
||||
m.setter = boost::bind(setter, _2, boost::bind(from_nasal_, _1, _3));
|
||||
m.setter = boost::bind
|
||||
(
|
||||
setter,
|
||||
_2,
|
||||
boost::bind(from_nasal_ptr<Param>::get(), _1, _3)
|
||||
);
|
||||
}
|
||||
|
||||
return member(field, m.getter, m.setter);
|
||||
@@ -439,75 +505,38 @@ namespace nasal
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a member function.
|
||||
*
|
||||
* @note Because only function pointers can be registered as Nasal
|
||||
* functions it is needed to pass the function pointer as template
|
||||
* argument. This allows us to create a separate instance of the
|
||||
* MemberFunctionWrapper for each registered function and therefore
|
||||
* provides us with the needed static functions to be passed on to
|
||||
* Nasal.
|
||||
*
|
||||
* @tparam func Pointer to member function being registered.
|
||||
* Register anything that accepts an object instance and a
|
||||
* nasal::CallContext and returns naRef as method.
|
||||
*
|
||||
* @code{cpp}
|
||||
* class MyClass
|
||||
* {
|
||||
* public:
|
||||
* naRef myMethod(naContext c, int argc, naRef* args);
|
||||
* naRef myMethod(const nasal::CallContext& ctx);
|
||||
* }
|
||||
*
|
||||
* Ghost<MyClassPtr>::init("Test")
|
||||
* .method<&MyClass::myMethod>("myMethod");
|
||||
* .method("myMethod", &MyClass::myMethod);
|
||||
* @endcode
|
||||
*/
|
||||
template<member_func_t func>
|
||||
Ghost& method(const std::string& name)
|
||||
Ghost& method(const std::string& name, const method_t& func)
|
||||
{
|
||||
_members[name].func = &MemberFunctionWrapper<func>::call;
|
||||
_members[name].func.reset( new MethodHolder(func) );
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a method which returns a value and convert it to Nasal.
|
||||
*/
|
||||
template<class Ret>
|
||||
static
|
||||
typename boost::disable_if<boost::is_void<Ret>, naRef>::type
|
||||
method_invoker
|
||||
(
|
||||
const boost::function<Ret (raw_type&, const CallContext&)>& func,
|
||||
raw_type& obj,
|
||||
const CallContext& ctx
|
||||
)
|
||||
{
|
||||
typedef typename boost::call_traits<Ret>::param_type param_type;
|
||||
naRef (*to_nasal_)(naContext, param_type) = &nasal::to_nasal;
|
||||
|
||||
return to_nasal_(ctx.c, func(obj, ctx));
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke a method which returns void and "convert" it to nil.
|
||||
*/
|
||||
template<class Ret>
|
||||
static
|
||||
typename boost::enable_if<boost::is_void<Ret>, naRef>::type
|
||||
method_invoker
|
||||
(
|
||||
const boost::function<void (raw_type&, const CallContext&)>& func,
|
||||
raw_type& obj,
|
||||
const CallContext& ctx
|
||||
)
|
||||
{
|
||||
func(obj, ctx);
|
||||
return naNil();
|
||||
};
|
||||
|
||||
/**
|
||||
* Bind any callable entity as method callable from Nasal
|
||||
* Register anything that accepts an object instance and a
|
||||
* nasal::CallContext whith automatic conversion of the return type to
|
||||
* Nasal.
|
||||
*
|
||||
* Does not really register method yet!!!
|
||||
* @code{cpp}
|
||||
* class MyClass;
|
||||
* void doIt(const MyClass& c, const nasal::CallContext& ctx);
|
||||
*
|
||||
* Ghost<MyClassPtr>::init("Test")
|
||||
* .method("doIt", &doIt);
|
||||
* @endcode
|
||||
*/
|
||||
template<class Ret>
|
||||
Ghost& method
|
||||
@@ -516,67 +545,14 @@ namespace nasal
|
||||
const boost::function<Ret (raw_type&, const CallContext&)>& func
|
||||
)
|
||||
{
|
||||
// _members[name].func.reset
|
||||
// (
|
||||
new method_t( boost::bind(method_invoker<Ret>, func, _1, _2) );
|
||||
// );
|
||||
return *this;
|
||||
return method(name, boost::bind(method_invoker<Ret>, func, _1, _2));
|
||||
}
|
||||
|
||||
template<class Ret>
|
||||
struct method_raw
|
||||
{
|
||||
typedef boost::function<Ret (raw_type&, const CallContext&)> type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bind member function as method callable from Nasal
|
||||
*
|
||||
* Does not really register method yet!!!
|
||||
*/
|
||||
template<class Ret>
|
||||
Ghost& method( const std::string& name,
|
||||
Ret (raw_type::*fn)() const )
|
||||
{
|
||||
return method<Ret>
|
||||
(
|
||||
name,
|
||||
typename method_raw<Ret>::type(boost::bind(fn, _1))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a free function as member function. The object instance is
|
||||
* passed as additional first argument.
|
||||
*
|
||||
* @tparam func Pointer to free function being registered.
|
||||
*
|
||||
* @note Due to a severe bug in Visual Studio it is not possible to create
|
||||
* a specialization of #method for free function pointers and
|
||||
* member function pointers at the same time. Do overcome this
|
||||
* limitation we had to use a different name for this function.
|
||||
*
|
||||
* @code{cpp}
|
||||
* class MyClass;
|
||||
* naRef myMethod(MyClass& obj, naContext c, int argc, naRef* args);
|
||||
*
|
||||
* Ghost<MyClassPtr>::init("Test")
|
||||
* .method_func<&myMethod>("myMethod");
|
||||
* @endcode
|
||||
*/
|
||||
template<free_func_t func>
|
||||
Ghost& method_func(const std::string& name)
|
||||
{
|
||||
_members[name].func = &FreeFunctionWrapper<func>::call;
|
||||
return *this;
|
||||
}
|
||||
#define BOOST_PP_ITERATION_LIMITS (0, 9)
|
||||
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/functor_templates.hxx>
|
||||
#include BOOST_PP_ITERATE()
|
||||
|
||||
// TODO use variadic template when supporting C++11
|
||||
/**
|
||||
* Create a Nasal instance of this ghost.
|
||||
*
|
||||
* @param c Active Nasal context
|
||||
*/
|
||||
// TODO check if default constructor exists
|
||||
// static naRef create( naContext c )
|
||||
// {
|
||||
@@ -768,44 +744,65 @@ namespace nasal
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class to enable registering pointers to member functions as
|
||||
* Nasal function callbacks. We need to use the function pointer as
|
||||
* template parameter to ensure every registered function gets a static
|
||||
* function which can be passed to Nasal.
|
||||
* Invoke a method which returns a value and convert it to Nasal.
|
||||
*/
|
||||
template<member_func_t func>
|
||||
struct MemberFunctionWrapper
|
||||
template<class Ret>
|
||||
static
|
||||
typename boost::disable_if<boost::is_void<Ret>, naRef>::type
|
||||
method_invoker
|
||||
(
|
||||
const boost::function<Ret (raw_type&, const CallContext&)>& func,
|
||||
raw_type& obj,
|
||||
const CallContext& ctx
|
||||
)
|
||||
{
|
||||
/**
|
||||
* Called from Nasal upon invocation of the according registered
|
||||
* function. Forwards the call to the passed object instance.
|
||||
*/
|
||||
static naRef call(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
return (requireObject(c, me).*func)(CallContext(c, argc, args));
|
||||
}
|
||||
return (*to_nasal_ptr<Ret>::get())(ctx.c, func(obj, ctx));
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper class to enable registering pointers to free functions (only
|
||||
* external linkage). We need to use the function pointer as template
|
||||
* parameter to ensure every registered function gets a static function
|
||||
* which can be passed to Nasal. Even though we just wrap another simple
|
||||
* function pointer this intermediate step is need to be able to retrieve
|
||||
* the object the function call belongs to and pass it along as argument.
|
||||
* Invoke a method which returns void and "convert" it to nil.
|
||||
*/
|
||||
template<free_func_t func>
|
||||
struct FreeFunctionWrapper
|
||||
template<class Ret>
|
||||
static
|
||||
typename boost::enable_if<boost::is_void<Ret>, naRef>::type
|
||||
method_invoker
|
||||
(
|
||||
const boost::function<Ret (raw_type&, const CallContext&)>& func,
|
||||
raw_type& obj,
|
||||
const CallContext& ctx
|
||||
)
|
||||
{
|
||||
/**
|
||||
* Called from Nasal upon invocation of the according registered
|
||||
* function. Forwards the call to the passed function pointer and passes
|
||||
* the required parameters.
|
||||
*/
|
||||
static naRef call(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
return func(requireObject(c, me), CallContext(c, argc, args));
|
||||
}
|
||||
func(obj, ctx);
|
||||
return naNil();
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract argument by index from nasal::CallContext and convert to given
|
||||
* type.
|
||||
*/
|
||||
template<class Arg>
|
||||
static
|
||||
typename boost::disable_if<
|
||||
boost::is_same<Arg, const CallContext&>,
|
||||
typename from_nasal_ptr<Arg>::return_type
|
||||
>::type
|
||||
arg_from_nasal(const CallContext& ctx, size_t index)
|
||||
{
|
||||
return ctx.requireArg<Arg>(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Specialization to pass through nasal::CallContext.
|
||||
*/
|
||||
template<class Arg>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
boost::is_same<Arg, const CallContext&>,
|
||||
typename from_nasal_ptr<Arg>::return_type
|
||||
>::type
|
||||
arg_from_nasal(const CallContext& ctx, size_t)
|
||||
{
|
||||
return ctx;
|
||||
};
|
||||
|
||||
typedef std::auto_ptr<Ghost> GhostPtr;
|
||||
@@ -871,7 +868,7 @@ namespace nasal
|
||||
return 0;
|
||||
|
||||
if( member->second.func )
|
||||
*out = nasal::to_nasal(c, member->second.func);
|
||||
*out = member->second.func->get_naRef(c);
|
||||
else if( !member->second.getter.empty() )
|
||||
*out = member->second.getter(c, *getRawPtr(g));
|
||||
else
|
||||
@@ -891,8 +888,10 @@ namespace nasal
|
||||
|
||||
if( member == getSingletonPtr()->_members.end() )
|
||||
naRuntimeError(c, "ghost: No such member: %s", key.c_str());
|
||||
if( member->second.setter.empty() )
|
||||
else if( member->second.setter.empty() )
|
||||
naRuntimeError(c, "ghost: Write protected member: %s", key.c_str());
|
||||
else if( member->second.func )
|
||||
naRuntimeError(c, "ghost: Write to function: %s", key.c_str());
|
||||
|
||||
member->second.setter(c, *getRawPtr(g), val);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ struct Base
|
||||
std::string getString() const { return ""; }
|
||||
void setString(const std::string&) {}
|
||||
void constVoidFunc() const {}
|
||||
int test1Arg(const std::string& str) const { return str.length(); }
|
||||
bool test2Args(const std::string& s, bool c) { return c && s.empty(); }
|
||||
|
||||
std::string var;
|
||||
const std::string& getVar() const { return var; }
|
||||
@@ -32,6 +34,9 @@ struct Base
|
||||
|
||||
void baseVoidFunc(Base& b) {}
|
||||
void baseConstVoidFunc(const Base& b) {}
|
||||
int baseFunc2Args(Base& b, int x, const std::string& s) { return x + s.size(); }
|
||||
std::string testPtr(Base& b) { return b.getString(); }
|
||||
void baseFuncCallContext(const Base&, const nasal::CallContext&) {}
|
||||
|
||||
struct Derived:
|
||||
public Base
|
||||
@@ -61,7 +66,7 @@ naRef to_nasal(naContext c, const BasePtr& base)
|
||||
return nasal::Ghost<BasePtr>::create(c, base);
|
||||
}
|
||||
|
||||
naRef member(Derived&, const nasal::CallContext&) { return naNil(); }
|
||||
naRef derivedFreeMember(Derived&, const nasal::CallContext&) { return naNil(); }
|
||||
naRef f_derivedGetX(naContext c, const Derived& d)
|
||||
{
|
||||
return nasal::to_nasal(c, d.getX());
|
||||
@@ -126,41 +131,31 @@ int main(int argc, char* argv[])
|
||||
Hash mod = hash.createHash("mod");
|
||||
mod.set("parent", hash);
|
||||
|
||||
String string( to_nasal(c, "Test") );
|
||||
VERIFY( from_nasal<std::string>(c, string.get_naRef()) == "Test" );
|
||||
VERIFY( string.c_str() == std::string("Test") );
|
||||
VERIFY( string.starts_with(string) );
|
||||
VERIFY( string.starts_with(String(c, "T")) );
|
||||
VERIFY( string.starts_with(String(c, "Te")) );
|
||||
VERIFY( string.starts_with(String(c, "Tes")) );
|
||||
VERIFY( string.starts_with(String(c, "Test")) );
|
||||
VERIFY( !string.starts_with(String(c, "Test1")) );
|
||||
VERIFY( !string.starts_with(String(c, "bb")) );
|
||||
VERIFY( !string.starts_with(String(c, "bbasdasdafasd")) );
|
||||
VERIFY( string.find('e') == 1 );
|
||||
VERIFY( string.find('9') == String::npos );
|
||||
VERIFY( string.find_first_of(String(c, "st")) == 2 );
|
||||
VERIFY( string.find_first_of(String(c, "st"), 3) == 3 );
|
||||
VERIFY( string.find_first_of(String(c, "xyz")) == String::npos );
|
||||
VERIFY( string.find_first_not_of(String(c, "Tst")) == 1 );
|
||||
VERIFY( string.find_first_not_of(String(c, "Tse"), 2) == 3 );
|
||||
VERIFY( string.find_first_not_of(String(c, "abc")) == 0 );
|
||||
VERIFY( string.find_first_not_of(String(c, "abc"), 20) == String::npos );
|
||||
//----------------------------------------------------------------------------
|
||||
// Test exposing classes to Nasal
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Ghost<BasePtr>::init("BasePtr")
|
||||
.method<&Base::member>("member")
|
||||
.method("member", &Base::member)
|
||||
.method("strlen", &Base::test1Arg)
|
||||
.member("str", &Base::getString, &Base::setString)
|
||||
.method("str_m", &Base::getString)
|
||||
.method("void", &Base::constVoidFunc)
|
||||
.member("var_r", &Base::getVar)
|
||||
.member("var_w", &Base::setVar)
|
||||
.member("var", &Base::getVar, &Base::setVar)
|
||||
/*.method("void", &baseVoidFunc)*/;
|
||||
.method("void", &baseVoidFunc)
|
||||
.method("void_c", &baseConstVoidFunc)
|
||||
.method("int2args", &baseFunc2Args)
|
||||
.method("bool2args", &Base::test2Args)
|
||||
.method("str_ptr", &testPtr);
|
||||
Ghost<DerivedPtr>::init("DerivedPtr")
|
||||
.bases<BasePtr>()
|
||||
.member("x", &Derived::getX, &Derived::setX)
|
||||
.member("x_alternate", &f_derivedGetX)
|
||||
.method_func<&member>("free_member");
|
||||
.method("free_fn", &derivedFreeMember)
|
||||
.method("free_member", &derivedFreeMember)
|
||||
.method("baseDoIt", &baseFuncCallContext);
|
||||
Ghost<DoubleDerivedPtr>::init("DoubleDerivedPtr")
|
||||
.bases<DerivedPtr>();
|
||||
Ghost<DoubleDerived2Ptr>::init("DoubleDerived2Ptr")
|
||||
@@ -222,6 +217,12 @@ int main(int argc, char* argv[])
|
||||
derived_obj.set("parents", parents2);
|
||||
VERIFY( Ghost<BasePtr>::fromNasal(c, derived_obj.get_naRef()) == d3 );
|
||||
|
||||
// TODO actually do something with the ghosts...
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Test nasal::CallContext
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
naRef args[] = {
|
||||
to_nasal(c, std::string("test-arg"))
|
||||
};
|
||||
@@ -233,7 +234,30 @@ int main(int argc, char* argv[])
|
||||
naRef args_vec = nasal::to_nasal(c, args);
|
||||
VERIFY( naIsVector(args_vec) );
|
||||
|
||||
// TODO actually do something with the ghosts...
|
||||
//----------------------------------------------------------------------------
|
||||
// Test nasal::String
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
String string( to_nasal(c, "Test") );
|
||||
VERIFY( from_nasal<std::string>(c, string.get_naRef()) == "Test" );
|
||||
VERIFY( string.c_str() == std::string("Test") );
|
||||
VERIFY( string.starts_with(string) );
|
||||
VERIFY( string.starts_with(String(c, "T")) );
|
||||
VERIFY( string.starts_with(String(c, "Te")) );
|
||||
VERIFY( string.starts_with(String(c, "Tes")) );
|
||||
VERIFY( string.starts_with(String(c, "Test")) );
|
||||
VERIFY( !string.starts_with(String(c, "Test1")) );
|
||||
VERIFY( !string.starts_with(String(c, "bb")) );
|
||||
VERIFY( !string.starts_with(String(c, "bbasdasdafasd")) );
|
||||
VERIFY( string.find('e') == 1 );
|
||||
VERIFY( string.find('9') == String::npos );
|
||||
VERIFY( string.find_first_of(String(c, "st")) == 2 );
|
||||
VERIFY( string.find_first_of(String(c, "st"), 3) == 3 );
|
||||
VERIFY( string.find_first_of(String(c, "xyz")) == String::npos );
|
||||
VERIFY( string.find_first_not_of(String(c, "Tst")) == 1 );
|
||||
VERIFY( string.find_first_not_of(String(c, "Tse"), 2) == 3 );
|
||||
VERIFY( string.find_first_not_of(String(c, "abc")) == 0 );
|
||||
VERIFY( string.find_first_not_of(String(c, "abc"), 20) == String::npos );
|
||||
|
||||
naFreeContext(c);
|
||||
|
||||
|
||||
92
simgear/nasal/cppbind/detail/functor_templates.hxx
Normal file
92
simgear/nasal/cppbind/detail/functor_templates.hxx
Normal file
@@ -0,0 +1,92 @@
|
||||
#ifndef SG_NASAL_GHOST_HXX_
|
||||
# error Nasal cppbind - do not include this file!
|
||||
#endif
|
||||
|
||||
#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))>
|
||||
|
||||
/**
|
||||
* Bind any callable entity accepting an instance of raw_type and an arbitrary
|
||||
* number of arguments as method.
|
||||
*/
|
||||
template<
|
||||
class Ret
|
||||
BOOST_PP_COMMA_IF(n)
|
||||
BOOST_PP_ENUM_PARAMS(n, class A)
|
||||
>
|
||||
Ghost& method(const std::string& name, const SG_GHOST_FUNC_TYPE& func)
|
||||
{
|
||||
#define SG_GHOST_REQUIRE_ARG(z, n, dummy)\
|
||||
boost::bind(&Ghost::arg_from_nasal<A##n>, _2, n)
|
||||
|
||||
return method<Ret>
|
||||
(
|
||||
name,
|
||||
typename boost::function<Ret (raw_type&, const CallContext&)>
|
||||
( boost::bind(
|
||||
func,
|
||||
_1
|
||||
BOOST_PP_COMMA_IF(n)
|
||||
BOOST_PP_ENUM(n, SG_GHOST_REQUIRE_ARG,)
|
||||
))
|
||||
);
|
||||
|
||||
#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_COMMA_IF(n)\
|
||||
BOOST_PP_ENUM_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_COMMA_IF(n)\
|
||||
BOOST_PP_ENUM_PARAMS(n,A)\
|
||||
>(name, SG_GHOST_FUNC_TYPE(fn));\
|
||||
}
|
||||
|
||||
|
||||
SG_GHOST_MEM_FN()
|
||||
SG_GHOST_MEM_FN(const)
|
||||
|
||||
#undef SG_GHOST_MEM_FN
|
||||
|
||||
/**
|
||||
* Bind free function accepting an instance of raw_type and an arbitrary
|
||||
* number of arguments as method.
|
||||
*/
|
||||
template<
|
||||
class Ret,
|
||||
class Type
|
||||
BOOST_PP_COMMA_IF(n)
|
||||
BOOST_PP_ENUM_PARAMS(n, class A)
|
||||
>
|
||||
Ghost& method
|
||||
(
|
||||
const std::string& name,
|
||||
Ret (*fn)(Type BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_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
|
||||
@@ -45,6 +45,24 @@ namespace nasal
|
||||
return from_nasal_helper(c, ref, static_cast<T*>(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pointer to specific version of from_nasal, converting to a type
|
||||
* compatible to Var.
|
||||
*/
|
||||
template<class Var>
|
||||
struct from_nasal_ptr
|
||||
{
|
||||
typedef typename boost::remove_const
|
||||
< typename boost::remove_reference<Var>::type
|
||||
>::type return_type;
|
||||
typedef return_type(*type)(naContext, naRef);
|
||||
|
||||
static type get()
|
||||
{
|
||||
return &from_nasal<return_type>;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
#endif /* SG_FROM_NASAL_HXX_ */
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace nasal
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
naRef to_nasal(naContext c, naRef ref)
|
||||
naRef to_nasal(naContext c, const naRef& ref)
|
||||
{
|
||||
return ref;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace nasal
|
||||
/**
|
||||
* Simple pass-through of naRef types to allow generic usage of to_nasal
|
||||
*/
|
||||
naRef to_nasal(naContext c, naRef ref);
|
||||
naRef to_nasal(naContext c, const naRef& ref);
|
||||
|
||||
naRef to_nasal(naContext c, const SGPath& path);
|
||||
|
||||
@@ -116,6 +116,22 @@ namespace nasal
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper to get pointer to specific version of to_nasal applicable to given
|
||||
* type.
|
||||
*/
|
||||
template<class Var>
|
||||
struct to_nasal_ptr
|
||||
{
|
||||
typedef typename boost::call_traits<Var>::param_type param_type;
|
||||
typedef naRef(*type)(naContext, param_type);
|
||||
|
||||
static type get()
|
||||
{
|
||||
return static_cast<type>(&to_nasal);
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
template<class Vec2>
|
||||
typename boost::enable_if<is_vec2<Vec2>, naRef>::type
|
||||
|
||||
Reference in New Issue
Block a user