]> git.mxchange.org Git - simgear.git/commitdiff
cppbind: allow calling methods with 'me' object from C++.
authorThomas Geymayer <tomgey@gmail.com>
Sat, 22 Mar 2014 11:31:03 +0000 (12:31 +0100)
committerThomas Geymayer <tomgey@gmail.com>
Sat, 22 Mar 2014 11:41:47 +0000 (12:41 +0100)
simgear/nasal/cppbind/Ghost.hxx
simgear/nasal/cppbind/NasalHash.cxx
simgear/nasal/cppbind/NasalHash.hxx
simgear/nasal/cppbind/cppbind_test.cxx
simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx
simgear/nasal/cppbind/detail/from_nasal_helper.hxx
simgear/nasal/cppbind/detail/functor_templates.hxx

index a1d82b32919c5a033d17785050b139060d825709..b186a186ff71db4947a08fc95aba634b11e87ef1 100644 (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
           );
 
index 20fb2ec5c8909e762141eb7d26d3e0cd82ea4086..d1dd3be32460b02c70ad4403f64a52b2928d4ea9 100644 (file)
@@ -69,7 +69,7 @@ namespace nasal
   }
 
   //----------------------------------------------------------------------------
-  const naRef Hash::get_naRef() const
+  naRef Hash::get_naRef() const
   {
     return _hash;
   }
index 914d264010632dbfa84addd95a49880abb9ed30f..e1de133df85e55fae09b1535060d52c083b60399 100644 (file)
@@ -118,7 +118,7 @@ namespace nasal
       /**
        * Get Nasal representation of Hash
        */
-      const naRef get_naRef() const;
+      naRef get_naRef() const;
 
     protected:
 
index a152a7228add225e09fac07c11c5e6222b6d22c1..d8f2461261e26fff80c5957eb5eab8e0705197e4 100644 (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
   //----------------------------------------------------------------------------
index e72adbf751131518b0cb9345d306227de8c3101c..6b788122c7e59aac9ea306f1c536d7156f97706d 100644 (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
 
   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[] = {
     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
 
   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
   ;
   {
     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
index cb7aee01457fcf084b756a55de909dc90b126e06..0769fc64148ff5e685271b1cd9f203e72ae05cb3 100644 (file)
 #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()
   }
index 59e9a0fb8374baa94f69a4796704e5dc883d2f40..e17b7d49c2f2eea1bc2634ebfe9a6f7778d63266 100644 (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));\
   }
 
   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
 
 #undef n
 #undef SG_GHOST_TYPEDEF_FUNC_TYPE
+#endif // SG_DONT_DO_ANYTHING