X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fnasal%2Fcppbind%2FGhost.hxx;h=d2a5d22c9906b80610750537961cb6f4720706ad;hb=46442ef50c5a2b7c8e41e5c025f86c1cd35e6e15;hp=b8b471eafbe1c6389a53356a3f89da6531ab0f29;hpb=55fbe68e6253d5cacba2bec840a49f1b7b026362;p=simgear.git diff --git a/simgear/nasal/cppbind/Ghost.hxx b/simgear/nasal/cppbind/Ghost.hxx index b8b471ea..d2a5d22c 100644 --- a/simgear/nasal/cppbind/Ghost.hxx +++ b/simgear/nasal/cppbind/Ghost.hxx @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -83,7 +84,9 @@ namespace nasal template struct SharedPointerPolicy { - typedef typename GhostTypeTraits::raw_type raw_type; + typedef typename GhostTypeTraits::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 @@ -94,9 +97,25 @@ namespace nasal return new T(ptr); } + static pointer getPtr(void* ptr) + { + if( ptr ) + return *static_cast(ptr); + else + return pointer(); + } + static raw_type* getRawPtr(void* ptr) { - return static_cast(ptr)->get(); + if( ptr ) + return static_cast(ptr)->get(); + else + return 0; + } + + static raw_type* getRawPtr(const T& ptr) + { + return ptr.get(); } }; @@ -106,7 +125,9 @@ namespace nasal template struct RawPointerPolicy { - typedef typename GhostTypeTraits::raw_type raw_type; + typedef typename GhostTypeTraits::raw_type raw_type; + typedef raw_type* pointer; + typedef boost::true_type returns_dynamic_type; /** * Create a new object instance on the heap @@ -116,6 +137,12 @@ namespace nasal return new T(); } + static pointer getPtr(void* ptr) + { + BOOST_STATIC_ASSERT((boost::is_same::value)); + return static_cast(ptr); + } + static raw_type* getRawPtr(void* ptr) { BOOST_STATIC_ASSERT((boost::is_same::value)); @@ -141,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 _base_classes; - std::vector _parents; + + typedef std::vector DerivedList; + + const std::string _name; + naGhostType _ghost_type; + DerivedList _derived_classes; + std::vector _parents; explicit GhostMetadata(const std::string& name): _name(name) @@ -153,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) { @@ -166,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 + T getArg(size_t index, const T& def = T()) const + { + if( index >= argc ) + return def; + + return from_nasal(c, args[index]); + } + + /** + * Get the argument with given index. Raises a Nasal runtime error if there + * are to few arguments available. + */ + template + T requireArg(size_t index) const + { + if( index >= argc ) + naRuntimeError(c, "Missing required arg #%d", index); + + return from_nasal(c, args[index]); + } + + naContext c; + size_t argc; + naRef *args; + }; + /** * Class for exposing C++ objects to Nasal * @@ -177,7 +276,7 @@ namespace nasal * void setX(int x); * int getX() const; * - * naRef myMember(int argc, naRef* args); + * naRef myMember(naContext c, int argc, naRef* args); * } * * void exposeClasses() @@ -205,10 +304,13 @@ namespace nasal RawPointerPolicy >::type { public: - typedef typename GhostTypeTraits::raw_type raw_type; - typedef naRef (T::*member_func_t)(int, naRef*); - typedef boost::function getter_t; - typedef boost::function setter_t; + typedef T value_type; + typedef typename GhostTypeTraits::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 getter_t; + typedef boost::function setter_t; /** * A ghost member. Can consist either of getter and/or setter functions @@ -242,10 +344,14 @@ namespace nasal /** * Register a new ghost type. * + * @note Only intialize each ghost type once! + * * @param name Descriptive name of the ghost type. */ static Ghost& init(const std::string& name) { + assert( !getSingletonPtr() ); + getSingletonHolder().reset( new Ghost(name) ); return *getSingletonPtr(); } @@ -272,7 +378,21 @@ namespace nasal bases() { BaseGhost* base = BaseGhost::getSingletonPtr(); - //addBaseClass( base ); + 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 +#else + // VS (2008, 2010, ... ?) only allow this version. + &Ghost::getTypeFor +#endif + ); // Replace any getter that is not available in the current class. // TODO check if this is the correct behavior of function overriding @@ -338,14 +458,15 @@ namespace nasal template 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::param_type) = 0 ) { member_t m; if( getter ) { - naRef (*to_nasal_)(naContext, Var) = &nasal::to_nasal; + typedef typename boost::call_traits::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)); } @@ -353,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)); } @@ -413,7 +534,7 @@ namespace nasal * class MyClass * { * public: - * naRef myMethod(int argc, naRef* args); + * naRef myMethod(naContext c, int argc, naRef* args); * } * * Ghost::init("Test") @@ -427,6 +548,32 @@ namespace nasal return *this; } + /** + * 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::init("Test") + * .method_func<&myMethod>("myMethod"); + * @endcode + */ + template + Ghost& method_func(const std::string& name) + { + _members[name].func = &FreeFunctionWrapper::call; + return *this; + } + // TODO use variadic template when supporting C++11 /** * Create a Nasal instance of this ghost. @@ -458,16 +605,140 @@ 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("parents")); + if( !naIsVector(na_parents) ) + { + SG_LOG(SG_NASAL, SG_DEBUG, "Missing 'parents' vector for ghost"); + return pointer(); + } + + typedef std::vector naRefs; + naRefs parents = from_nasal(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 friend class Ghost; + typedef naGhostType* (*type_checker_t)(const raw_type*); + typedef std::vector DerivedList; + DerivedList _derived_types; + + void addDerived( const internal::GhostMetadata* derived_meta, + const type_checker_t& derived_info ) + { + GhostMetadata::addDerived(derived_meta); + _derived_types.push_back(derived_info); + } + + template + static + typename boost::enable_if + < boost::is_polymorphic, + naGhostType* + >::type + getTypeFor(const typename BaseGhost::raw_type* base) + { + // Check first if passed pointer can by converted to instance of class + // this ghost wraps. + if( !boost::is_same + < typename BaseGhost::raw_type, + typename Ghost::raw_type + >::value + && dynamic_cast(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_types.rbegin(); + derived != getSingletonPtr()->_derived_types.rend(); + ++derived ) + { + naGhostType* ghost_type = + (*derived)( static_cast(base) ); + if( ghost_type ) + return ghost_type; + } + + // If base is not an instance of any derived class, this class has to + // be the dynamic type. + return &getSingletonPtr()->_ghost_type; + } + + template + static + typename boost::disable_if + < boost::is_polymorphic, + naGhostType* + >::type + getTypeFor(const typename BaseGhost::raw_type* base) + { + // For non polymorphic classes there is no possibility to get the actual + // dynamic type, therefore we can only use its static type. + return &BaseGhost::getSingletonPtr()->_ghost_type; + } + static Ghost* getSingletonPtr() { return getSingletonHolder().get(); } + static raw_type& requireObject(naContext c, naRef me) + { + raw_type* obj = Ghost::getRawPtr( fromNasal(c, me) ); + naGhostType* ghost_type = naGhost_type(me); + + if( !obj ) + naRuntimeError + ( + c, + "method called on object of wrong type: is '%s' expected '%s'", + ghost_type ? ghost_type->name : "unknown", + getSingletonPtr()->_ghost_type.name + ); + + return *obj; + } + /** * Wrapper class to enable registering pointers to member functions as * Nasal function callbacks. We need to use the function pointer as @@ -483,18 +754,29 @@ namespace nasal */ static naRef call(naContext c, naRef me, int argc, naRef* args) { - if( naGhost_type(me) != &getSingletonPtr()->_ghost_type ) - naRuntimeError - ( - c, - "method called on object of wrong type: '%s' expected", - getSingletonPtr()->_ghost_type.name - ); - - raw_type* obj = Ghost::getRawPtr( static_cast(naGhost_ptr(me)) ); - assert(obj); + return (requireObject(c, me).*func)(CallContext(c, argc, args)); + } + }; - return (obj->*func)(argc, args); + /** + * 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. + */ + template + struct FreeFunctionWrapper + { + /** + * 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)); } }; @@ -518,7 +800,26 @@ namespace nasal static naRef makeGhost(naContext c, void *ptr) { - return naNewGhost2(c, &getSingletonPtr()->_ghost_type, 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 + // the dynamic type of this Ghost's raw_type the type is always the + // same. + ghost_type = &getSingletonPtr()->_ghost_type; + else + // If wrapping eg. shared pointers the users passes an already + // existing instance of an object which will then be hold be a new + // shared pointer. We therefore have to check for the dynamic type + // of the object as it might differ from the passed static type. + ghost_type = getTypeFor( Ghost::getRawPtr(ptr) ); + + if( !ghost_type ) + return naNil(); + + return naNewGhost2(c, ghost_type, ptr); } static void destroyGhost(void *ptr) @@ -550,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"; @@ -571,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); } };