3 #ifndef SG_NASAL_GHOST_HXX_
4 #define SG_NASAL_GHOST_HXX_
6 #include "from_nasal.hxx"
7 #include "to_nasal.hxx"
9 #include <simgear/debug/logstream.hxx>
11 #include <boost/bind.hpp>
12 #include <boost/function.hpp>
13 #include <boost/lambda/lambda.hpp>
14 #include <boost/utility/enable_if.hpp>
20 * Traits for C++ classes exposed as Ghost. This is the basic template for
24 struct GhostTypeTraits
26 /** Whether the class is passed by shared pointer or raw pointer */
27 typedef boost::false_type::type is_shared;
29 /** The raw class type (Without any shared pointer) */
34 struct GhostTypeTraits<boost::shared_ptr<T> >
36 typedef boost::true_type::type is_shared;
42 struct GhostTypeTraits<osg::ref_ptr<T> >
44 typedef boost::true_type::type is_shared;
49 #ifdef SGSharedPtr_HXX
51 struct GhostTypeTraits<SGSharedPtr<T> >
53 typedef boost::true_type::type is_shared;
59 * Policy for creating ghost instances from shared pointer objects.
62 struct SharedPointerPolicy
64 typedef typename GhostTypeTraits<T>::raw_type raw_type;
67 * Create a shared pointer on the heap to handle the reference counting for
68 * the passed shared pointer while it is used in Nasal space.
70 static T* createInstance(const T& ptr)
75 static raw_type* getRawPtr(void* ptr)
77 return static_cast<T*>(ptr)->get();
82 * Policy for creating ghost instances as raw objects on the heap.
85 struct RawPointerPolicy
87 typedef typename GhostTypeTraits<T>::raw_type raw_type;
90 * Create a new object instance on the heap
92 static T* createInstance()
97 static raw_type* getRawPtr(void* ptr)
99 BOOST_STATIC_ASSERT((boost::is_same<raw_type, T>::value));
100 return static_cast<T*>(ptr);
107 * Metadata for Ghost object types
113 * Add a nasal base class to the ghost. Will be available in the ghosts
116 void addNasalBase(const naRef& parent)
118 assert( naIsHash(parent) );
119 _parents.push_back(parent);
123 const std::string _name;
124 naGhostType _ghost_type;
125 // std::vector<GhostMetadata*> _base_classes;
126 std::vector<naRef> _parents;
128 explicit GhostMetadata(const std::string& name):
134 // void addBaseClass(GhostMetadata* base)
137 // _base_classes.push_back(base);
140 naRef getParents(naContext c)
142 return nasal::to_nasal(c, _parents);
148 * Class for exposing C++ objects to Nasal
151 * // Example class to be exposed to Nasal
158 * naRef myMember(int argc, naRef* args);
161 * void exposeClasses()
163 * // Register a nasal ghost type for MyClass. This needs to be done only
164 * // once before creating the first ghost instance.
165 * Ghost<MyClass>::init("MyClass")
166 * // Members can be exposed by getters and setters
167 * .member("x", &MyClass::getX, &MyClass::setX)
168 * // For readonly variables only pass a getter
169 * .member("x_readonly", &MyClass::getX)
170 * // It is also possible to expose writeonly members
171 * .member("x_writeonly", &MyClass::setX)
172 * // Methods use a slightly different syntax - The pointer to the member
173 * // function has to be passed as template argument
174 * .method<&MyClass::myMember>("myMember");
180 public internal::GhostMetadata,
181 protected boost::mpl::if_< typename GhostTypeTraits<T>::is_shared,
182 SharedPointerPolicy<T>,
183 RawPointerPolicy<T> >::type
186 typedef typename GhostTypeTraits<T>::raw_type raw_type;
187 typedef naRef (T::*member_func_t)(int, naRef*);
188 typedef boost::function<naRef(naContext c, raw_type*)> getter_t;
189 typedef boost::function<void(naContext c, raw_type*, naRef)> setter_t;
192 * A ghost member. Can consist either of getter and/or setter functions
193 * for exposing a data variable or a single callable function.
201 member_t( const getter_t& getter,
202 const setter_t& setter,
203 naCFunction func = 0 ):
209 member_t(naCFunction func):
218 typedef std::map<std::string, member_t> MemberMap;
221 * Register a new ghost type.
223 * @param name Descriptive name of the ghost type.
225 static Ghost& init(const std::string& name)
227 getSingletonHolder().reset( new Ghost(name) );
228 return *getSingletonPtr();
232 * Register a base class for this ghost. The base class needs to be
233 * registers on its own before it can be used as base class.
235 * @tparam BaseGhost Type of base class already wrapped into Ghost class
239 * Ghost<MyBase>::init("MyBase");
240 * Ghost<MyClass>::init("MyClass")
241 * .bases<Ghost<MyBase> >();
244 template<class BaseGhost>
245 typename boost::enable_if_c
246 < boost::is_base_of<GhostMetadata, BaseGhost>::value
247 && boost::is_base_of<typename BaseGhost::raw_type, raw_type>::value,
252 BaseGhost* base = BaseGhost::getSingletonPtr();
253 //addBaseClass( base );
255 // Replace any getter that is not available in the current class.
256 // TODO check if this is the correct behavior of function overriding
257 for( typename BaseGhost::MemberMap::const_iterator member =
258 base->_members.begin();
259 member != base->_members.end();
262 if( _members.find(member->first) == _members.end() )
263 _members[member->first] = member_t
265 member->second.getter,
266 member->second.setter,
275 * Register a base class for this ghost. The base class needs to be
276 * registers on its own before it can be used as base class.
278 * @tparam Base Type of base class (Base as used in Ghost<Base>)
281 * Ghost<MyBase>::init("MyBase");
282 * Ghost<MyClass>::init("MyClass")
287 typename boost::enable_if_c
288 < !boost::is_base_of<GhostMetadata, Base>::value
289 && boost::is_base_of<typename Ghost<Base>::raw_type, raw_type>::value,
294 return bases< Ghost<Base> >();
298 * Register an existing Nasal class/hash as base class for this ghost.
300 * @param parent Nasal hash/class
302 Ghost& bases(const naRef& parent)
304 addNasalBase(parent);
309 * Register a member variable by passing a getter and/or setter method.
311 * @param field Name of member
312 * @param getter Getter for variable
313 * @param setter Setter for variable (Pass 0 to prevent write access)
317 Ghost& member( const std::string& field,
318 Var (raw_type::*getter)() const,
319 void (raw_type::*setter)(Var) = 0 )
324 naRef (*to_nasal_)(naContext, Var) = &nasal::to_nasal;
326 // Getter signature: naRef(naContext, raw_type*)
327 m.getter = boost::bind(to_nasal_, _1, boost::bind(getter, _2));
332 Var (*from_nasal_)(naContext, naRef) = &nasal::from_nasal;
334 // Setter signature: void(naContext, raw_type*, naRef)
335 m.setter = boost::bind(setter, _2, boost::bind(from_nasal_, _1, _3));
338 return member(field, m.getter, m.setter);
342 * Register a write only member variable.
344 * @param field Name of member
345 * @param setter Setter for variable
348 Ghost& member( const std::string& field,
349 void (raw_type::*setter)(Var) )
351 return member<Var>(field, 0, setter);
355 * Register a member variable by passing a getter and/or setter method.
357 * @param field Name of member
358 * @param getter Getter for variable
359 * @param setter Setter for variable (Pass empty to prevent write access)
362 Ghost& member( const std::string& field,
363 const getter_t& getter,
364 const setter_t& setter = setter_t() )
366 if( !getter.empty() || !setter.empty() )
367 _members[field] = member_t(getter, setter);
373 "Member '" << field << "' requires a getter or setter"
379 * Register a member function.
381 * @note Because only function pointers can be registered as Nasal
382 * functions it is needed to pass the function pointer as template
383 * argument. This allows us to create a separate instance of the
384 * MemberFunctionWrapper for each registered function and therefore
385 * provides us with the needed static functions to be passed on to
388 * @tparam func Pointer to member function being registered.
394 * naRef myMethod(int argc, naRef* args);
397 * Ghost<MyClass>::init("Test")
398 * .method<&MyClass::myMethod>("myMethod");
401 template<member_func_t func>
402 Ghost& method(const std::string& name)
404 _members[name].func = &MemberFunctionWrapper<func>::call;
408 // TODO use variadic template when supporting C++11
410 * Create a Nasal instance of this ghost.
412 * @param c Active Nasal context
414 static naRef create( naContext c )
416 return makeGhost(c, Ghost::createInstance());
420 * Create a Nasal instance of this ghost.
422 * @param c Active Nasal context
423 * @param a1 Parameter used for creating new instance
426 static naRef create( naContext c, const A1& a1 )
428 return makeGhost(c, Ghost::createInstance(a1));
432 * Nasal callback for creating a new instance of this ghost.
434 static naRef f_create(naContext c, naRef me, int argc, naRef* args)
444 static Ghost* getSingletonPtr()
446 return getSingletonHolder().get();
450 * Wrapper class to enable registering pointers to member functions as
451 * Nasal function callbacks. We need to use the function pointer as
452 * template parameter to ensure every registered function gets a static
453 * function which can be passed to Nasal.
455 template<member_func_t func>
456 struct MemberFunctionWrapper
459 * Called from Nasal upon invocation of the according registered
460 * function. Forwards the call to the passed object instance.
462 static naRef call(naContext c, naRef me, int argc, naRef* args)
464 if( naGhost_type(me) != &getSingletonPtr()->_ghost_type )
468 "method called on object of wrong type: '%s' expected",
469 getSingletonPtr()->_ghost_type.name
472 raw_type* obj = Ghost::getRawPtr( static_cast<T*>(naGhost_ptr(me)) );
475 return (obj->*func)(argc, args);
479 typedef std::auto_ptr<Ghost> GhostPtr;
482 explicit Ghost(const std::string& name):
483 GhostMetadata( name )
485 _ghost_type.destroy = &destroyGhost;
486 _ghost_type.name = _name.c_str();
487 _ghost_type.get_member = &getMember;
488 _ghost_type.set_member = &setMember;
491 static GhostPtr& getSingletonHolder()
493 static GhostPtr instance;
497 static naRef makeGhost(naContext c, void *ptr)
499 return naNewGhost2(c, &getSingletonPtr()->_ghost_type, ptr);
502 static void destroyGhost(void *ptr)
508 * Callback for retrieving a ghost member.
510 static const char* getMember(naContext c, void* g, naRef key, naRef* out)
512 const std::string key_str = nasal::from_nasal<std::string>(c, key);
513 if( key_str == "parents" )
515 if( getSingletonPtr()->_parents.empty() )
518 *out = getSingletonPtr()->getParents(c);
522 typename MemberMap::iterator member =
523 getSingletonPtr()->_members.find(key_str);
525 if( member == getSingletonPtr()->_members.end() )
528 if( member->second.func )
529 *out = nasal::to_nasal(c, member->second.func);
530 else if( !member->second.getter.empty() )
531 *out = member->second.getter(c, Ghost::getRawPtr(g));
533 return "Read-protected member";
539 * Callback for writing to a ghost member.
541 static void setMember(naContext c, void* g, naRef field, naRef val)
543 const std::string key = nasal::from_nasal<std::string>(c, field);
544 typename MemberMap::iterator member =
545 getSingletonPtr()->_members.find(key);
547 if( member == getSingletonPtr()->_members.end() )
548 naRuntimeError(c, "ghost: No such member: %s", key.c_str());
549 if( member->second.setter.empty() )
550 naRuntimeError(c, "ghost: Write protected member: %s", key.c_str());
552 member->second.setter(c, Ghost::getRawPtr(g), val);
558 #endif /* SG_NASAL_GHOST_HXX_ */