From 5258699f205ba3fcc2ebf4cf60ed256327bf2be5 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Tue, 15 Oct 2013 17:37:57 +0200 Subject: [PATCH] cppbind: convert Nasal functions to C++ boost::function. --- simgear/nasal/cppbind/CMakeLists.txt | 3 + simgear/nasal/cppbind/NasalHash.hxx | 15 ++++ simgear/nasal/cppbind/NasalObjectHolder.cxx | 51 +++++++++++ simgear/nasal/cppbind/NasalObjectHolder.hxx | 66 ++++++++++++++ simgear/nasal/cppbind/cppbind_test.cxx | 22 ++++- .../detail/from_nasal_function_templates.hxx | 85 +++++++++++++++++++ .../cppbind/detail/from_nasal_helper.hxx | 55 +++++++++++- 7 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 simgear/nasal/cppbind/NasalObjectHolder.cxx create mode 100644 simgear/nasal/cppbind/NasalObjectHolder.hxx create mode 100644 simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx diff --git a/simgear/nasal/cppbind/CMakeLists.txt b/simgear/nasal/cppbind/CMakeLists.txt index 129aae83..a139202e 100644 --- a/simgear/nasal/cppbind/CMakeLists.txt +++ b/simgear/nasal/cppbind/CMakeLists.txt @@ -4,12 +4,14 @@ set(HEADERS Ghost.hxx NasalCallContext.hxx NasalHash.hxx + NasalObjectHolder.hxx NasalString.hxx from_nasal.hxx to_nasal.hxx ) set(DETAIL_HEADERS + detail/from_nasal_function_templates.hxx detail/from_nasal_helper.hxx detail/functor_templates.hxx detail/nasal_traits.hxx @@ -18,6 +20,7 @@ set(DETAIL_HEADERS set(SOURCES NasalHash.cxx + NasalObjectHolder.cxx NasalString.cxx detail/from_nasal_helper.cxx detail/to_nasal_helper.cxx diff --git a/simgear/nasal/cppbind/NasalHash.hxx b/simgear/nasal/cppbind/NasalHash.hxx index 4cc15642..914d2640 100644 --- a/simgear/nasal/cppbind/NasalHash.hxx +++ b/simgear/nasal/cppbind/NasalHash.hxx @@ -86,6 +86,21 @@ namespace nasal return from_nasal(_context, get(name)); } + /** + * Get member converted to callable object + * + * @tparam Sig Function signature + * @param name Member name + */ + template + typename boost::enable_if< boost::is_function, + boost::function + >::type + get(const std::string& name) + { + return from_nasal_helper(_context, get(name), static_cast(0)); + } + /** * Create a new child hash (module) * diff --git a/simgear/nasal/cppbind/NasalObjectHolder.cxx b/simgear/nasal/cppbind/NasalObjectHolder.cxx new file mode 100644 index 00000000..555a2735 --- /dev/null +++ b/simgear/nasal/cppbind/NasalObjectHolder.cxx @@ -0,0 +1,51 @@ +// Wrapper class for keeping Nasal objects save from the garbage collector +// +// Copyright (C) 2013 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#include "NasalObjectHolder.hxx" +#include + +namespace nasal +{ + + //---------------------------------------------------------------------------- + ObjectHolder::~ObjectHolder() + { + naGCRelease(_gc_key); + } + + //---------------------------------------------------------------------------- + naRef ObjectHolder::get_naRef() const + { + return _ref; + } + + //---------------------------------------------------------------------------- + SGSharedPtr ObjectHolder::makeShared(naRef obj) + { + return new ObjectHolder(obj); + } + + //---------------------------------------------------------------------------- + ObjectHolder::ObjectHolder(naRef obj): + _ref(obj), + _gc_key(naGCSave(obj)) + { + + } + +} // namespace nasal diff --git a/simgear/nasal/cppbind/NasalObjectHolder.hxx b/simgear/nasal/cppbind/NasalObjectHolder.hxx new file mode 100644 index 00000000..c63b059d --- /dev/null +++ b/simgear/nasal/cppbind/NasalObjectHolder.hxx @@ -0,0 +1,66 @@ +///@file Wrapper class for keeping Nasal objects save from the garbage collector +// +// Copyright (C) 2013 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SG_NASAL_OBJECT_HOLDER_HXX_ +#define SG_NASAL_OBJECT_HOLDER_HXX_ + +#include +#include + +namespace nasal +{ + + /** + * Prevent a Nasal object from being destroyed by the garbage collector during + * the lifetime of this object. + */ + class ObjectHolder: + public SGReferenced + { + public: + + /** + * + */ + ~ObjectHolder(); + + /** + * Get captured Nasal object + */ + naRef get_naRef() const; + + /** + * Save the given object as long as the returned holder exists. + * + * @param obj Object to save + */ + static SGSharedPtr makeShared(naRef obj); + + protected: + naRef _ref; + int _gc_key; + + /** + * @param obj Object to save + */ + ObjectHolder(naRef obj); + }; + +} // namespace nasal + +#endif /* SG_NASAL_OBJECT_HOLDER_HXX_ */ diff --git a/simgear/nasal/cppbind/cppbind_test.cxx b/simgear/nasal/cppbind/cppbind_test.cxx index 2a010d38..5b265964 100644 --- a/simgear/nasal/cppbind/cppbind_test.cxx +++ b/simgear/nasal/cppbind/cppbind_test.cxx @@ -80,7 +80,7 @@ naRef f_derivedGetX(naContext c, const Derived& d) { return nasal::to_nasal(c, d.getX()); } -naRef f_freeFunction(nasal::CallContext) { return naNil(); } +naRef f_freeFunction(nasal::CallContext c) { return c.requireArg(0); } int main(int argc, char* argv[]) { @@ -145,6 +145,26 @@ int main(int argc, char* argv[]) Hash mod = hash.createHash("mod"); mod.set("parent", hash); + + // 'func' is a C++ function registered to Nasal and now converted back to C++ + boost::function f = hash.get("func"); + VERIFY( f ); + VERIFY( f(3) == 3 ); + + boost::function fs = hash.get("func"); + VERIFY( fs ); + VERIFY( fs(14) == "14" ); + + typedef boost::function FuncVoidInt; + FuncVoidInt fvi = hash.get("func"); + VERIFY( fvi ); + fvi(123); + + typedef boost::function FuncMultiArg; + FuncMultiArg fma = hash.get("func"); + VERIFY( fma ); + VERIFY( fma("test", 3, .5) == "test" ); + //---------------------------------------------------------------------------- // Test exposing classes to Nasal //---------------------------------------------------------------------------- diff --git a/simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx b/simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx new file mode 100644 index 00000000..2b75b172 --- /dev/null +++ b/simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx @@ -0,0 +1,85 @@ +#ifndef SG_FROM_NASAL_HELPER_HXX_ +# error Nasal cppbind - do not include this file! +#endif + +#define n BOOST_PP_ITERATION() + +#ifndef SG_BOOST_FUNCTION_FROM_NASAL_FWD +# define SG_CALL_TRAITS_PARAM(z, n, dummy)\ + typename boost::call_traits::param_type a##n +# define SG_CALL_ARG(z, n, dummy)\ + to_nasal(ctx, a##n) + + template< + class Ret + BOOST_PP_COMMA_IF(n) + BOOST_PP_ENUM_PARAMS(n, class A) + > + typename boost::disable_if, Ret>::type + callNasalFunction( const ObjectHolder* holder + BOOST_PP_COMMA_IF(n) + BOOST_PP_ENUM(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]); + + naRef result = + naCallMethod(holder->get_naRef(), naNil(), num_args, args, naNil()); + + Ret r = from_nasal_helper(ctx, result, static_cast(0)); + naFreeContext(ctx); + + return r; + } + + template< + class Ret + BOOST_PP_COMMA_IF(n) + BOOST_PP_ENUM_PARAMS(n, class A) + > + typename boost::enable_if, Ret>::type + callNasalFunction( const ObjectHolder* holder + BOOST_PP_COMMA_IF(n) + BOOST_PP_ENUM(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); + } + +# undef SG_CALL_TRAITS_PARAM +# undef SG_CALL_ARG +#endif + + template< + class Ret + BOOST_PP_COMMA_IF(n) + BOOST_PP_ENUM_PARAMS(n, class A) + > + boost::function + boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A))) +#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD + ; +#else + { + return boost::bind + ( + &callNasalFunction, + ObjectHolder::makeShared(code) + BOOST_PP_COMMA_IF(n) + BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _) + ); + } +#endif + +#undef n diff --git a/simgear/nasal/cppbind/detail/from_nasal_helper.hxx b/simgear/nasal/cppbind/detail/from_nasal_helper.hxx index 7953ee96..cb7aee01 100644 --- a/simgear/nasal/cppbind/detail/from_nasal_helper.hxx +++ b/simgear/nasal/cppbind/detail/from_nasal_helper.hxx @@ -23,13 +23,20 @@ #include "nasal_traits.hxx" #include +#include +#include #include +#include -#include +#include +#include +#include +#include +#include #include +#include #include -#include // std::bad_cast #include class SGPath; @@ -98,6 +105,42 @@ namespace nasal */ bool from_nasal_helper(naContext c, naRef ref, const bool*); + namespace detail + { +#define SG_BOOST_FUNCTION_FROM_NASAL_FWD +#define BOOST_PP_ITERATION_LIMITS (0, 9) +#define BOOST_PP_FILENAME_1 +#include BOOST_PP_ITERATE() +#undef SG_BOOST_FUNCTION_FROM_NASAL_FWD + } + + /** + * Convert a Nasal function to a boost::function with the given signature. + * + * @tparam Sig Signature of returned function (arguments and return value + * are automatically converted using from_nasal/to_nasal) + */ + template + boost::function + from_nasal_helper(naContext c, naRef ref, boost::function*) + { + if( !naIsCode(ref) + && !naIsCCode(ref) + && !naIsFunc(ref) ) + throw bad_nasal_cast("not a function"); + + return detail::boostFunctionFromNasal(ref, static_cast(0)); + } + + template + typename boost::enable_if< boost::is_function, + boost::function + >::type + from_nasal_helper(naContext c, naRef ref, Sig*) + { + return from_nasal_helper(c, ref, static_cast*>(0)); + } + /** * Convert a Nasal number to a C++ numeric type */ @@ -147,6 +190,14 @@ namespace nasal return Vec2(vec[0], vec[1]); } + // Helpers for wrapping calls to Nasal functions into boost::function + namespace detail + { +#define BOOST_PP_ITERATION_LIMITS (0, 9) +#define BOOST_PP_FILENAME_1 +#include BOOST_PP_ITERATE() + } + } // namespace nasal #endif /* SG_FROM_NASAL_HELPER_HXX_ */ -- 2.39.5