]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/cppbind/Ghost.hxx
First working version of DOM like Canvas event handling
[simgear.git] / simgear / nasal / cppbind / Ghost.hxx
1 ///@file
2 /// Expose C++ objects to Nasal as ghosts
3 ///
4 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Library General Public
8 // License as published by the Free Software Foundation; either
9 // version 2 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Library General Public License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
19
20 #ifndef SG_NASAL_GHOST_HXX_
21 #define SG_NASAL_GHOST_HXX_
22
23 #include "from_nasal.hxx"
24 #include "to_nasal.hxx"
25
26 #include <simgear/debug/logstream.hxx>
27
28 #include <boost/bind.hpp>
29 #include <boost/call_traits.hpp>
30 #include <boost/function.hpp>
31 #include <boost/lambda/lambda.hpp>
32 #include <boost/utility/enable_if.hpp>
33
34 #include <map>
35
36 /**
37  * Bindings between C++ and the Nasal scripting language
38  */
39 namespace nasal
40 {
41
42   /**
43    * Traits for C++ classes exposed as Ghost. This is the basic template for
44    * raw types.
45    */
46   template<class T>
47   struct GhostTypeTraits
48   {
49     /** Whether the class is passed by shared pointer or raw pointer */
50     typedef boost::false_type::type is_shared;
51
52     /** The raw class type (Without any shared pointer) */
53     typedef T raw_type;
54   };
55
56   template<class T>
57   struct GhostTypeTraits<boost::shared_ptr<T> >
58   {
59     typedef boost::true_type::type is_shared;
60     typedef T raw_type;
61   };
62
63 #ifdef OSG_REF_PTR
64   template<class T>
65   struct GhostTypeTraits<osg::ref_ptr<T> >
66   {
67     typedef boost::true_type::type is_shared;
68     typedef T raw_type;
69   };
70 #endif
71
72 #ifdef SGSharedPtr_HXX
73   template<class T>
74   struct GhostTypeTraits<SGSharedPtr<T> >
75   {
76     typedef boost::true_type::type is_shared;
77     typedef T raw_type;
78   };
79 #endif
80
81   /**
82    * Policy for creating ghost instances from shared pointer objects.
83    */
84   template<class T>
85   struct SharedPointerPolicy
86   {
87     typedef typename GhostTypeTraits<T>::raw_type   raw_type;
88     typedef T                                       pointer;
89     typedef boost::false_type                       returns_dynamic_type;
90
91     /**
92      * Create a shared pointer on the heap to handle the reference counting for
93      * the passed shared pointer while it is used in Nasal space.
94      */
95     static T* createInstance(const T& ptr)
96     {
97       return new T(ptr);
98     }
99
100     static pointer getPtr(void* ptr)
101     {
102       if( ptr )
103         return *static_cast<T*>(ptr);
104       else
105         return pointer();
106     }
107
108     static raw_type* getRawPtr(void* ptr)
109     {
110       if( ptr )
111         return static_cast<T*>(ptr)->get();
112       else
113         return 0;
114     }
115
116     static raw_type* getRawPtr(const T& ptr)
117     {
118       return ptr.get();
119     }
120   };
121
122   /**
123    * Policy for creating ghost instances as raw objects on the heap.
124    */
125   template<class T>
126   struct RawPointerPolicy
127   {
128     typedef typename GhostTypeTraits<T>::raw_type   raw_type;
129     typedef raw_type*                               pointer;
130     typedef boost::true_type                        returns_dynamic_type;
131
132     /**
133      * Create a new object instance on the heap
134      */
135     static T* createInstance()
136     {
137       return new T();
138     }
139
140     static pointer getPtr(void* ptr)
141     {
142       BOOST_STATIC_ASSERT((boost::is_same<pointer, T*>::value));
143       return static_cast<T*>(ptr);
144     }
145
146     static raw_type* getRawPtr(void* ptr)
147     {
148       BOOST_STATIC_ASSERT((boost::is_same<raw_type, T>::value));
149       return static_cast<T*>(ptr);
150     }
151   };
152
153   namespace internal
154   {
155     /**
156      * Metadata for Ghost object types
157      */
158     class GhostMetadata
159     {
160       public:
161         /**
162          * Add a nasal base class to the ghost. Will be available in the ghosts
163          * parents array.
164          */
165         void addNasalBase(const naRef& parent)
166         {
167           assert( naIsHash(parent) );
168           _parents.push_back(parent);
169         }
170
171         bool isBaseOf(naGhostType* ghost_type) const
172         {
173           if( ghost_type == &_ghost_type )
174             return true;
175
176           for( DerivedList::const_iterator derived = _derived_classes.begin();
177                                            derived != _derived_classes.end();
178                                          ++derived )
179           {
180             if( (*derived)->isBaseOf(ghost_type) )
181               return true;
182           }
183
184           return false;
185         }
186
187       protected:
188
189         typedef std::vector<const GhostMetadata*> DerivedList;
190
191         const std::string   _name;
192         naGhostType         _ghost_type;
193         DerivedList         _derived_classes;
194         std::vector<naRef>  _parents;
195
196         explicit GhostMetadata(const std::string& name):
197           _name(name)
198         {
199
200         }
201
202         void addDerived(const GhostMetadata* derived)
203         {
204           assert(derived);
205           _derived_classes.push_back(derived);
206
207           SG_LOG
208           (
209             SG_NASAL,
210             SG_INFO,
211             "Ghost::addDerived: " <<_ghost_type.name << " -> " << derived->_name
212           );
213         }
214
215         naRef getParents(naContext c)
216         {
217           return nasal::to_nasal(c, _parents);
218         }
219     };
220   }
221
222   /**
223    * Context passed to a function/method being called from Nasal
224    */
225   struct CallContext
226   {
227     CallContext(naContext c, size_t argc, naRef* args):
228       c(c),
229       argc(argc),
230       args(args)
231     {}
232
233     /**
234      * Get the argument with given index if it exists. Otherwise returns the
235      * passed default value.
236      *
237      * @tparam T    Type of argument (converted using ::from_nasal)
238      * @param index Index of requested argument
239      * @param def   Default value returned if too few arguments available
240      */
241     template<class T>
242     T getArg(size_t index, const T& def = T()) const
243     {
244       if( index >= argc )
245         return def;
246
247       return from_nasal<T>(c, args[index]);
248     }
249
250     /**
251      * Get the argument with given index. Raises a Nasal runtime error if there
252      * are to few arguments available.
253      */
254     template<class T>
255     T requireArg(size_t index) const
256     {
257       if( index >= argc )
258         naRuntimeError(c, "Missing required arg #%d", index);
259
260       return from_nasal<T>(c, args[index]);
261     }
262
263     naContext   c;
264     size_t      argc;
265     naRef      *args;
266   };
267
268   /**
269    * Class for exposing C++ objects to Nasal
270    *
271    * @code{cpp}
272    * // Example class to be exposed to Nasal
273    * class MyClass
274    * {
275    *   public:
276    *     void setX(int x);
277    *     int getX() const;
278    *
279    *     naRef myMember(naContext c, int argc, naRef* args);
280    * }
281    *
282    * void exposeClasses()
283    * {
284    *   // Register a nasal ghost type for MyClass. This needs to be done only
285    *   // once before creating the first ghost instance.
286    *   Ghost<MyClass>::init("MyClass")
287    *     // Members can be exposed by getters and setters
288    *     .member("x", &MyClass::getX, &MyClass::setX)
289    *     // For readonly variables only pass a getter
290    *     .member("x_readonly", &MyClass::getX)
291    *     // It is also possible to expose writeonly members
292    *     .member("x_writeonly", &MyClass::setX)
293    *     // Methods use a slightly different syntax - The pointer to the member
294    *     // function has to be passed as template argument
295    *     .method<&MyClass::myMember>("myMember");
296    * }
297    * @endcode
298    */
299   template<class T>
300   class Ghost:
301     public internal::GhostMetadata,
302     protected boost::mpl::if_< typename GhostTypeTraits<T>::is_shared,
303                                SharedPointerPolicy<T>,
304                                RawPointerPolicy<T> >::type
305   {
306     public:
307       typedef T                                                   value_type;
308       typedef typename GhostTypeTraits<T>::raw_type               raw_type;
309       typedef typename Ghost::pointer                             pointer;
310       typedef naRef (raw_type::*member_func_t)(const CallContext&);
311       typedef naRef (*free_func_t)(raw_type&, const CallContext&);
312       typedef boost::function<naRef(naContext, raw_type&)>        getter_t;
313       typedef boost::function<void(naContext, raw_type&, naRef)>  setter_t;
314
315       /**
316        * A ghost member. Can consist either of getter and/or setter functions
317        * for exposing a data variable or a single callable function.
318        */
319       struct member_t
320       {
321         member_t():
322           func(0)
323         {}
324
325         member_t( const getter_t& getter,
326                   const setter_t& setter,
327                   naCFunction func = 0 ):
328           getter( getter ),
329           setter( setter ),
330           func( func )
331         {}
332
333         member_t(naCFunction func):
334           func( func )
335         {}
336
337         getter_t    getter;
338         setter_t    setter;
339         naCFunction func;
340       };
341
342       typedef std::map<std::string, member_t> MemberMap;
343
344       /**
345        * Register a new ghost type.
346        *
347        * @note Only intialize each ghost type once!
348        *
349        * @param name    Descriptive name of the ghost type.
350        */
351       static Ghost& init(const std::string& name)
352       {
353         assert( !getSingletonPtr() );
354
355         getSingletonHolder().reset( new Ghost(name) );
356         return *getSingletonPtr();
357       }
358
359       /**
360        * Register a base class for this ghost. The base class needs to be
361        * registers on its own before it can be used as base class.
362        *
363        * @tparam BaseGhost  Type of base class already wrapped into Ghost class
364        *                    (Ghost<Base>)
365        *
366        * @code{cpp}
367        * Ghost<MyBase>::init("MyBase");
368        * Ghost<MyClass>::init("MyClass")
369        *   .bases<Ghost<MyBase> >();
370        * @endcode
371        */
372       template<class BaseGhost>
373       typename boost::enable_if_c
374         <    boost::is_base_of<GhostMetadata, BaseGhost>::value
375           && boost::is_base_of<typename BaseGhost::raw_type, raw_type>::value,
376           Ghost
377         >::type&
378       bases()
379       {
380         BaseGhost* base = BaseGhost::getSingletonPtr();
381         base->addDerived
382         (
383           this,
384           // Both ways of retrieving the address of a static member function
385           // should be legal but not all compilers know this.
386           // g++-4.4.7+ has been tested to work with both versions
387 #if defined(GCC_VERSION) && GCC_VERSION < 40407
388           // The old version of g++ used on Jenkins (16.11.2012) only compiles
389           // this version.
390           &getTypeFor<BaseGhost>
391 #else
392           // VS (2008, 2010, ... ?) only allow this version.
393           &Ghost::getTypeFor<BaseGhost>
394 #endif
395         );
396
397         // Replace any getter that is not available in the current class.
398         // TODO check if this is the correct behavior of function overriding
399         for( typename BaseGhost::MemberMap::const_iterator member =
400                base->_members.begin();
401                member != base->_members.end();
402              ++member )
403         {
404           if( _members.find(member->first) == _members.end() )
405             _members[member->first] = member_t
406             (
407               member->second.getter,
408               member->second.setter,
409               member->second.func
410             );
411         }
412
413         return *this;
414       }
415
416       /**
417        * Register a base class for this ghost. The base class needs to be
418        * registers on its own before it can be used as base class.
419        *
420        * @tparam Base   Type of base class (Base as used in Ghost<Base>)
421        *
422        * @code{cpp}
423        * Ghost<MyBase>::init("MyBase");
424        * Ghost<MyClass>::init("MyClass")
425        *   .bases<MyBase>();
426        * @endcode
427        */
428       template<class Base>
429       typename boost::enable_if_c
430         <   !boost::is_base_of<GhostMetadata, Base>::value
431           && boost::is_base_of<typename Ghost<Base>::raw_type, raw_type>::value,
432           Ghost
433         >::type&
434       bases()
435       {
436         return bases< Ghost<Base> >();
437       }
438
439       /**
440        * Register an existing Nasal class/hash as base class for this ghost.
441        *
442        * @param parent  Nasal hash/class
443        */
444       Ghost& bases(const naRef& parent)
445       {
446         addNasalBase(parent);
447         return *this;
448       }
449
450       /**
451        * Register a member variable by passing a getter and/or setter method.
452        *
453        * @param field   Name of member
454        * @param getter  Getter for variable
455        * @param setter  Setter for variable (Pass 0 to prevent write access)
456        *
457        */
458       template<class Var>
459       Ghost& member( const std::string& field,
460                      Var (raw_type::*getter)() const,
461                      void (raw_type::*setter)(typename boost::call_traits<Var>::param_type) = 0 )
462       {
463         member_t m;
464         if( getter )
465         {
466           typedef typename boost::call_traits<Var>::param_type param_type;
467           naRef (*to_nasal_)(naContext, param_type) = &nasal::to_nasal;
468
469           // Getter signature: naRef(naContext, raw_type&)
470           m.getter = boost::bind(to_nasal_, _1, boost::bind(getter, _2));
471         }
472
473         if( setter )
474         {
475           Var (*from_nasal_)(naContext, naRef) = &nasal::from_nasal;
476
477           // Setter signature: void(naContext, raw_type&, naRef)
478           m.setter = boost::bind(setter, _2, boost::bind(from_nasal_, _1, _3));
479         }
480
481         return member(field, m.getter, m.setter);
482       }
483
484       /**
485        * Register a write only member variable.
486        *
487        * @param field   Name of member
488        * @param setter  Setter for variable
489        */
490       template<class Var>
491       Ghost& member( const std::string& field,
492                      void (raw_type::*setter)(Var) )
493       {
494         return member<Var>(field, 0, setter);
495       }
496
497       /**
498        * Register a member variable by passing a getter and/or setter method.
499        *
500        * @param field   Name of member
501        * @param getter  Getter for variable
502        * @param setter  Setter for variable (Pass empty to prevent write access)
503        *
504        */
505       Ghost& member( const std::string& field,
506                      const getter_t& getter,
507                      const setter_t& setter = setter_t() )
508       {
509         if( !getter.empty() || !setter.empty() )
510           _members[field] = member_t(getter, setter);
511         else
512           SG_LOG
513           (
514             SG_NASAL,
515             SG_WARN,
516             "Member '" << field << "' requires a getter or setter"
517           );
518         return *this;
519       }
520
521       /**
522        * Register a member function.
523        *
524        * @note Because only function pointers can be registered as Nasal
525        *       functions it is needed to pass the function pointer as template
526        *       argument. This allows us to create a separate instance of the
527        *       MemberFunctionWrapper for each registered function and therefore
528        *       provides us with the needed static functions to be passed on to
529        *       Nasal.
530        *
531        * @tparam func   Pointer to member function being registered.
532        *
533        * @code{cpp}
534        * class MyClass
535        * {
536        *   public:
537        *     naRef myMethod(naContext c, int argc, naRef* args);
538        * }
539        *
540        * Ghost<MyClass>::init("Test")
541        *   .method<&MyClass::myMethod>("myMethod");
542        * @endcode
543        */
544       template<member_func_t func>
545       Ghost& method(const std::string& name)
546       {
547         _members[name].func = &MemberFunctionWrapper<func>::call;
548         return *this;
549       }
550
551       /**
552        * Register a free function as member function. The object instance is
553        * passed as additional first argument.
554        *
555        * @tparam func   Pointer to free function being registered.
556        *
557        * @note Due to a severe bug in Visual Studio it is not possible to create
558        *       a specialization of #method for free function pointers and
559        *       member function pointers at the same time. Do overcome this
560        *       limitation we had to use a different name for this function.
561        *
562        * @code{cpp}
563        * class MyClass;
564        * naRef myMethod(MyClass& obj, naContext c, int argc, naRef* args);
565        *
566        * Ghost<MyClass>::init("Test")
567        *   .method_func<&myMethod>("myMethod");
568        * @endcode
569        */
570       template<free_func_t func>
571       Ghost& method_func(const std::string& name)
572       {
573         _members[name].func = &FreeFunctionWrapper<func>::call;
574         return *this;
575       }
576
577       // TODO use variadic template when supporting C++11
578       /**
579        * Create a Nasal instance of this ghost.
580        *
581        * @param c   Active Nasal context
582        */
583       static naRef create( naContext c )
584       {
585         return makeGhost(c, Ghost::createInstance());
586       }
587
588       /**
589        * Create a Nasal instance of this ghost.
590        *
591        * @param c   Active Nasal context
592        * @param a1  Parameter used for creating new instance
593        */
594       template<class A1>
595       static naRef create( naContext c, const A1& a1 )
596       {
597         return makeGhost(c, Ghost::createInstance(a1));
598       }
599
600       /**
601        * Nasal callback for creating a new instance of this ghost.
602        */
603       static naRef f_create(naContext c, naRef me, int argc, naRef* args)
604       {
605         return create(c);
606       }
607
608       static bool isBaseOf(naGhostType* ghost_type)
609       {
610         if( !ghost_type )
611           return false;
612
613         return getSingletonPtr()->GhostMetadata::isBaseOf(ghost_type);
614       }
615
616       static bool isBaseOf(naRef obj)
617       {
618         return isBaseOf( naGhost_type(obj) );
619       }
620
621       /**
622        * Convert Nasal object to C++ object. To get a valid object the passed
623        * Nasal objects has to be derived class of the target class (Either
624        * derived in C++ or in Nasal using a 'parents' vector)
625        */
626       static pointer fromNasal(naContext c, naRef me)
627       {
628         // Check if it's a ghost and if it can be converted
629         if( isBaseOf( naGhost_type(me) ) )
630           return Ghost::getPtr( naGhost_ptr(me) );
631
632         // Now if it is derived from a ghost (hash with ghost in parent vector)
633         else if( naIsHash(me) )
634         {
635           naRef na_parents = naHash_cget(me, const_cast<char*>("parents"));
636           if( !naIsVector(na_parents) )
637           {
638             SG_LOG(SG_NASAL, SG_DEBUG, "Missing 'parents' vector for ghost");
639             return pointer();
640           }
641
642           typedef std::vector<naRef> naRefs;
643           naRefs parents = from_nasal<naRefs>(c, na_parents);
644           for( naRefs::const_iterator parent = parents.begin();
645                                       parent != parents.end();
646                                     ++parent )
647           {
648             pointer ptr = fromNasal(c, *parent);
649             if( ptr )
650               return ptr;
651           }
652         }
653
654         return pointer();
655       }
656
657     private:
658
659       template<class>
660       friend class Ghost;
661
662       typedef naGhostType* (*type_checker_t)(const raw_type*);
663       typedef std::vector<type_checker_t> DerivedList;
664       DerivedList _derived_types;
665
666       void addDerived( const internal::GhostMetadata* derived_meta,
667                        const type_checker_t& derived_info )
668       {
669         GhostMetadata::addDerived(derived_meta);
670         _derived_types.push_back(derived_info);
671       }
672
673       template<class BaseGhost>
674       static
675       typename boost::enable_if
676         < boost::is_polymorphic<typename BaseGhost::raw_type>,
677           naGhostType*
678         >::type
679       getTypeFor(const typename BaseGhost::raw_type* base)
680       {
681         // Check first if passed pointer can by converted to instance of class
682         // this ghost wraps.
683         if(   !boost::is_same
684                  < typename BaseGhost::raw_type,
685                    typename Ghost::raw_type
686                  >::value
687             && dynamic_cast<const typename Ghost::raw_type*>(base) != base )
688           return 0;
689
690         // Now check if we can further downcast to one of our derived classes.
691         for( typename DerivedList::reverse_iterator
692                derived = getSingletonPtr()->_derived_types.rbegin();
693                derived != getSingletonPtr()->_derived_types.rend();
694              ++derived )
695         {
696           naGhostType* ghost_type =
697             (*derived)( static_cast<const typename Ghost::raw_type*>(base) );
698           if( ghost_type )
699             return ghost_type;
700         }
701
702         // If base is not an instance of any derived class, this class has to
703         // be the dynamic type.
704         return &getSingletonPtr()->_ghost_type;
705       }
706
707       template<class BaseGhost>
708       static
709       typename boost::disable_if
710         < boost::is_polymorphic<typename BaseGhost::raw_type>,
711           naGhostType*
712         >::type
713       getTypeFor(const typename BaseGhost::raw_type* base)
714       {
715         // For non polymorphic classes there is no possibility to get the actual
716         // dynamic type, therefore we can only use its static type.
717         return &BaseGhost::getSingletonPtr()->_ghost_type;
718       }
719
720       static Ghost* getSingletonPtr()
721       {
722         return getSingletonHolder().get();
723       }
724
725       static raw_type& requireObject(naContext c, naRef me)
726       {
727         raw_type* obj = Ghost::getRawPtr( fromNasal(c, me) );
728         naGhostType* ghost_type = naGhost_type(me);
729
730         if( !obj )
731           naRuntimeError
732           (
733             c,
734             "method called on object of wrong type: is '%s' expected '%s'",
735             ghost_type ? ghost_type->name : "unknown",
736             getSingletonPtr()->_ghost_type.name
737           );
738
739         return *obj;
740       }
741
742       /**
743        * Wrapper class to enable registering pointers to member functions as
744        * Nasal function callbacks. We need to use the function pointer as
745        * template parameter to ensure every registered function gets a static
746        * function which can be passed to Nasal.
747        */
748       template<member_func_t func>
749       struct MemberFunctionWrapper
750       {
751         /**
752          * Called from Nasal upon invocation of the according registered
753          * function. Forwards the call to the passed object instance.
754          */
755         static naRef call(naContext c, naRef me, int argc, naRef* args)
756         {
757           return (requireObject(c, me).*func)(CallContext(c, argc, args));
758         }
759       };
760
761       /**
762        * Wrapper class to enable registering pointers to free functions (only
763        * external linkage). We need to use the function pointer as template
764        * parameter to ensure every registered function gets a static function
765        * which can be passed to Nasal. Even though we just wrap another simple
766        * function pointer this intermediate step is need to be able to retrieve
767        * the object the function call belongs to and pass it along as argument.
768        */
769       template<free_func_t func>
770       struct FreeFunctionWrapper
771       {
772         /**
773          * Called from Nasal upon invocation of the according registered
774          * function. Forwards the call to the passed function pointer and passes
775          * the required parameters.
776          */
777         static naRef call(naContext c, naRef me, int argc, naRef* args)
778         {
779           return func(requireObject(c, me), CallContext(c, argc, args));
780         }
781       };
782
783       typedef std::auto_ptr<Ghost> GhostPtr;
784       MemberMap _members;
785
786       explicit Ghost(const std::string& name):
787         GhostMetadata( name )
788       {
789         _ghost_type.destroy = &destroyGhost;
790         _ghost_type.name = _name.c_str();
791         _ghost_type.get_member = &getMember;
792         _ghost_type.set_member = &setMember;
793       }
794
795       static GhostPtr& getSingletonHolder()
796       {
797         static GhostPtr instance;
798         return instance;
799       }
800
801       static naRef makeGhost(naContext c, void *ptr)
802       {
803         if( !Ghost::getRawPtr(ptr) )
804           return naNil();
805
806         naGhostType* ghost_type = 0;
807         if( Ghost::returns_dynamic_type::value )
808           // For pointer policies already returning instances of an object with
809           // the dynamic type of this Ghost's raw_type the type is always the
810           // same.
811           ghost_type = &getSingletonPtr()->_ghost_type;
812         else
813           // If wrapping eg. shared pointers the users passes an already
814           // existing instance of an object which will then be hold be a new
815           // shared pointer. We therefore have to check for the dynamic type
816           // of the object as it might differ from the passed static type.
817           ghost_type = getTypeFor<Ghost>( Ghost::getRawPtr(ptr) );
818
819         if( !ghost_type )
820           return naNil();
821
822         return naNewGhost2(c, ghost_type, ptr);
823       }
824
825       static void destroyGhost(void *ptr)
826       {
827         delete (T*)ptr;
828       }
829
830       /**
831        * Callback for retrieving a ghost member.
832        */
833       static const char* getMember(naContext c, void* g, naRef key, naRef* out)
834       {
835         const std::string key_str = nasal::from_nasal<std::string>(c, key);
836         if( key_str == "parents" )
837         {
838           if( getSingletonPtr()->_parents.empty() )
839             return 0;
840
841           *out = getSingletonPtr()->getParents(c);
842           return "";
843         }
844
845         typename MemberMap::iterator member =
846           getSingletonPtr()->_members.find(key_str);
847
848         if( member == getSingletonPtr()->_members.end() )
849           return 0;
850
851         if( member->second.func )
852           *out = nasal::to_nasal(c, member->second.func);
853         else if( !member->second.getter.empty() )
854           *out = member->second.getter(c, *Ghost::getRawPtr(g));
855         else
856           return "Read-protected member";
857
858         return "";
859       }
860
861       /**
862        * Callback for writing to a ghost member.
863        */
864       static void setMember(naContext c, void* g, naRef field, naRef val)
865       {
866         const std::string key = nasal::from_nasal<std::string>(c, field);
867         typename MemberMap::iterator member =
868           getSingletonPtr()->_members.find(key);
869
870         if( member == getSingletonPtr()->_members.end() )
871           naRuntimeError(c, "ghost: No such member: %s", key.c_str());
872         if( member->second.setter.empty() )
873           naRuntimeError(c, "ghost: Write protected member: %s", key.c_str());
874
875         member->second.setter(c, *Ghost::getRawPtr(g), val);
876       }
877   };
878
879 } // namespace nasal
880
881 #endif /* SG_NASAL_GHOST_HXX_ */