From 88a35fd1622be2d6e7d9bd017b1bf8445adf6e8f Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Tue, 27 Nov 2012 13:01:46 +0100 Subject: [PATCH] cppbind: Automatic conversion of derived ghosts and some cleanup/fixes --- simgear/nasal/cppbind/Ghost.hxx | 185 ++++++++++++++++---- simgear/nasal/cppbind/NasalHash.cxx | 10 +- simgear/nasal/cppbind/NasalHash.hxx | 22 ++- simgear/nasal/cppbind/cppbind_test.cxx | 46 ++++- simgear/nasal/cppbind/from_nasal.cxx | 4 +- simgear/nasal/cppbind/from_nasal.hxx | 4 +- simgear/nasal/cppbind/from_nasal_detail.hxx | 27 ++- 7 files changed, 240 insertions(+), 58 deletions(-) diff --git a/simgear/nasal/cppbind/Ghost.hxx b/simgear/nasal/cppbind/Ghost.hxx index 759b25ed..6fce4cbc 100644 --- a/simgear/nasal/cppbind/Ghost.hxx +++ b/simgear/nasal/cppbind/Ghost.hxx @@ -83,8 +83,9 @@ namespace nasal template struct SharedPointerPolicy { - typedef typename GhostTypeTraits::raw_type raw_type; - typedef boost::false_type returns_dynamic_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 @@ -95,9 +96,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(); } }; @@ -107,8 +124,9 @@ namespace nasal template struct RawPointerPolicy { - typedef typename GhostTypeTraits::raw_type raw_type; - typedef boost::true_type returns_dynamic_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 @@ -118,6 +136,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)); @@ -143,11 +167,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) @@ -155,11 +198,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 +218,22 @@ namespace nasal }; } + /** + * Context passed to a function/method being called from Nasal + */ + struct CallContext + { + CallContext(naContext c, int argc, naRef* args): + c(c), + argc(argc), + args(args) + {} + + naContext c; + int argc; + naRef *args; + }; + /** * Class for exposing C++ objects to Nasal * @@ -207,9 +273,11 @@ namespace nasal RawPointerPolicy >::type { public: - typedef typename GhostTypeTraits::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 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; @@ -281,6 +349,7 @@ namespace nasal BaseGhost* base = BaseGhost::getSingletonPtr(); 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 @@ -504,6 +573,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) + // TODO handle recursive parents + 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 ) + { + if( isBaseOf(naGhost_type(*parent)) ) + return Ghost::getPtr( naGhost_ptr(*parent) ); + } + } + + return pointer(); + } + private: template @@ -511,11 +629,13 @@ namespace nasal typedef naGhostType* (*type_checker_t)(const raw_type*); typedef std::vector 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 @@ -537,8 +657,8 @@ namespace nasal // 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 = @@ -570,24 +690,17 @@ 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) - { - if( naGhost_type(me) != &getSingletonPtr()->_ghost_type ) - return 0; - - return Ghost::getRawPtr( static_cast(naGhost_ptr(me)) ); - } - static raw_type& requireObject(naContext c, naRef me) { - raw_type* obj = Ghost::from_nasal(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: '%s' expected", + "method called on object of wrong type: is '%s' expected '%s'", + ghost_type ? ghost_type->name : "unknown", getSingletonPtr()->_ghost_type.name ); @@ -609,7 +722,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)); } }; @@ -631,7 +744,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)); } }; diff --git a/simgear/nasal/cppbind/NasalHash.cxx b/simgear/nasal/cppbind/NasalHash.cxx index 1595c266..20fb2ec5 100644 --- a/simgear/nasal/cppbind/NasalHash.cxx +++ b/simgear/nasal/cppbind/NasalHash.cxx @@ -33,7 +33,7 @@ namespace nasal } //---------------------------------------------------------------------------- - Hash::Hash(const naRef& hash, naContext c): + Hash::Hash(naRef hash, naContext c): _hash(hash), _context(c) { @@ -46,6 +46,14 @@ namespace nasal naHash_set(_hash, to_nasal(_context, name), ref); } + //---------------------------------------------------------------------------- + naRef Hash::get(const std::string& name) + { + naRef result; + return naHash_get(_hash, to_nasal(_context, name), &result) ? result + : naNil(); + } + //---------------------------------------------------------------------------- Hash Hash::createHash(const std::string& name) { diff --git a/simgear/nasal/cppbind/NasalHash.hxx b/simgear/nasal/cppbind/NasalHash.hxx index 0e48a9dd..4cc15642 100644 --- a/simgear/nasal/cppbind/NasalHash.hxx +++ b/simgear/nasal/cppbind/NasalHash.hxx @@ -19,6 +19,7 @@ #ifndef SG_NASAL_HASH_HXX_ #define SG_NASAL_HASH_HXX_ +#include "from_nasal.hxx" #include "to_nasal.hxx" namespace nasal @@ -44,7 +45,7 @@ namespace nasal * @param hash Existing Nasal Hash * @param c Nasal context for creating new Nasal objects */ - Hash(const naRef& hash, naContext c); + Hash(naRef hash, naContext c); /** * Set member @@ -66,6 +67,25 @@ namespace nasal set(name, to_nasal(_context, val)); } + /** + * Get member + * + * @param name Member name + */ + naRef get(const std::string& name); + + /** + * Get member converted to given type + * + * @tparam T Type to convert to (using from_nasal) + * @param name Member name + */ + template + T get(const std::string& name) + { + return from_nasal(_context, get(name)); + } + /** * Create a new child hash (module) * diff --git a/simgear/nasal/cppbind/cppbind_test.cxx b/simgear/nasal/cppbind/cppbind_test.cxx index 30d4a338..4a2802ac 100644 --- a/simgear/nasal/cppbind/cppbind_test.cxx +++ b/simgear/nasal/cppbind/cppbind_test.cxx @@ -15,7 +15,7 @@ struct Base { - naRef member(naContext, int, naRef*) { return naNil(); } + naRef member(const nasal::CallContext&) { return naNil(); } virtual ~Base(){}; }; struct Derived: @@ -36,7 +36,13 @@ struct DoubleDerived2: }; -naRef member(Derived&, naContext, int, naRef*) { return naNil(); } +typedef boost::shared_ptr BasePtr; +typedef boost::shared_ptr DerivedPtr; +typedef boost::shared_ptr DoubleDerivedPtr; +typedef boost::shared_ptr DoubleDerived2Ptr; + +naRef member(Derived&, const nasal::CallContext&) { return naNil(); } +naRef member(DerivedPtr&, const nasal::CallContext&) { return naNil(); } int main(int argc, char* argv[]) { @@ -84,6 +90,9 @@ int main(int argc, char* argv[]) r = to_nasal(c, hash); VERIFY( naIsHash(r) ); + VERIFY( hash.get("name") == "my-name" ); + VERIFY( naIsString(hash.get("name")) ); + Hash mod = hash.createHash("mod"); mod.set("parent", hash); @@ -98,14 +107,11 @@ int main(int argc, char* argv[]) VERIFY( naIsGhost(derived) ); VERIFY( std::string("Derived") == naGhost_type(derived)->name ); - typedef boost::shared_ptr BasePtr; - typedef boost::shared_ptr DerivedPtr; - typedef boost::shared_ptr DoubleDerivedPtr; - typedef boost::shared_ptr DoubleDerived2Ptr; - Ghost::init("BasePtr"); Ghost::init("DerivedPtr") - .bases(); + .bases() + .member("x", &Derived::getX, &Derived::setX) + .method_func<&member>("free_member"); Ghost::init("DoubleDerivedPtr") .bases(); Ghost::init("DoubleDerived2Ptr") @@ -126,7 +132,29 @@ int main(int argc, char* argv[]) VERIFY( naIsGhost(derived) ); VERIFY( std::string("DoubleDerived2Ptr") == naGhost_type(derived)->name ); - // TODO actuall do something with the ghosts... + VERIFY( Ghost::isBaseOf(derived) ); + VERIFY( Ghost::isBaseOf(derived) ); + VERIFY( Ghost::isBaseOf(derived) ); + + VERIFY( Ghost::fromNasal(c, derived) == d3 ); + VERIFY( Ghost::fromNasal(c, derived) != d2 ); + VERIFY( Ghost::fromNasal(c, derived) + == boost::dynamic_pointer_cast(d3) ); + VERIFY( Ghost::fromNasal(c, derived) + == boost::dynamic_pointer_cast(d3) ); + VERIFY( !Ghost::fromNasal(c, derived) ); + + // Check converting to Ghost if using Nasal hashes with actual ghost inside + // the hashes parents vector + std::vector parents; + parents.push_back(hash.get_naRef()); + parents.push_back(derived); + + Hash obj(c); + obj.set("parents", parents); + VERIFY( Ghost::fromNasal(c, obj.get_naRef()) == d3 ); + + // TODO actually do something with the ghosts... naFreeContext(c); diff --git a/simgear/nasal/cppbind/from_nasal.cxx b/simgear/nasal/cppbind/from_nasal.cxx index 03c4dc2e..0cede8c1 100644 --- a/simgear/nasal/cppbind/from_nasal.cxx +++ b/simgear/nasal/cppbind/from_nasal.cxx @@ -47,14 +47,14 @@ namespace nasal } //---------------------------------------------------------------------------- - std::string from_nasal(naContext c, naRef ref, std::string*) + std::string from_nasal_helper(naContext c, naRef ref, std::string*) { naRef na_str = naStringValue(c, ref); return std::string(naStr_data(na_str), naStr_len(na_str)); } //---------------------------------------------------------------------------- - Hash from_nasal(naContext c, naRef ref, Hash*) + Hash from_nasal_helper(naContext c, naRef ref, Hash*) { if( !naIsHash(ref) ) throw bad_nasal_cast("Not a hash"); diff --git a/simgear/nasal/cppbind/from_nasal.hxx b/simgear/nasal/cppbind/from_nasal.hxx index d36d5826..37b63659 100644 --- a/simgear/nasal/cppbind/from_nasal.hxx +++ b/simgear/nasal/cppbind/from_nasal.hxx @@ -37,12 +37,12 @@ namespace nasal * @note Every type which should be supported needs a function with the * following signature declared: * - * Type from_nasal(naContext, naRef, Type*) + * Type from_nasal_helper(naContext, naRef, Type*) */ template T from_nasal(naContext c, naRef ref) { - return from_nasal(c, ref, static_cast(0)); + return from_nasal_helper(c, ref, static_cast(0)); } } // namespace nasal diff --git a/simgear/nasal/cppbind/from_nasal_detail.hxx b/simgear/nasal/cppbind/from_nasal_detail.hxx index 2a053e4d..48a8879e 100644 --- a/simgear/nasal/cppbind/from_nasal_detail.hxx +++ b/simgear/nasal/cppbind/from_nasal_detail.hxx @@ -63,15 +63,20 @@ namespace nasal std::string _msg; }; + /** + * Simple pass through for unified handling also of naRef. + */ + inline naRef from_nasal_helper(naContext, naRef ref, naRef*) { return ref; } + /** * Convert Nasal string to std::string */ - std::string from_nasal(naContext c, naRef ref, std::string*); + std::string from_nasal_helper(naContext c, naRef ref, std::string*); /** * Convert a Nasal hash to a nasal::Hash */ - Hash from_nasal(naContext c, naRef ref, Hash*); + Hash from_nasal_helper(naContext c, naRef ref, Hash*); /** * Convert a Nasal number to a C++ numeric type @@ -80,7 +85,7 @@ namespace nasal typename boost::enable_if< boost::is_arithmetic, T >::type - from_nasal(naContext c, naRef ref, T*) + from_nasal_helper(naContext c, naRef ref, T*) { naRef num = naNumValue(ref); if( !naIsNum(num) ) @@ -92,11 +97,14 @@ namespace nasal /** * Convert a Nasal vector to a std::vector */ - template - typename boost::enable_if< boost::is_same >, + template + typename boost::enable_if< boost::is_same + < Vector, + std::vector + >, Vector >::type - from_nasal(naContext c, naRef ref, Vector*) + from_nasal_helper(naContext c, naRef ref, Vector*) { if( !naIsVector(ref) ) throw bad_nasal_cast("Not a vector"); @@ -105,7 +113,12 @@ namespace nasal Vector vec(size); for(int i = 0; i < size; ++i) - vec[i] = from_nasal(c, naVec_get(ref, i)); + vec[i] = from_nasal_helper + ( + c, + naVec_get(ref, i), + static_cast(0) + ); return vec; } -- 2.39.5