]> git.mxchange.org Git - simgear.git/commitdiff
cppbind: convert Nasal functions to C++ boost::function.
authorThomas Geymayer <tomgey@gmail.com>
Tue, 15 Oct 2013 15:37:57 +0000 (17:37 +0200)
committerThomas Geymayer <tomgey@gmail.com>
Tue, 15 Oct 2013 15:46:31 +0000 (17:46 +0200)
simgear/nasal/cppbind/CMakeLists.txt
simgear/nasal/cppbind/NasalHash.hxx
simgear/nasal/cppbind/NasalObjectHolder.cxx [new file with mode: 0644]
simgear/nasal/cppbind/NasalObjectHolder.hxx [new file with mode: 0644]
simgear/nasal/cppbind/cppbind_test.cxx
simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx [new file with mode: 0644]
simgear/nasal/cppbind/detail/from_nasal_helper.hxx

index 129aae83218457fe0686a7fcee65195f7c6f2c92..a139202ee855cf1b567d603bd49ab5c813e9a7e2 100644 (file)
@@ -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
index 4cc15642acd3fde349279b6d7cb09fa2c5b24dfe..914d264010632dbfa84addd95a49880abb9ed30f 100644 (file)
@@ -86,6 +86,21 @@ namespace nasal
         return from_nasal<T>(_context, get(name));
       }
 
+      /**
+       * Get member converted to callable object
+       *
+       * @tparam Sig    Function signature
+       * @param name    Member name
+       */
+      template<class Sig>
+      typename boost::enable_if< boost::is_function<Sig>,
+                                 boost::function<Sig>
+                               >::type
+      get(const std::string& name)
+      {
+        return from_nasal_helper(_context, get(name), static_cast<Sig*>(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 (file)
index 0000000..555a273
--- /dev/null
@@ -0,0 +1,51 @@
+// Wrapper class for keeping Nasal objects save from the garbage collector
+//
+// Copyright (C) 2013  Thomas Geymayer <tomgey@gmail.com>
+//
+// 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 <simgear/nasal/nasal.h>
+
+namespace nasal
+{
+
+  //----------------------------------------------------------------------------
+  ObjectHolder::~ObjectHolder()
+  {
+    naGCRelease(_gc_key);
+  }
+
+  //----------------------------------------------------------------------------
+  naRef ObjectHolder::get_naRef() const
+  {
+    return _ref;
+  }
+
+  //----------------------------------------------------------------------------
+  SGSharedPtr<ObjectHolder> 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 (file)
index 0000000..c63b059
--- /dev/null
@@ -0,0 +1,66 @@
+///@file Wrapper class for keeping Nasal objects save from the garbage collector
+//
+// Copyright (C) 2013  Thomas Geymayer <tomgey@gmail.com>
+//
+// 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 <simgear/nasal/naref.h>
+#include <simgear/structure/SGSharedPtr.hxx>
+
+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<ObjectHolder> 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_ */
index 2a010d387666470aa7d74c070e7b674ad1cdeb99..5b265964371230343572f9892118ca87735a35e8 100644 (file)
@@ -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<naRef>(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<int (int)> f = hash.get<int (int)>("func");
+  VERIFY( f );
+  VERIFY( f(3) == 3 );
+
+  boost::function<std::string (int)> fs = hash.get<std::string (int)>("func");
+  VERIFY( fs );
+  VERIFY( fs(14) == "14" );
+
+  typedef boost::function<void (int)> FuncVoidInt;
+  FuncVoidInt fvi = hash.get<FuncVoidInt>("func");
+  VERIFY( fvi );
+  fvi(123);
+
+  typedef boost::function<std::string (const std::string&, int, float)> FuncMultiArg;
+  FuncMultiArg fma = hash.get<FuncMultiArg>("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 (file)
index 0000000..2b75b17
--- /dev/null
@@ -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<A##n>::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<boost::is_void<Ret>, 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<Ret*>(0));
+    naFreeContext(ctx);
+
+    return r;
+  }
+
+  template<
+    class Ret
+    BOOST_PP_COMMA_IF(n)
+    BOOST_PP_ENUM_PARAMS(n, class A)
+  >
+  typename boost::enable_if<boost::is_void<Ret>, 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<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
+  boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A)))
+#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD
+  ;
+#else
+  {
+    return boost::bind
+    (
+      &callNasalFunction<Ret BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, A)>,
+      ObjectHolder::makeShared(code)
+      BOOST_PP_COMMA_IF(n)
+      BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _)
+    );
+  }
+#endif
+
+#undef n
index 7953ee969ca75fe2b6f94359830399f447942d20..cb7aee01457fcf084b756a55de909dc90b126e06 100644 (file)
 #include "nasal_traits.hxx"
 
 #include <simgear/nasal/nasal.h>
+#include <simgear/nasal/cppbind/NasalObjectHolder.hxx>
+#include <simgear/nasal/cppbind/to_nasal.hxx>
 #include <simgear/structure/exception.hxx>
+#include <simgear/structure/SGSharedPtr.hxx>
 
-#include <boost/utility/enable_if.hpp>
+#include <boost/bind.hpp>
+#include <boost/call_traits.hpp>
+#include <boost/function.hpp>
+#include <boost/preprocessor/iteration/iterate.hpp>
+#include <boost/preprocessor/repetition/enum_shifted_params.hpp>
 #include <boost/type_traits.hpp>
+#include <boost/utility/enable_if.hpp>
 
 #include <string>
-#include <typeinfo> // std::bad_cast
 #include <vector>
 
 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 <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
+#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<class Sig>
+  boost::function<Sig>
+  from_nasal_helper(naContext c, naRef ref, boost::function<Sig>*)
+  {
+    if(    !naIsCode(ref)
+        && !naIsCCode(ref)
+        && !naIsFunc(ref) )
+      throw bad_nasal_cast("not a function");
+
+    return detail::boostFunctionFromNasal(ref, static_cast<Sig*>(0));
+  }
+
+  template<class Sig>
+  typename boost::enable_if< boost::is_function<Sig>,
+                             boost::function<Sig>
+                           >::type
+  from_nasal_helper(naContext c, naRef ref, Sig*)
+  {
+    return from_nasal_helper(c, ref, static_cast<boost::function<Sig>*>(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 <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
+#include BOOST_PP_ITERATE()
+  }
+
 } // namespace nasal
 
 #endif /* SG_FROM_NASAL_HELPER_HXX_ */