]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/cppbind/Ghost.hxx
cppbind.Ghost: clean up a bit
[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 "NasalCallContext.hxx"
24 #include "NasalObjectHolder.hxx"
25
26 #include <simgear/debug/logstream.hxx>
27 #include <simgear/structure/SGWeakReferenced.hxx>
28 #include <simgear/structure/SGWeakPtr.hxx>
29
30 #include <boost/bind.hpp>
31 #include <boost/call_traits.hpp>
32 #include <boost/function.hpp>
33 #include <boost/lambda/lambda.hpp>
34 #include <boost/mpl/has_xxx.hpp>
35 #include <boost/preprocessor/iteration/iterate.hpp>
36 #include <boost/shared_ptr.hpp>
37 #include <boost/utility/enable_if.hpp>
38
39 #include <map>
40
41 template<class T>
42 inline T* get_pointer(boost::weak_ptr<T> const& p)
43 {
44   return p.lock().get();
45 }
46
47 template<class T>
48 inline T* get_pointer(SGWeakPtr<T> const& p)
49 {
50   return p.lock().get();
51 }
52
53 /**
54  * Bindings between C++ and the Nasal scripting language
55  */
56 namespace nasal
57 {
58
59   namespace internal
60   {
61     /**
62      * Metadata for Ghost object types
63      */
64     class GhostMetadata
65     {
66       public:
67         /**
68          * Add a nasal base class to the ghost. Will be available in the ghosts
69          * parents array.
70          */
71         void addNasalBase(const naRef& parent)
72         {
73           assert( naIsHash(parent) );
74           _parents.push_back(parent);
75         }
76
77         bool isBaseOf(naGhostType* ghost_type) const
78         {
79           if( ghost_type == _ghost_type_ptr )
80             return true;
81
82           for( DerivedList::const_iterator derived = _derived_classes.begin();
83                                            derived != _derived_classes.end();
84                                          ++derived )
85           {
86             if( (*derived)->isBaseOf(ghost_type) )
87               return true;
88           }
89
90           return false;
91         }
92
93       protected:
94
95         typedef std::vector<const GhostMetadata*> DerivedList;
96
97         const std::string   _name;
98         const naGhostType  *_ghost_type_ptr;
99         DerivedList         _derived_classes;
100         std::vector<naRef>  _parents;
101
102         GhostMetadata( const std::string& name,
103                        const naGhostType* ghost_type ):
104           _name(name),
105           _ghost_type_ptr(ghost_type)
106         {
107
108         }
109
110         void addDerived(const GhostMetadata* derived)
111         {
112           assert(derived);
113           _derived_classes.push_back(derived);
114
115           SG_LOG
116           (
117             SG_NASAL,
118             SG_INFO,
119             "Ghost::addDerived: " << _name << " -> " << derived->_name
120           );
121         }
122
123         naRef getParents(naContext c)
124         {
125           return nasal::to_nasal(c, _parents);
126         }
127     };
128
129     /**
130      * Hold callable method and convert to Nasal function if required.
131      */
132     class MethodHolder:
133       public SGWeakReferenced
134     {
135       public:
136         virtual ~MethodHolder() {}
137
138         naRef get_naRef(naContext c)
139         {
140           if( !_obj.valid() )
141             _obj.reset(createNasalObject(c));
142           return _obj.get_naRef();
143         }
144
145       protected:
146         ObjectHolder<> _obj;
147
148         virtual naRef createNasalObject(naContext c) = 0;
149     };
150
151     BOOST_MPL_HAS_XXX_TRAIT_DEF(element_type)
152
153     template<class T>
154     struct reduced_type
155     {
156       typedef typename boost::remove_cv<
157         typename boost::remove_reference<T>::type
158       >::type type;
159     };
160
161     template<class T1, class T2>
162     struct reduced_is_same:
163       public boost::is_same<typename reduced_type<T1>::type, T2>
164     {};
165   }
166
167   typedef SGSharedPtr<internal::MethodHolder> MethodHolderPtr;
168   typedef SGWeakPtr<internal::MethodHolder> MethodHolderWeakPtr;
169
170   /**
171    * Class for exposing C++ objects to Nasal
172    *
173    * @code{cpp}
174    * // Example class to be exposed to Nasal
175    * class MyClass
176    * {
177    *   public:
178    *     void setX(int x);
179    *     int getX() const;
180    *
181    *     int myMember();
182    *     void doSomethingElse(const nasal::CallContext& ctx);
183    * }
184    * typedef boost::shared_ptr<MyClass> MyClassPtr;
185    *
186    * std::string myOtherFreeMember(int num);
187    *
188    * void exposeClasses()
189    * {
190    *   // Register a nasal ghost type for MyClass. This needs to be done only
191    *   // once before creating the first ghost instance. The exposed class needs
192    *   // to be wrapped inside a shared pointer, eg. boost::shared_ptr.
193    *   Ghost<MyClassPtr>::init("MyClass")
194    *     // Members can be exposed by getters and setters
195    *     .member("x", &MyClass::getX, &MyClass::setX)
196    *     // For readonly variables only pass a getter
197    *     .member("x_readonly", &MyClass::getX)
198    *     // It is also possible to expose writeonly members
199    *     .member("x_writeonly", &MyClass::setX)
200    *     // Methods can be nearly anything callable and accepting a reference
201    *     // to an instance of the class type. (member functions, free functions
202    *     // and anything else bindable using boost::function and boost::bind)
203    *     .method("myMember", &MyClass::myMember)
204    *     .method("doSomething", &MyClass::doSomethingElse)
205    *     .method("other", &myOtherFreeMember);
206    * }
207    * @endcode
208    */
209   template<class T>
210   class Ghost:
211     public internal::GhostMetadata
212   {
213       BOOST_STATIC_ASSERT( internal::has_element_type<T>::value );
214
215     public:
216       typedef typename T::element_type                              raw_type;
217       typedef T                                                     pointer;
218       typedef naRef (raw_type::*member_func_t)(const CallContext&);
219       typedef naRef (*free_func_t)(raw_type&, const CallContext&);
220       typedef boost::function<naRef(raw_type&, naContext)>          getter_t;
221       typedef boost::function<void( raw_type&, naContext, naRef)>   setter_t;
222       typedef boost::function<naRef(raw_type&, const CallContext&)> method_t;
223       typedef boost::function<bool( raw_type&,
224                                     naContext,
225                                     const std::string&,
226                                     naRef& )>              fallback_getter_t;
227       typedef boost::function<bool( raw_type&,
228                                     naContext,
229                                     const std::string&,
230                                     naRef )>               fallback_setter_t;
231
232       class MethodHolder:
233         public internal::MethodHolder
234       {
235         public:
236           explicit MethodHolder(const method_t& method):
237             _method(method)
238           {}
239
240         protected:
241
242           typedef SGSharedPtr<MethodHolder> SharedPtr;
243           typedef SGWeakPtr<MethodHolder> WeakPtr;
244
245           method_t  _method;
246
247           virtual naRef createNasalObject(naContext c)
248           {
249             return naNewFunc
250             (
251               c,
252               naNewCCodeUD( c,
253                             &MethodHolder::call,
254                             new WeakPtr(this),
255                             &destroyHolder )
256             );
257           }
258
259           static void destroyHolder(void* user_data)
260           {
261             delete static_cast<WeakPtr*>(user_data);
262           }
263
264           static naRef call( naContext c,
265                              naRef me,
266                              int argc,
267                              naRef* args,
268                              void* user_data )
269           {
270             WeakPtr* holder_weak = static_cast<WeakPtr*>(user_data);
271             if( !holder_weak )
272               naRuntimeError(c, "invalid method holder!");
273
274             try
275             {
276               SharedPtr holder = holder_weak->lock();
277               if( !holder )
278                 throw std::runtime_error("holder has expired");
279
280               return holder->_method
281               (
282                 requireObject(c, me),
283                 CallContext(c, argc, args)
284               );
285             }
286             catch(const std::exception& ex)
287             {
288               naRuntimeError(c, "Fatal error in method call: %s", ex.what());
289             }
290             catch(...)
291             {
292               naRuntimeError(c, "Unknown exception in method call.");
293             }
294
295             return naNil();
296           }
297       };
298
299       /**
300        * A ghost member. Can consist either of getter and/or setter functions
301        * for exposing a data variable or a single callable function.
302        */
303       struct member_t
304       {
305         member_t()
306         {}
307
308         member_t( const getter_t& getter,
309                   const setter_t& setter,
310                   const MethodHolderPtr& func = MethodHolderPtr() ):
311           getter( getter ),
312           setter( setter ),
313           func( func )
314         {}
315
316         explicit member_t(const MethodHolderPtr& func):
317           func( func )
318         {}
319
320         getter_t        getter;
321         setter_t        setter;
322         MethodHolderPtr func;
323       };
324
325       typedef std::map<std::string, member_t> MemberMap;
326
327       /**
328        * Register a new ghost type.
329        *
330        * @note Only intialize each ghost type once!
331        *
332        * @param name    Descriptive name of the ghost type.
333        */
334       static Ghost& init(const std::string& name)
335       {
336         getSingletonHolder().reset( new Ghost(name) );
337         return *getSingletonPtr();
338       }
339
340       /**
341        * Check whether ghost type has already been initialized.
342        */
343       static bool isInit()
344       {
345         return getSingletonPtr();
346       }
347
348       /**
349        * Register a base class for this ghost. The base class needs to be
350        * registers on its own before it can be used as base class.
351        *
352        * @tparam BaseGhost  Type of base class already wrapped into Ghost class
353        *                    (Ghost<BasePtr>)
354        *
355        * @code{cpp}
356        * Ghost<MyBasePtr>::init("MyBase");
357        * Ghost<MyClassPtr>::init("MyClass")
358        *   .bases<Ghost<MyBasePtr> >();
359        * @endcode
360        */
361       template<class BaseGhost>
362       typename boost::enable_if
363         <
364           boost::is_base_of<GhostMetadata, BaseGhost>,
365           Ghost
366         >::type&
367       bases()
368       {
369         BOOST_STATIC_ASSERT
370         ((
371           boost::is_base_of<typename BaseGhost::raw_type, raw_type>::value
372         ));
373
374         BaseGhost* base = BaseGhost::getSingletonPtr();
375         base->addDerived
376         (
377           this,
378           // Both ways of retrieving the address of a static member function
379           // should be legal but not all compilers know this.
380           // g++-4.4.7+ has been tested to work with both versions
381 #if defined(SG_GCC_VERSION) && SG_GCC_VERSION < 40407
382           // The old version of g++ used on Jenkins (16.11.2012) only compiles
383           // this version.
384           &getTypeFor<BaseGhost>
385 #else
386           // VS (2008, 2010, ... ?) only allow this version.
387           &Ghost::getTypeFor<BaseGhost>
388 #endif
389         );
390
391         // Replace any getter that is not available in the current class.
392         // TODO check if this is the correct behavior of function overriding
393         for( typename BaseGhost::MemberMap::const_iterator member =
394                base->_members.begin();
395                member != base->_members.end();
396              ++member )
397         {
398           if( _members.find(member->first) == _members.end() )
399             _members[member->first] = member_t
400             (
401               member->second.getter,
402               member->second.setter,
403               member->second.func
404             );
405         }
406
407         if( !_fallback_setter )
408           _fallback_setter = base->_fallback_setter;
409         if( !_fallback_getter )
410           _fallback_getter = base->_fallback_getter;
411
412         return *this;
413       }
414
415       /**
416        * Register a base class for this ghost. The base class needs to be
417        * registers on its own before it can be used as base class.
418        *
419        * @tparam Base   Type of base class (Base as used in Ghost<BasePtr>)
420        *
421        * @code{cpp}
422        * Ghost<MyBasePtr>::init("MyBase");
423        * Ghost<MyClassPtr>::init("MyClass")
424        *   .bases<MyBasePtr>();
425        * @endcode
426        */
427       template<class Base>
428       typename boost::disable_if
429         <
430           boost::is_base_of<GhostMetadata, Base>,
431           Ghost
432         >::type&
433       bases()
434       {
435         BOOST_STATIC_ASSERT
436         ((
437           boost::is_base_of<typename Ghost<Base>::raw_type, raw_type>::value
438         ));
439
440         return bases< Ghost<Base> >();
441       }
442
443       /**
444        * Register an existing Nasal class/hash as base class for this ghost.
445        *
446        * @param parent  Nasal hash/class
447        */
448       Ghost& bases(const naRef& parent)
449       {
450         addNasalBase(parent);
451         return *this;
452       }
453
454       /**
455        * Register a member variable by passing a getter and/or setter method.
456        *
457        * @param field   Name of member
458        * @param getter  Getter for variable
459        * @param setter  Setter for variable (Pass 0 to prevent write access)
460        *
461        */
462       template<class Ret, class Param>
463       Ghost& member( const std::string& field,
464                      Ret (raw_type::*getter)() const,
465                      void (raw_type::*setter)(Param) )
466       {
467         return member(field, to_getter(getter), to_setter(setter));
468       }
469
470       /**
471        * Register a read only member variable.
472        *
473        * @param field   Name of member
474        * @param getter  Getter for variable
475        */
476       template<class Ret>
477       Ghost& member( const std::string& field,
478                      Ret (raw_type::*getter)() const )
479       {
480         return member(field, to_getter(getter), setter_t());
481       }
482
483       /**
484        * Register a write only member variable.
485        *
486        * @param field   Name of member
487        * @param setter  Setter for variable
488        */
489       template<class Var>
490       Ghost& member( const std::string& field,
491                      void (raw_type::*setter)(Var) )
492       {
493         return member(field, getter_t(), to_setter(setter));
494       }
495
496       /**
497        * Register a member variable by passing a getter and/or setter method.
498        *
499        * @param field   Name of member
500        * @param getter  Getter for variable
501        * @param setter  Setter for variable (Pass empty to prevent write access)
502        *
503        */
504       Ghost& member( const std::string& field,
505                      const getter_t& getter,
506                      const setter_t& setter = setter_t() )
507       {
508         if( !getter.empty() || !setter.empty() )
509           _members[field] = member_t(getter, setter);
510         else
511           SG_LOG
512           (
513             SG_NASAL,
514             SG_WARN,
515             "Member '" << field << "' requires a getter or setter"
516           );
517         return *this;
518       }
519
520       /**
521        * Register a function which is called upon retrieving an unknown member
522        * of this ghost.
523        */
524       Ghost& _get(const fallback_getter_t& getter)
525       {
526         _fallback_getter = getter;
527         return *this;
528       }
529
530       /**
531        * Register a function which is called upon retrieving an unknown member
532        * of this ghost, and convert it to the given @a Param type.
533        */
534       template<class Param>
535       Ghost& _get( const boost::function<bool ( raw_type&,
536                                                 const std::string&,
537                                                 Param& )>& getter )
538       {
539         return _get(boost::bind(
540           convert_param_invoker<Param>, getter, _1, _2, _3, _4
541         ));
542       }
543
544       /**
545        * Register a method which is called upon retrieving an unknown member of
546        * this ghost.
547        *
548        * @code{cpp}
549        * class MyClass
550        * {
551        *   public:
552        *     bool getMember( const std::string& key,
553        *                     std::string& value_out );
554        * }
555        *
556        * Ghost<MyClassPtr>::init("Test")
557        *   ._get(&MyClass::getMember);
558        * @endcode
559        */
560       template<class Param>
561       Ghost& _get(bool (raw_type::*getter)(const std::string&, Param&))
562       {
563         return _get(
564           boost::function<bool (raw_type&, const std::string&, Param&)>(getter)
565         );
566       }
567
568       /**
569        * Register a method which is called upon retrieving an unknown member of
570        * this ghost.
571        *
572        * @code{cpp}
573        * class MyClass
574        * {
575        *   public:
576        *     bool getMember( naContext c,
577        *                     const std::string& key,
578        *                     naRef& value_out );
579        * }
580        *
581        * Ghost<MyClassPtr>::init("Test")
582        *   ._get(&MyClass::getMember);
583        * @endcode
584        */
585       Ghost& _get(bool (raw_type::*getter)( naContext,
586                                             const std::string&,
587                                             naRef& ))
588       {
589         return _get( fallback_getter_t(getter) );
590       }
591
592       /**
593        * Register a function which is called upon setting an unknown member of
594        * this ghost.
595        */
596       Ghost& _set(const fallback_setter_t& setter)
597       {
598         _fallback_setter = setter;
599         return *this;
600       }
601
602       /**
603        * Register a function which is called upon setting an unknown member of
604        * this ghost, and convert it to the given @a Param type.
605        */
606       template<class Param>
607       Ghost& _set(const boost::function<bool ( raw_type&,
608                                                const std::string&,
609                                                Param )>& setter)
610       {
611         // Setter signature: bool( raw_type&,
612         //                         naContext,
613         //                         const std::string&,
614         //                         naRef )
615         return _set(boost::bind(
616           setter,
617           _1,
618           _3,
619           boost::bind(from_nasal_ptr<Param>::get(), _2, _4)
620         ));
621       }
622
623       /**
624        * Register a method which is called upon setting an unknown member of
625        * this ghost.
626        *
627        * @code{cpp}
628        * class MyClass
629        * {
630        *   public:
631        *     bool setMember( const std::string& key,
632        *                     const std::string& value );
633        * }
634        *
635        * Ghost<MyClassPtr>::init("Test")
636        *   ._set(&MyClass::setMember);
637        * @endcode
638        */
639       template<class Param>
640       Ghost& _set(bool (raw_type::*setter)(const std::string&, Param))
641       {
642         return _set(
643           boost::function<bool (raw_type&, const std::string&, Param)>(setter)
644         );
645       }
646
647       /**
648        * Register a method which is called upon setting an unknown member of
649        * this ghost.
650        *
651        * @code{cpp}
652        * class MyClass
653        * {
654        *   public:
655        *     bool setMember( naContext c,
656        *                     const std::string& key,
657        *                     naRef value );
658        * }
659        *
660        * Ghost<MyClassPtr>::init("Test")
661        *   ._set(&MyClass::setMember);
662        * @endcode
663        */
664       Ghost& _set(bool (raw_type::*setter)( naContext,
665                                             const std::string&,
666                                             naRef ))
667       {
668         return _set( fallback_setter_t(setter) );
669       }
670
671       /**
672        * Register anything that accepts an object instance and a
673        * nasal::CallContext and returns naRef as method.
674        *
675        * @code{cpp}
676        * class MyClass
677        * {
678        *   public:
679        *     naRef myMethod(const nasal::CallContext& ctx);
680        * }
681        *
682        * Ghost<MyClassPtr>::init("Test")
683        *   .method("myMethod", &MyClass::myMethod);
684        * @endcode
685        */
686       Ghost& method(const std::string& name, const method_t& func)
687       {
688         _members[name].func = new MethodHolder(func);
689         return *this;
690       }
691
692       /**
693        * Register anything that accepts an object instance and a
694        * nasal::CallContext whith automatic conversion of the return type to
695        * Nasal.
696        *
697        * @code{cpp}
698        * class MyClass;
699        * void doIt(const MyClass& c, const nasal::CallContext& ctx);
700        *
701        * Ghost<MyClassPtr>::init("Test")
702        *   .method("doIt", &doIt);
703        * @endcode
704        */
705       template<class Ret>
706       Ghost& method
707       (
708         const std::string& name,
709         const boost::function<Ret (raw_type&, const CallContext&)>& func
710       )
711       {
712         return method(name, boost::bind(method_invoker<Ret>, func, _1, _2));
713       }
714
715       // Build dependency for CMake, gcc, etc.
716 #define SG_DONT_DO_ANYTHING
717 # include <simgear/nasal/cppbind/detail/functor_templates.hxx>
718 #undef SG_DONT_DO_ANYTHING
719
720 #define BOOST_PP_ITERATION_LIMITS (0, 9)
721 #define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/functor_templates.hxx>
722 #include BOOST_PP_ITERATE()
723
724       // TODO use variadic template when supporting C++11
725       // TODO check if default constructor exists
726 //      static naRef create( naContext c )
727 //      {
728 //        return makeGhost(c, createInstance());
729 //      }
730
731       /**
732        * Create a Nasal instance of this ghost.
733        *
734        * @param c   Active Nasal context
735        * @param a1  Parameter used for creating new instance
736        */
737       template<class A1>
738       static naRef create( naContext c, const A1& a1 )
739       {
740         return makeGhost(c, createInstance(a1));
741       }
742
743       /**
744        * Nasal callback for creating a new instance of this ghost.
745        */
746       static naRef f_create(naContext c, naRef me, int argc, naRef* args)
747       {
748         return create(c);
749       }
750
751       static bool isBaseOf(naGhostType* ghost_type)
752       {
753         if( !ghost_type )
754           return false;
755
756         return getSingletonPtr()->GhostMetadata::isBaseOf(ghost_type);
757       }
758
759       static bool isBaseOf(naRef obj)
760       {
761         return isBaseOf( naGhost_type(obj) );
762       }
763
764       /**
765        * Convert Nasal object to C++ object. To get a valid object the passed
766        * Nasal objects has to be derived class of the target class (Either
767        * derived in C++ or in Nasal using a 'parents' vector)
768        */
769       static pointer fromNasal(naContext c, naRef me)
770       {
771         // Check if it's a ghost and if it can be converted
772         if( isBaseOf( naGhost_type(me) ) )
773           return getPtr( naGhost_ptr(me) );
774
775         // Now if it is derived from a ghost (hash with ghost in parent vector)
776         else if( naIsHash(me) )
777         {
778           naRef na_parents = naHash_cget(me, const_cast<char*>("parents"));
779           if( !naIsVector(na_parents) )
780             return pointer();
781
782           typedef std::vector<naRef> naRefs;
783           naRefs parents = from_nasal<naRefs>(c, na_parents);
784           for( naRefs::const_iterator parent = parents.begin();
785                                       parent != parents.end();
786                                     ++parent )
787           {
788             pointer ptr = fromNasal(c, *parent);
789             if( get_pointer(ptr) )
790               return ptr;
791           }
792         }
793
794         return pointer();
795       }
796
797     private:
798
799       template<class>
800       friend class Ghost;
801
802       static naGhostType _ghost_type;
803
804       typedef naGhostType* (*type_checker_t)(const raw_type*);
805       typedef std::vector<type_checker_t> DerivedList;
806       DerivedList _derived_types;
807
808       /**
809        * Create a shared pointer on the heap to handle the reference counting
810        * for the passed shared pointer while it is used in Nasal space.
811        */
812       static pointer* createInstance(const pointer& ptr)
813       {
814         return get_pointer(ptr) ? new pointer(ptr) : 0;
815       }
816
817       static pointer getPtr(void* ptr)
818       {
819         if( ptr )
820           return *static_cast<pointer*>(ptr);
821         else
822           return pointer();
823       }
824
825       static raw_type* getRawPtr(void* ptr)
826       {
827         if( ptr )
828           return get_pointer(*static_cast<pointer*>(ptr));
829         else
830           return 0;
831       }
832
833       static raw_type* getRawPtr(const pointer& ptr)
834       {
835         return get_pointer(ptr);
836       }
837
838       void addDerived( const internal::GhostMetadata* derived_meta,
839                        const type_checker_t& derived_info )
840       {
841         GhostMetadata::addDerived(derived_meta);
842         _derived_types.push_back(derived_info);
843       }
844
845       template<class BaseGhost>
846       static
847       typename boost::enable_if
848         < boost::is_polymorphic<typename BaseGhost::raw_type>,
849           naGhostType*
850         >::type
851       getTypeFor(const typename BaseGhost::raw_type* base)
852       {
853         // Check first if passed pointer can by converted to instance of class
854         // this ghost wraps.
855         if(   !boost::is_same
856                  < typename BaseGhost::raw_type,
857                    typename Ghost::raw_type
858                  >::value
859             && dynamic_cast<const typename Ghost::raw_type*>(base) != base )
860           return 0;
861
862         if( !getSingletonPtr() )
863         {
864           SG_LOG
865           (
866             SG_NASAL,
867             SG_INFO,
868             "Ghost::getTypeFor: can not get type for unregistered ghost"
869           );
870           return 0;
871         }
872
873         // Now check if we can further downcast to one of our derived classes.
874         for( typename DerivedList::reverse_iterator
875                derived = getSingletonPtr()->_derived_types.rbegin();
876                derived != getSingletonPtr()->_derived_types.rend();
877              ++derived )
878         {
879           naGhostType* ghost_type =
880             (*derived)( static_cast<const typename Ghost::raw_type*>(base) );
881           if( ghost_type )
882             return ghost_type;
883         }
884
885         // If base is not an instance of any derived class, this class has to
886         // be the dynamic type.
887         return &_ghost_type;
888       }
889
890       template<class BaseGhost>
891       static
892       typename boost::disable_if
893         < boost::is_polymorphic<typename BaseGhost::raw_type>,
894           naGhostType*
895         >::type
896       getTypeFor(const typename BaseGhost::raw_type* base)
897       {
898         // For non polymorphic classes there is no possibility to get the actual
899         // dynamic type, therefore we can only use its static type.
900         return &BaseGhost::_ghost_type;
901       }
902
903       static Ghost* getSingletonPtr()
904       {
905         return getSingletonHolder().get();
906       }
907
908       static raw_type& requireObject(naContext c, naRef me)
909       {
910         raw_type* obj = getRawPtr( fromNasal(c, me) );
911         naGhostType* ghost_type = naGhost_type(me);
912
913         if( !obj )
914           naRuntimeError
915           (
916             c,
917             "method called on object of wrong type: is '%s' expected '%s'",
918             naIsNil(me) ? "nil" : (ghost_type ? ghost_type->name : "unknown"),
919             _ghost_type.name
920           );
921
922         return *obj;
923       }
924
925       template<class Ret>
926       getter_t to_getter(Ret (raw_type::*getter)() const)
927       {
928         typedef typename boost::call_traits<Ret>::param_type param_type;
929         naRef(*to_nasal_)(naContext, param_type) = &to_nasal;
930
931         // Getter signature: naRef(raw_type&, naContext)
932         return boost::bind
933         (
934           to_nasal_,
935           _2,
936           boost::bind(getter, _1)
937         );
938       }
939
940       template<class Param>
941       setter_t to_setter(void (raw_type::*setter)(Param))
942       {
943         // Setter signature: void(raw_type&, naContext, naRef)
944         return boost::bind
945         (
946           setter,
947           _1,
948           boost::bind(from_nasal_ptr<Param>::get(), _2, _3)
949         );
950       }
951
952       /**
953        * Invoke a method which writes the converted parameter to a reference
954        */
955       template<class Param>
956       static
957       bool convert_param_invoker
958       (
959         const boost::function<bool ( raw_type&,
960                                      const std::string&,
961                                      Param& )>& func,
962         raw_type& obj,
963         naContext c,
964         const std::string& key,
965         naRef& out
966       )
967       {
968         Param p;
969         if( !func(obj, key, p) )
970           return false;
971
972         out = to_nasal(c, p);
973         return true;
974       };
975
976       /**
977        * Invoke a method which returns a value and convert it to Nasal.
978        */
979       template<class Ret>
980       static
981       typename boost::disable_if<boost::is_void<Ret>, naRef>::type
982       method_invoker
983       (
984         const boost::function<Ret (raw_type&, const CallContext&)>& func,
985         raw_type& obj,
986         const CallContext& ctx
987       )
988       {
989         return (*to_nasal_ptr<Ret>::get())(ctx.c, func(obj, ctx));
990       };
991
992       /**
993        * Invoke a method which returns void and "convert" it to nil.
994        */
995       template<class Ret>
996       static
997       typename boost::enable_if<boost::is_void<Ret>, naRef>::type
998       method_invoker
999       (
1000         const boost::function<Ret (raw_type&, const CallContext&)>& func,
1001         raw_type& obj,
1002         const CallContext& ctx
1003       )
1004       {
1005         func(obj, ctx);
1006         return naNil();
1007       };
1008
1009       /**
1010        * Extract argument by index from nasal::CallContext and convert to given
1011        * type.
1012        */
1013       template<class Arg>
1014       static
1015       typename boost::disable_if<
1016         internal::reduced_is_same<Arg, CallContext>,
1017         typename from_nasal_ptr<Arg>::return_type
1018       >::type
1019       arg_from_nasal(const CallContext& ctx, size_t index)
1020       {
1021         return ctx.requireArg<Arg>(index);
1022       };
1023
1024       /**
1025        * Specialization to pass through nasal::CallContext.
1026        */
1027       template<class Arg>
1028       static
1029       typename boost::enable_if<
1030         internal::reduced_is_same<Arg, CallContext>,
1031         typename from_nasal_ptr<Arg>::return_type
1032       >::type
1033       arg_from_nasal(const CallContext& ctx, size_t)
1034       {
1035         // Either const CallContext& or CallContext, non-const reference
1036         // does not make sense.
1037         BOOST_STATIC_ASSERT( (!boost::is_same<Arg, CallContext&>::value) );
1038         return ctx;
1039       };
1040
1041       typedef std::auto_ptr<Ghost> GhostPtr;
1042       MemberMap         _members;
1043       fallback_getter_t _fallback_getter;
1044       fallback_setter_t _fallback_setter;
1045
1046       explicit Ghost(const std::string& name):
1047         GhostMetadata(name, &_ghost_type)
1048       {
1049         _ghost_type.destroy = &destroyGhost;
1050         _ghost_type.name = _name.c_str();
1051         _ghost_type.get_member = &getMember;
1052         _ghost_type.set_member = &setMember;
1053       }
1054
1055       static GhostPtr& getSingletonHolder()
1056       {
1057         static GhostPtr instance;
1058         return instance;
1059       }
1060
1061       static naRef makeGhost(naContext c, void *ptr)
1062       {
1063         if( getRawPtr(ptr) )
1064         {
1065           // We are wrapping shared pointers to already existing objects which
1066           // will then be hold be a new shared pointer. We therefore have to
1067           // check for the dynamic type of the object as it might differ from
1068           // the passed static type.
1069           naGhostType* ghost_type = getTypeFor<Ghost>( getRawPtr(ptr) );
1070
1071           if( ghost_type )
1072             return naNewGhost2(c, ghost_type, ptr);
1073         }
1074
1075         destroyGhost(ptr);
1076         return naNil();
1077       }
1078
1079       static void destroyGhost(void *ptr)
1080       {
1081         delete static_cast<pointer*>(ptr);
1082       }
1083
1084       /**
1085        * Callback for retrieving a ghost member.
1086        */
1087       static const char* getMember(naContext c, void* g, naRef key, naRef* out)
1088       {
1089         const std::string key_str = nasal::from_nasal<std::string>(c, key);
1090         // TODO merge instance parents with static class parents
1091 //        if( key_str == "parents" )
1092 //        {
1093 //          if( getSingletonPtr()->_parents.empty() )
1094 //            return 0;
1095 //
1096 //          *out = getSingletonPtr()->getParents(c);
1097 //          return "";
1098 //        }
1099
1100         typename MemberMap::iterator member =
1101           getSingletonPtr()->_members.find(key_str);
1102
1103         if( member == getSingletonPtr()->_members.end() )
1104         {
1105           fallback_getter_t fallback_get = getSingletonPtr()->_fallback_getter;
1106           if(    !fallback_get
1107               || !fallback_get(*getRawPtr(g), c, key_str, *out) )
1108             return 0;
1109         }
1110         else if( member->second.func )
1111           *out = member->second.func->get_naRef(c);
1112         else if( !member->second.getter.empty() )
1113           *out = member->second.getter(*getRawPtr(g), c);
1114         else
1115           return "Read-protected member";
1116
1117         return "";
1118       }
1119
1120       /**
1121        * Callback for writing to a ghost member.
1122        */
1123       static void setMember(naContext c, void* g, naRef field, naRef val)
1124       {
1125         const std::string key = nasal::from_nasal<std::string>(c, field);
1126         const MemberMap& members = getSingletonPtr()->_members;
1127
1128         typename MemberMap::const_iterator member = members.find(key);
1129         if( member == members.end() )
1130         {
1131           fallback_setter_t fallback_set = getSingletonPtr()->_fallback_setter;
1132           if( !fallback_set )
1133             naRuntimeError(c, "ghost: No such member: %s", key.c_str());
1134           else if( !fallback_set(*getRawPtr(g), c, key, val) )
1135             naRuntimeError(c, "ghost: Failed to write (_set: %s)", key.c_str());
1136         }
1137         else if( member->second.setter.empty() )
1138           naRuntimeError(c, "ghost: Write protected member: %s", key.c_str());
1139         else if( member->second.func )
1140           naRuntimeError(c, "ghost: Write to function: %s", key.c_str());
1141         else
1142           member->second.setter(*getRawPtr(g), c, val);
1143       }
1144   };
1145
1146   template<class T>
1147   naGhostType Ghost<T>::_ghost_type;
1148
1149 } // namespace nasal
1150
1151 // Needs to be outside any namespace to make ADL work
1152 /**
1153  * Convert every shared pointer to a ghost.
1154  */
1155 template<class T>
1156 typename boost::enable_if<
1157   nasal::internal::has_element_type<
1158     typename nasal::internal::reduced_type<T>::type
1159   >,
1160   naRef
1161 >::type
1162 to_nasal_helper(naContext c, T ptr)
1163 {
1164   return nasal::Ghost<T>::create(c, ptr);
1165 }
1166
1167 /**
1168  * Convert nasal ghosts/hashes to shared pointer (of a ghost).
1169  */
1170 template<class T>
1171 typename boost::enable_if<
1172   nasal::internal::has_element_type<
1173     typename nasal::internal::reduced_type<T>::type
1174   >,
1175   T
1176 >::type
1177 from_nasal_helper(naContext c, naRef ref, const T*)
1178 {
1179   return nasal::Ghost<T>::fromNasal(c, ref);
1180 }
1181
1182 /**
1183  * Convert any pointer to a SGReference based object to a ghost.
1184  */
1185 template<class T>
1186 typename boost::enable_if_c<
1187      boost::is_base_of<SGReferenced, T>::value
1188   || boost::is_base_of<SGWeakReferenced, T>::value,
1189   naRef
1190 >::type
1191 to_nasal_helper(naContext c, T* ptr)
1192 {
1193   return nasal::Ghost<SGSharedPtr<T> >::create(c, SGSharedPtr<T>(ptr));
1194 }
1195
1196 /**
1197  * Convert nasal ghosts/hashes to pointer (of a SGReference based ghost).
1198  */
1199 template<class T>
1200 typename boost::enable_if_c<
1201      boost::is_base_of<
1202        SGReferenced,
1203        typename boost::remove_pointer<T>::type
1204      >::value
1205   || boost::is_base_of<
1206        SGWeakReferenced,
1207        typename boost::remove_pointer<T>::type
1208      >::value,
1209   T
1210 >::type
1211 from_nasal_helper(naContext c, naRef ref, const T*)
1212 {
1213   typedef SGSharedPtr<typename boost::remove_pointer<T>::type> TypeRef;
1214   return nasal::Ghost<TypeRef>::fromNasal(c, ref).release();
1215 }
1216
1217 #endif /* SG_NASAL_GHOST_HXX_ */