| 
				   | 
				
| Line 303: | 
Line 303: | 
 | Also, the implementation of the SGPropertyNode bindings in [https://gitorious.org/fg/flightgear/blobs/next/src/Scripting/nasal-props.cxx nasal-props.cxx] contains additional examples.  |  | Also, the implementation of the SGPropertyNode bindings in [https://gitorious.org/fg/flightgear/blobs/next/src/Scripting/nasal-props.cxx nasal-props.cxx] contains additional examples.  | 
 | -->  |  | -->  | 
 | == A standard interface for mapping C++ classes to Nasal objects (Status 2013: implemented in cppbind) ==
  |  | 
 | 
  |  | 
 | There's a plan to eventually generalize and unify all those helpers in FGPositioned.cxx and provide them as part of the FGNasalSys class (or some custom abstract base class that C++ classes should implement to expose objects to Nasal), so that they automatically become available to all users of the Nasal subsystem, just by implementing a certain interface. By exposing objects with methods, rather than plain variables, it will be possible to compute results lazily.
  |  | 
 | 
  |  | 
 | Such an abstract base class should feature:
  |  | 
 | * virtual constructor to set up: ghost dtor, ghost name, ghost getter
  |  | 
 | * virtual destructor function to delete Nasal ghosts
  |  | 
 | * mapping helper to map C++ functions with SG/STL return types to Nasal data structures (std::string, std::vector, std::map) 
  |  | 
 | * lazy getter
  |  | 
 | * helper: hashset()
  |  | 
 | * helper: stringToNasal
  |  | 
 | * helper: to set up valid parents vector for inheritance to work properly
  |  | 
 |  
  |  | 
 | 
  |  | 
 | At the moment, the following discussions focuses on the FGPositioned/NasalPositioned implementation, however this will be further generalized during the next months.
  |  | 
 | Looking at the code in [http://gitorious.org/fg/flightgear/blobs/78afdb3c2227e75fc1542b3992dcea26181d98cf/src/Scripting/NasalPositioned.cxx#line139 NasalPositioned.cxx]:
  |  | 
 | 
  |  | 
 | The function bodies for these helpers are largely identical:
  |  | 
 | 
  |  | 
 | * naRef ghostForPositioned(naContext c, const FGPositioned* pos);
  |  | 
 | * naRef ghostForAirport(naContext c, const FGAirport* apt)
  |  | 
 | * naRef ghostForNavaid(naContext c, const FGNavRecord* n)
  |  | 
 | * naRef ghostForRunway(naContext c, const FGRunway* r)
  |  | 
 | * naRef ghostForWaypt(naContext c, const flightgear::Waypt* wpt)
  |  | 
 | 
  |  | 
 | So they could be replaced by a single template:
  |  | 
 | 
  |  | 
 | <syntaxhighlight lang="cpp">
  |  | 
 | template <class T>
  |  | 
 | static T* 
  |  | 
 | getGhost(naRef r)
  |  | 
 | {      
  |  | 
 |  if( naGhost_type(r) == &T::GhostType)
  |  | 
 |   return (T*) naGhost_ptr(r);
  |  | 
 |   return 0;
  |  | 
 | }    
  |  | 
 | </syntaxhighlight>
  |  | 
 | 
  |  | 
 | This would only require adding a new '''naGhostType''' member to the FGPositioned class (and automatically to FGAirport, FGNavRecord, FGRunway because they are derived from FGPositioned).
  |  | 
 | 
  |  | 
 | So it would make sense to simply use a new "FGNasalWrapper" class, add it to the top of NasalSys.hxx, i.e. for C++ classes that are intended to be exposed to Nasal ?
  |  | 
 | 
  |  | 
 | <syntaxhighlight lang="cpp">
  |  | 
 | class FGNasalWrapper {
  |  | 
 | public:
  |  | 
 | protected:
  |  | 
 |  naGhostType GhostType;
  |  | 
 | private:
  |  | 
 | };
  |  | 
 | </syntaxhighlight>
  |  | 
 | 
  |  | 
 | This would provide an opportunity to come up with an interface that is required to expose C++ classes as Nasal ghosts.
  |  | 
 | 
  |  | 
 | And then, by changing positioned.hxx such that it derives from the new "FGNasalWrapper" class:
  |  | 
 | 
  |  | 
 | <syntaxhighlight lang="cpp">
  |  | 
 | class FGPositioned : public SGReferenced, public FGNasalWrapper { 
  |  | 
 | // ...
  |  | 
 | };
  |  | 
 | </syntaxhighlight>
  |  | 
 | 
  |  | 
 | Once each instance/subclass of FGPositioned, has such a "GhostType" field, the getMember() wrappers you created [http://gitorious.org/fg/flightgear/blobs/78afdb3c2227e75fc1542b3992dcea26181d98cf/src/Scripting/NasalPositioned.cxx#line55 at the top of NasalPositioned.hxx], could be moved to the new FGNasalWrapper class, so that the GhostType field could be initialized by the ctor, using its own getMember() method:
  |  | 
 | 
  |  | 
 | <syntaxhighlight lang="cpp">
  |  | 
 | class FGNasalWrapper {
  |  | 
 | public:
  |  | 
 |  FGNasalWrapper(void *ghost_destructor, const char* ghost_name
  |  | 
 |  const char* getMember(naContext c, void* g, naRef field, naRef* out);
  |  | 
 | protected:
  |  | 
 |  naGhostType GhostType;
  |  | 
 | private:
  |  | 
 | };
  |  | 
 | </syntaxhighlight>
  |  | 
 | 
  |  | 
 | The ghost_destructor could also be implemented as a method of FGNasalWrapper (rather than being a global void* function), so that each C++ class that is to be exposed to Nasal, would only need to implement the class-specific methods.
  |  | 
 | 
  |  | 
 | Ultimately, this would also make it possible to reimplement the ghostFor* helpers as a template: http://gitorious.org/fg/flightgear/blobs/78afdb3c2227e75fc1542b3992dcea26181d98cf/src/Scripting/NasalPositioned.cxx#line139
  |  | 
 | 
  |  | 
 | * ghostForPositioned();
  |  | 
 | * ghostForAirport();
  |  | 
 | * ghostForNavaid();
  |  | 
 | * ghostForRunway();
  |  | 
 | * ghostForWaypt();
  |  | 
 | 
  |  | 
 | So that the "getGhost" method could be a part of the NasalWrapper class:
  |  | 
 | 
  |  | 
 | <syntaxhighlight lang="cpp">
  |  | 
 | template <class T>
  |  | 
 | naRef getGhost(naContext c, const T* pos)
  |  | 
 | {
  |  | 
 |   if (!pos) {
  |  | 
 |     return naNil();
  |  | 
 |   }
  |  | 
 |   T::get(pos); // take a ref
  |  | 
 |   return naNewGhost2(c, &T::GhostType, (void*) pos);
  |  | 
 | }
  |  | 
 | </syntaxhighlight>
  |  | 
 | 
  |  | 
 | = Passing Pointers to Nasal scripts =  |  | = Passing Pointers to Nasal scripts =  | 
 | Things get more complicated if you need to pass a handle to a C/C++ object into a Nasal script. There, you need to use a wrapped handle type called a ghost ("Garbage-collectable Handle to an OutSide Thing"), which has a callback that you need to implement to deal with what happens when the Nasal interpreter garbage collects your object.  |  | Things get more complicated if you need to pass a handle to a C/C++ object into a Nasal script. There, you need to use a wrapped handle type called a ghost ("Garbage-collectable Handle to an OutSide Thing"), which has a callback that you need to implement to deal with what happens when the Nasal interpreter garbage collects your object.  |