]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/cppbind/Ghost.hxx
Add nasal::Ghost class for exposing C++ classes to Nasal
[simgear.git] / simgear / nasal / cppbind / Ghost.hxx
1 ///@file
2
3 #ifndef SG_NASAL_GHOST_HXX_
4 #define SG_NASAL_GHOST_HXX_
5
6 #include "from_nasal.hxx"
7 #include "to_nasal.hxx"
8
9 #include <simgear/debug/logstream.hxx>
10
11 #include <boost/bind.hpp>
12 #include <boost/function.hpp>
13 #include <boost/lambda/lambda.hpp>
14 #include <boost/utility/enable_if.hpp>
15
16 namespace nasal
17 {
18
19   /**
20    * Traits for C++ classes exposed as Ghost. This is the basic template for
21    * raw types.
22    */
23   template<class T>
24   struct GhostTypeTraits
25   {
26     /** Whether the class is passed by shared pointer or raw pointer */
27     typedef boost::false_type::type is_shared;
28
29     /** The raw class type (Without any shared pointer) */
30     typedef T raw_type;
31   };
32
33   template<class T>
34   struct GhostTypeTraits<boost::shared_ptr<T> >
35   {
36     typedef boost::true_type::type is_shared;
37     typedef T raw_type;
38   };
39
40 #ifdef OSG_REF_PTR
41   template<class T>
42   struct GhostTypeTraits<osg::ref_ptr<T> >
43   {
44     typedef boost::true_type::type is_shared;
45     typedef T raw_type;
46   };
47 #endif
48
49 #ifdef SGSharedPtr_HXX
50   template<class T>
51   struct GhostTypeTraits<SGSharedPtr<T> >
52   {
53     typedef boost::true_type::type is_shared;
54     typedef T raw_type;
55   };
56 #endif
57
58   /**
59    * Policy for creating ghost instances from shared pointer objects.
60    */
61   template<class T>
62   struct SharedPointerPolicy
63   {
64     typedef typename GhostTypeTraits<T>::raw_type raw_type;
65
66     /**
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.
69      */
70     static T* createInstance(const T& ptr)
71     {
72       return new T(ptr);
73     }
74
75     static raw_type* getRawPtr(void* ptr)
76     {
77       return static_cast<T*>(ptr)->get();
78     }
79   };
80
81   /**
82    * Policy for creating ghost instances as raw objects on the heap.
83    */
84   template<class T>
85   struct RawPointerPolicy
86   {
87     typedef typename GhostTypeTraits<T>::raw_type raw_type;
88
89     /**
90      * Create a new object instance on the heap
91      */
92     static T* createInstance()
93     {
94       return new T();
95     }
96
97     static raw_type* getRawPtr(void* ptr)
98     {
99       BOOST_STATIC_ASSERT((boost::is_same<raw_type, T>::value));
100       return static_cast<T*>(ptr);
101     }
102   };
103
104   namespace internal
105   {
106     /**
107      * Metadata for Ghost object types
108      */
109     class GhostMetadata
110     {
111       public:
112         /**
113          * Add a nasal base class to the ghost. Will be available in the ghosts
114          * parents array.
115          */
116         void addNasalBase(const naRef& parent)
117         {
118           assert( naIsHash(parent) );
119           _parents.push_back(parent);
120         }
121
122       protected:
123         const std::string             _name;
124         naGhostType                   _ghost_type;
125   //      std::vector<GhostMetadata*>   _base_classes;
126         std::vector<naRef>            _parents;
127
128         explicit GhostMetadata(const std::string& name):
129           _name(name)
130         {
131
132         }
133
134   //      void addBaseClass(GhostMetadata* base)
135   //      {
136   //        assert(base);
137   //        _base_classes.push_back(base);
138   //      }
139
140         naRef getParents(naContext c)
141         {
142           return nasal::to_nasal(c, _parents);
143         }
144     };
145   }
146
147   /**
148    * Class for exposing C++ objects to Nasal
149    *
150    * @code{cpp}
151    * // Example class to be exposed to Nasal
152    * class MyClass
153    * {
154    *   public:
155    *     void setX(int x);
156    *     int getX() const;
157    *
158    *     naRef myMember(int argc, naRef* args);
159    * }
160    *
161    * void exposeClasses()
162    * {
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");
175    * }
176    * @endcode
177    */
178   template<class T>
179   class Ghost:
180     public internal::GhostMetadata,
181     protected boost::mpl::if_< typename GhostTypeTraits<T>::is_shared,
182                                SharedPointerPolicy<T>,
183                                RawPointerPolicy<T> >::type
184   {
185     public:
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;
190
191       /**
192        * A ghost member. Can consist either of getter and/or setter functions
193        * for exposing a data variable or a single callable function.
194        */
195       struct member_t
196       {
197         member_t():
198           func(0)
199         {}
200
201         member_t( const getter_t& getter,
202                   const setter_t& setter,
203                   naCFunction func = 0 ):
204           getter( getter ),
205           setter( setter ),
206           func( func )
207         {}
208
209         member_t(naCFunction func):
210           func( func )
211         {}
212
213         getter_t    getter;
214         setter_t    setter;
215         naCFunction func;
216       };
217
218       typedef std::map<std::string, member_t> MemberMap;
219
220       /**
221        * Register a new ghost type.
222        *
223        * @param name    Descriptive name of the ghost type.
224        */
225       static Ghost& init(const std::string& name)
226       {
227         getSingletonHolder().reset( new Ghost(name) );
228         return *getSingletonPtr();
229       }
230
231       /**
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.
234        *
235        * @tparam BaseGhost  Type of base class already wrapped into Ghost class
236        *                    (Ghost<Base>)
237        *
238        * @code{cpp}
239        * Ghost<MyBase>::init("MyBase");
240        * Ghost<MyClass>::init("MyClass")
241        *   .bases<Ghost<MyBase> >();
242        * @endcode
243        */
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,
248           Ghost
249         >::type&
250       bases()
251       {
252         BaseGhost* base = BaseGhost::getSingletonPtr();
253         //addBaseClass( base );
254
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();
260              ++member )
261         {
262           if( _members.find(member->first) == _members.end() )
263             _members[member->first] = member_t
264             (
265               member->second.getter,
266               member->second.setter,
267               member->second.func
268             );
269         }
270
271         return *this;
272       }
273
274       /**
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.
277        *
278        * @tparam Base   Type of base class (Base as used in Ghost<Base>)
279        *
280        * @code{cpp}
281        * Ghost<MyBase>::init("MyBase");
282        * Ghost<MyClass>::init("MyClass")
283        *   .bases<MyBase>();
284        * @endcode
285        */
286       template<class Base>
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,
290           Ghost
291         >::type&
292       bases()
293       {
294         return bases< Ghost<Base> >();
295       }
296
297       /**
298        * Register an existing Nasal class/hash as base class for this ghost.
299        *
300        * @param parent  Nasal hash/class
301        */
302       Ghost& bases(const naRef& parent)
303       {
304         addNasalBase(parent);
305         return *this;
306       }
307
308       /**
309        * Register a member variable by passing a getter and/or setter method.
310        *
311        * @param field   Name of member
312        * @param getter  Getter for variable
313        * @param setter  Setter for variable (Pass 0 to prevent write access)
314        *
315        */
316       template<class Var>
317       Ghost& member( const std::string& field,
318                      Var (raw_type::*getter)() const,
319                      void (raw_type::*setter)(Var) = 0 )
320       {
321         member_t m;
322         if( getter )
323         {
324           naRef (*to_nasal_)(naContext, Var) = &nasal::to_nasal;
325
326           // Getter signature: naRef(naContext, raw_type*)
327           m.getter = boost::bind(to_nasal_, _1, boost::bind(getter, _2));
328         }
329
330         if( setter )
331         {
332           Var (*from_nasal_)(naContext, naRef) = &nasal::from_nasal;
333
334           // Setter signature: void(naContext, raw_type*, naRef)
335           m.setter = boost::bind(setter, _2, boost::bind(from_nasal_, _1, _3));
336         }
337
338         return member(field, m.getter, m.setter);
339       }
340
341       /**
342        * Register a write only member variable.
343        *
344        * @param field   Name of member
345        * @param setter  Setter for variable
346        */
347       template<class Var>
348       Ghost& member( const std::string& field,
349                      void (raw_type::*setter)(Var) )
350       {
351         return member<Var>(field, 0, setter);
352       }
353
354       /**
355        * Register a member variable by passing a getter and/or setter method.
356        *
357        * @param field   Name of member
358        * @param getter  Getter for variable
359        * @param setter  Setter for variable (Pass empty to prevent write access)
360        *
361        */
362       Ghost& member( const std::string& field,
363                      const getter_t& getter,
364                      const setter_t& setter = setter_t() )
365       {
366         if( !getter.empty() || !setter.empty() )
367           _members[field] = member_t(getter, setter);
368         else
369           SG_LOG
370           (
371             SG_NASAL,
372             SG_WARN,
373             "Member '" << field << "' requires a getter or setter"
374           );
375         return *this;
376       }
377
378       /**
379        * Register a member function.
380        *
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
386        *       Nasal.
387        *
388        * @tparam func   Pointer to member function being registered.
389        *
390        * @code{cpp}
391        * class MyClass
392        * {
393        *   public:
394        *     naRef myMethod(int argc, naRef* args);
395        * }
396        *
397        * Ghost<MyClass>::init("Test")
398        *   .method<&MyClass::myMethod>("myMethod");
399        * @endcode
400        */
401       template<member_func_t func>
402       Ghost& method(const std::string& name)
403       {
404         _members[name].func = &MemberFunctionWrapper<func>::call;
405         return *this;
406       }
407
408       // TODO use variadic template when supporting C++11
409       /**
410        * Create a Nasal instance of this ghost.
411        *
412        * @param c   Active Nasal context
413        */
414       static naRef create( naContext c )
415       {
416         return makeGhost(c, Ghost::createInstance());
417       }
418
419       /**
420        * Create a Nasal instance of this ghost.
421        *
422        * @param c   Active Nasal context
423        * @param a1  Parameter used for creating new instance
424        */
425       template<class A1>
426       static naRef create( naContext c, const A1& a1 )
427       {
428         return makeGhost(c, Ghost::createInstance(a1));
429       }
430
431       /**
432        * Nasal callback for creating a new instance of this ghost.
433        */
434       static naRef f_create(naContext c, naRef me, int argc, naRef* args)
435       {
436         return create(c);
437       }
438
439     private:
440
441       template<class>
442       friend class Ghost;
443
444       static Ghost* getSingletonPtr()
445       {
446         return getSingletonHolder().get();
447       }
448
449       /**
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.
454        */
455       template<member_func_t func>
456       struct MemberFunctionWrapper
457       {
458         /**
459          * Called from Nasal upon invocation of the according registered
460          * function. Forwards the call to the passed object instance.
461          */
462         static naRef call(naContext c, naRef me, int argc, naRef* args)
463         {
464           if( naGhost_type(me) != &getSingletonPtr()->_ghost_type )
465             naRuntimeError
466             (
467               c,
468               "method called on object of wrong type: '%s' expected",
469               getSingletonPtr()->_ghost_type.name
470             );
471
472           raw_type* obj = Ghost::getRawPtr( static_cast<T*>(naGhost_ptr(me)) );
473           assert(obj);
474
475           return (obj->*func)(argc, args);
476         }
477       };
478
479       typedef std::auto_ptr<Ghost> GhostPtr;
480       MemberMap _members;
481
482       explicit Ghost(const std::string& name):
483         GhostMetadata( name )
484       {
485         _ghost_type.destroy = &destroyGhost;
486         _ghost_type.name = _name.c_str();
487         _ghost_type.get_member = &getMember;
488         _ghost_type.set_member = &setMember;
489       }
490
491       static GhostPtr& getSingletonHolder()
492       {
493         static GhostPtr instance;
494         return instance;
495       }
496
497       static naRef makeGhost(naContext c, void *ptr)
498       {
499         return naNewGhost2(c, &getSingletonPtr()->_ghost_type, ptr);
500       }
501
502       static void destroyGhost(void *ptr)
503       {
504         delete (T*)ptr;
505       }
506
507       /**
508        * Callback for retrieving a ghost member.
509        */
510       static const char* getMember(naContext c, void* g, naRef key, naRef* out)
511       {
512         const std::string key_str = nasal::from_nasal<std::string>(c, key);
513         if( key_str == "parents" )
514         {
515           if( getSingletonPtr()->_parents.empty() )
516             return 0;
517
518           *out = getSingletonPtr()->getParents(c);
519           return "";
520         }
521
522         typename MemberMap::iterator member =
523           getSingletonPtr()->_members.find(key_str);
524
525         if( member == getSingletonPtr()->_members.end() )
526           return 0;
527
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));
532         else
533           return "Read-protected member";
534
535         return "";
536       }
537
538       /**
539        * Callback for writing to a ghost member.
540        */
541       static void setMember(naContext c, void* g, naRef field, naRef val)
542       {
543         const std::string key = nasal::from_nasal<std::string>(c, field);
544         typename MemberMap::iterator member =
545           getSingletonPtr()->_members.find(key);
546
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());
551
552         member->second.setter(c, Ghost::getRawPtr(g), val);
553       }
554   };
555
556 } // namespace nasal
557
558 #endif /* SG_NASAL_GHOST_HXX_ */