]> git.mxchange.org Git - simgear.git/blobdiff - simgear/nasal/cppbind/Ghost.hxx
First working version of DOM like Canvas event handling
[simgear.git] / simgear / nasal / cppbind / Ghost.hxx
index 300db25047b476d7299fbbe85f2a8dc613225814..d2a5d22c9906b80610750537961cb6f4720706ad 100644 (file)
@@ -26,6 +26,7 @@
 #include <simgear/debug/logstream.hxx>
 
 #include <boost/bind.hpp>
+#include <boost/call_traits.hpp>
 #include <boost/function.hpp>
 #include <boost/lambda/lambda.hpp>
 #include <boost/utility/enable_if.hpp>
@@ -83,8 +84,9 @@ namespace nasal
   template<class T>
   struct SharedPointerPolicy
   {
-    typedef typename GhostTypeTraits<T>::raw_type raw_type;
-    typedef boost::false_type returns_dynamic_type;
+    typedef typename GhostTypeTraits<T>::raw_type   raw_type;
+    typedef T                                       pointer;
+    typedef boost::false_type                       returns_dynamic_type;
 
     /**
      * Create a shared pointer on the heap to handle the reference counting for
@@ -95,9 +97,25 @@ namespace nasal
       return new T(ptr);
     }
 
+    static pointer getPtr(void* ptr)
+    {
+      if( ptr )
+        return *static_cast<T*>(ptr);
+      else
+        return pointer();
+    }
+
     static raw_type* getRawPtr(void* ptr)
     {
-      return static_cast<T*>(ptr)->get();
+      if( ptr )
+        return static_cast<T*>(ptr)->get();
+      else
+        return 0;
+    }
+
+    static raw_type* getRawPtr(const T& ptr)
+    {
+      return ptr.get();
     }
   };
 
@@ -107,8 +125,9 @@ namespace nasal
   template<class T>
   struct RawPointerPolicy
   {
-    typedef typename GhostTypeTraits<T>::raw_type raw_type;
-    typedef boost::true_type returns_dynamic_type;
+    typedef typename GhostTypeTraits<T>::raw_type   raw_type;
+    typedef raw_type*                               pointer;
+    typedef boost::true_type                        returns_dynamic_type;
 
     /**
      * Create a new object instance on the heap
@@ -118,6 +137,12 @@ namespace nasal
       return new T();
     }
 
+    static pointer getPtr(void* ptr)
+    {
+      BOOST_STATIC_ASSERT((boost::is_same<pointer, T*>::value));
+      return static_cast<T*>(ptr);
+    }
+
     static raw_type* getRawPtr(void* ptr)
     {
       BOOST_STATIC_ASSERT((boost::is_same<raw_type, T>::value));
@@ -143,11 +168,30 @@ namespace nasal
           _parents.push_back(parent);
         }
 
+        bool isBaseOf(naGhostType* ghost_type) const
+        {
+          if( ghost_type == &_ghost_type )
+            return true;
+
+          for( DerivedList::const_iterator derived = _derived_classes.begin();
+                                           derived != _derived_classes.end();
+                                         ++derived )
+          {
+            if( (*derived)->isBaseOf(ghost_type) )
+              return true;
+          }
+
+          return false;
+        }
+
       protected:
-        const std::string             _name;
-        naGhostType                   _ghost_type;
-  //      std::vector<GhostMetadata*>   _base_classes;
-        std::vector<naRef>            _parents;
+
+        typedef std::vector<const GhostMetadata*> DerivedList;
+
+        const std::string   _name;
+        naGhostType         _ghost_type;
+        DerivedList         _derived_classes;
+        std::vector<naRef>  _parents;
 
         explicit GhostMetadata(const std::string& name):
           _name(name)
@@ -155,11 +199,18 @@ namespace nasal
 
         }
 
-  //      void addBaseClass(GhostMetadata* base)
-  //      {
-  //        assert(base);
-  //        _base_classes.push_back(base);
-  //      }
+        void addDerived(const GhostMetadata* derived)
+        {
+          assert(derived);
+          _derived_classes.push_back(derived);
+
+          SG_LOG
+          (
+            SG_NASAL,
+            SG_INFO,
+            "Ghost::addDerived: " <<_ghost_type.name << " -> " << derived->_name
+          );
+        }
 
         naRef getParents(naContext c)
         {
@@ -168,6 +219,52 @@ namespace nasal
     };
   }
 
+  /**
+   * Context passed to a function/method being called from Nasal
+   */
+  struct CallContext
+  {
+    CallContext(naContext c, size_t argc, naRef* args):
+      c(c),
+      argc(argc),
+      args(args)
+    {}
+
+    /**
+     * Get the argument with given index if it exists. Otherwise returns the
+     * passed default value.
+     *
+     * @tparam T    Type of argument (converted using ::from_nasal)
+     * @param index Index of requested argument
+     * @param def   Default value returned if too few arguments available
+     */
+    template<class T>
+    T getArg(size_t index, const T& def = T()) const
+    {
+      if( index >= argc )
+        return def;
+
+      return from_nasal<T>(c, args[index]);
+    }
+
+    /**
+     * Get the argument with given index. Raises a Nasal runtime error if there
+     * are to few arguments available.
+     */
+    template<class T>
+    T requireArg(size_t index) const
+    {
+      if( index >= argc )
+        naRuntimeError(c, "Missing required arg #%d", index);
+
+      return from_nasal<T>(c, args[index]);
+    }
+
+    naContext   c;
+    size_t      argc;
+    naRef      *args;
+  };
+
   /**
    * Class for exposing C++ objects to Nasal
    *
@@ -207,11 +304,13 @@ namespace nasal
                                RawPointerPolicy<T> >::type
   {
     public:
-      typedef typename GhostTypeTraits<T>::raw_type                 raw_type;
-      typedef naRef (raw_type::*member_func_t)(naContext, int, naRef*);
-      typedef naRef (*free_func_t)(raw_type&, naContext, int, naRef*);
-      typedef boost::function<naRef(naContext, raw_type*)>        getter_t;
-      typedef boost::function<void(naContext, raw_type*, naRef)>  setter_t;
+      typedef T                                                   value_type;
+      typedef typename GhostTypeTraits<T>::raw_type               raw_type;
+      typedef typename Ghost::pointer                             pointer;
+      typedef naRef (raw_type::*member_func_t)(const CallContext&);
+      typedef naRef (*free_func_t)(raw_type&, const CallContext&);
+      typedef boost::function<naRef(naContext, raw_type&)>        getter_t;
+      typedef boost::function<void(naContext, raw_type&, naRef)>  setter_t;
 
       /**
        * A ghost member. Can consist either of getter and/or setter functions
@@ -279,7 +378,21 @@ namespace nasal
       bases()
       {
         BaseGhost* base = BaseGhost::getSingletonPtr();
-        base->addDerived( &getTypeFor<BaseGhost> );
+        base->addDerived
+        (
+          this,
+          // Both ways of retrieving the address of a static member function
+          // should be legal but not all compilers know this.
+          // g++-4.4.7+ has been tested to work with both versions
+#if defined(GCC_VERSION) && GCC_VERSION < 40407
+          // The old version of g++ used on Jenkins (16.11.2012) only compiles
+          // this version.
+          &getTypeFor<BaseGhost>
+#else
+          // VS (2008, 2010, ... ?) only allow this version.
+          &Ghost::getTypeFor<BaseGhost>
+#endif
+        );
 
         // Replace any getter that is not available in the current class.
         // TODO check if this is the correct behavior of function overriding
@@ -345,14 +458,15 @@ namespace nasal
       template<class Var>
       Ghost& member( const std::string& field,
                      Var (raw_type::*getter)() const,
-                     void (raw_type::*setter)(Var) = 0 )
+                     void (raw_type::*setter)(typename boost::call_traits<Var>::param_type) = 0 )
       {
         member_t m;
         if( getter )
         {
-          naRef (*to_nasal_)(naContext, Var) = &nasal::to_nasal;
+          typedef typename boost::call_traits<Var>::param_type param_type;
+          naRef (*to_nasal_)(naContext, param_type) = &nasal::to_nasal;
 
-          // Getter signature: naRef(naContext, raw_type*)
+          // Getter signature: naRef(naContext, raw_type&)
           m.getter = boost::bind(to_nasal_, _1, boost::bind(getter, _2));
         }
 
@@ -360,7 +474,7 @@ namespace nasal
         {
           Var (*from_nasal_)(naContext, naRef) = &nasal::from_nasal;
 
-          // Setter signature: void(naContext, raw_type*, naRef)
+          // Setter signature: void(naContext, raw_type&, naRef)
           m.setter = boost::bind(setter, _2, boost::bind(from_nasal_, _1, _3));
         }
 
@@ -491,6 +605,55 @@ namespace nasal
         return create(c);
       }
 
+      static bool isBaseOf(naGhostType* ghost_type)
+      {
+        if( !ghost_type )
+          return false;
+
+        return getSingletonPtr()->GhostMetadata::isBaseOf(ghost_type);
+      }
+
+      static bool isBaseOf(naRef obj)
+      {
+        return isBaseOf( naGhost_type(obj) );
+      }
+
+      /**
+       * Convert Nasal object to C++ object. To get a valid object the passed
+       * Nasal objects has to be derived class of the target class (Either
+       * derived in C++ or in Nasal using a 'parents' vector)
+       */
+      static pointer fromNasal(naContext c, naRef me)
+      {
+        // Check if it's a ghost and if it can be converted
+        if( isBaseOf( naGhost_type(me) ) )
+          return Ghost::getPtr( naGhost_ptr(me) );
+
+        // Now if it is derived from a ghost (hash with ghost in parent vector)
+        else if( naIsHash(me) )
+        {
+          naRef na_parents = naHash_cget(me, const_cast<char*>("parents"));
+          if( !naIsVector(na_parents) )
+          {
+            SG_LOG(SG_NASAL, SG_DEBUG, "Missing 'parents' vector for ghost");
+            return pointer();
+          }
+
+          typedef std::vector<naRef> naRefs;
+          naRefs parents = from_nasal<naRefs>(c, na_parents);
+          for( naRefs::const_iterator parent = parents.begin();
+                                      parent != parents.end();
+                                    ++parent )
+          {
+            pointer ptr = fromNasal(c, *parent);
+            if( ptr )
+              return ptr;
+          }
+        }
+
+        return pointer();
+      }
+
     private:
 
       template<class>
@@ -498,11 +661,13 @@ namespace nasal
 
       typedef naGhostType* (*type_checker_t)(const raw_type*);
       typedef std::vector<type_checker_t> DerivedList;
-      DerivedList _derived_classes;
+      DerivedList _derived_types;
 
-      void addDerived(const type_checker_t& derived_info)
+      void addDerived( const internal::GhostMetadata* derived_meta,
+                       const type_checker_t& derived_info )
       {
-        _derived_classes.push_back(derived_info);
+        GhostMetadata::addDerived(derived_meta);
+        _derived_types.push_back(derived_info);
       }
 
       template<class BaseGhost>
@@ -519,13 +684,13 @@ namespace nasal
                  < typename BaseGhost::raw_type,
                    typename Ghost::raw_type
                  >::value
-            && dynamic_cast<const Ghost::raw_type*>(base) != base )
+            && dynamic_cast<const typename Ghost::raw_type*>(base) != base )
           return 0;
 
         // Now check if we can further downcast to one of our derived classes.
         for( typename DerivedList::reverse_iterator
-               derived = getSingletonPtr()->_derived_classes.rbegin();
-               derived != getSingletonPtr()->_derived_classes.rend();
+               derived = getSingletonPtr()->_derived_types.rbegin();
+               derived != getSingletonPtr()->_derived_types.rend();
              ++derived )
         {
           naGhostType* ghost_type =
@@ -557,28 +722,21 @@ namespace nasal
         return getSingletonHolder().get();
       }
 
-      // TODO integrate with from_nasal template to be able to cast objects
-      //      passed as function argument.
-      static raw_type* from_nasal(naRef me)
+      static raw_type& requireObject(naContext c, naRef me)
       {
-        if( naGhost_type(me) != &getSingletonPtr()->_ghost_type )
-          return 0;
-
-        return Ghost::getRawPtr( static_cast<T*>(naGhost_ptr(me)) );
-      }
+        raw_type* obj = Ghost::getRawPtr( fromNasal(c, me) );
+        naGhostType* ghost_type = naGhost_type(me);
 
-      static raw_type* requireObject(naContext c, naRef me)
-      {
-        raw_type* obj = Ghost::from_nasal(me);
         if( !obj )
           naRuntimeError
           (
             c,
-            "method called on object of wrong type: '%s' expected",
+            "method called on object of wrong type: is '%s' expected '%s'",
+            ghost_type ? ghost_type->name : "unknown",
             getSingletonPtr()->_ghost_type.name
           );
 
-        return obj;
+        return *obj;
       }
 
       /**
@@ -596,7 +754,7 @@ namespace nasal
          */
         static naRef call(naContext c, naRef me, int argc, naRef* args)
         {
-          return (requireObject(c, me)->*func)(c, argc, args);
+          return (requireObject(c, me).*func)(CallContext(c, argc, args));
         }
       };
 
@@ -618,7 +776,7 @@ namespace nasal
          */
         static naRef call(naContext c, naRef me, int argc, naRef* args)
         {
-          return func(*requireObject(c, me), c, argc, args);
+          return func(requireObject(c, me), CallContext(c, argc, args));
         }
       };
 
@@ -642,6 +800,9 @@ namespace nasal
 
       static naRef makeGhost(naContext c, void *ptr)
       {
+        if( !Ghost::getRawPtr(ptr) )
+          return naNil();
+
         naGhostType* ghost_type = 0;
         if( Ghost::returns_dynamic_type::value )
           // For pointer policies already returning instances of an object with
@@ -690,7 +851,7 @@ namespace nasal
         if( member->second.func )
           *out = nasal::to_nasal(c, member->second.func);
         else if( !member->second.getter.empty() )
-          *out = member->second.getter(c, Ghost::getRawPtr(g));
+          *out = member->second.getter(c, *Ghost::getRawPtr(g));
         else
           return "Read-protected member";
 
@@ -711,7 +872,7 @@ namespace nasal
         if( member->second.setter.empty() )
           naRuntimeError(c, "ghost: Write protected member: %s", key.c_str());
 
-        member->second.setter(c, Ghost::getRawPtr(g), val);
+        member->second.setter(c, *Ghost::getRawPtr(g), val);
       }
   };