]> git.mxchange.org Git - simgear.git/commitdiff
Nasal: add an naRef to ghosts to allow for proper gc of dependent objects/ghosts.
authorThomas Geymayer <tomgey@gmail.com>
Sun, 23 Nov 2014 22:39:56 +0000 (23:39 +0100)
committerThomas Geymayer <tomgey@gmail.com>
Sun, 23 Nov 2014 22:45:22 +0000 (23:45 +0100)
This allows for binding the lifetime of any nasal object to
the lifetime of a ghost. Otherwise circular references from
objects saved within the ghost would prevent the ghost from
being garbage collected.

13 files changed:
simgear/nasal/cppbind/CMakeLists.txt
simgear/nasal/cppbind/cppbind_test.cxx [deleted file]
simgear/nasal/cppbind/cppbind_test_ghost.cxx [deleted file]
simgear/nasal/cppbind/nasal_num_test.cxx [deleted file]
simgear/nasal/cppbind/test/TestContext.hxx [new file with mode: 0644]
simgear/nasal/cppbind/test/cppbind_test.cxx [new file with mode: 0644]
simgear/nasal/cppbind/test/cppbind_test_ghost.cxx [new file with mode: 0644]
simgear/nasal/cppbind/test/nasal_gc_test.cxx [new file with mode: 0644]
simgear/nasal/cppbind/test/nasal_num_test.cxx [new file with mode: 0644]
simgear/nasal/data.h
simgear/nasal/gc.c
simgear/nasal/misc.c
simgear/nasal/nasal.h

index 1e52d9b913f5ae81fd0df88ee2ac5769b9ffa18f..9d005394dd8df120c24ea76df9b8943b196feaa4 100644 (file)
@@ -35,16 +35,21 @@ simgear_component(nasal/cppbind nasal/cppbind "${SOURCES}" "${HEADERS}")
 simgear_component(nasal/cppbind/detail nasal/cppbind/detail "" "${DETAIL_HEADERS}")
 
 add_boost_test(cppbind_ghost
-  SOURCES cppbind_test_ghost.cxx
+  SOURCES test/cppbind_test_ghost.cxx
   LIBRARIES ${TEST_LIBS}
 )
 
 add_boost_test(cppbind_misc
-  SOURCES cppbind_test.cxx
+  SOURCES test/cppbind_test.cxx
+  LIBRARIES ${TEST_LIBS}
+)
+
+add_boost_test(nasal_gc_test
+  SOURCES test/nasal_gc_test.cxx
   LIBRARIES ${TEST_LIBS}
 )
 
 add_boost_test(nasal_num
-  SOURCES nasal_num_test.cxx
+  SOURCES test/nasal_num_test.cxx
   LIBRARIES ${TEST_LIBS}
 )
\ No newline at end of file
diff --git a/simgear/nasal/cppbind/cppbind_test.cxx b/simgear/nasal/cppbind/cppbind_test.cxx
deleted file mode 100644 (file)
index 2ea918d..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-#define BOOST_TEST_MODULE cppbind
-#include <BoostTestTargetConfig.h>
-
-#include "Ghost.hxx"
-#include "NasalHash.hxx"
-#include "NasalString.hxx"
-
-#include <simgear/math/SGMath.hxx>
-#include <simgear/structure/map.hxx>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-#include <cstring>
-
-enum MyEnum
-{
-  ENUM_FIRST,
-  ENUM_ANOTHER,
-  ENUM_LAST
-};
-struct Base
-{
-  naRef member(const nasal::CallContext&) { return naNil(); }
-  virtual ~Base(){};
-
-  std::string getString() const { return ""; }
-  void setString(const std::string&) {}
-  void constVoidFunc() const {}
-  size_t test1Arg(const std::string& str) const { return str.length(); }
-  bool test2Args(const std::string& s, bool c) { return c && s.empty(); }
-
-  std::string var;
-  const std::string& getVar() const { return var; }
-  void setVar(const std::string v) { var = v; }
-
-  unsigned long getThis() const { return (unsigned long)this; }
-  bool genericSet(const std::string& key, const std::string& val)
-  {
-    return key == "test";
-  }
-  bool genericGet(const std::string& key, std::string& val_out) const
-  {
-    if( key != "get_test" )
-      return false;
-
-    val_out = "generic-get";
-    return true;
-  }
-};
-
-void baseVoidFunc(Base& b) {}
-void baseConstVoidFunc(const Base& b) {}
-size_t baseFunc2Args(Base& b, int x, const std::string& s) { return x + s.size(); }
-std::string testPtr(Base& b) { return b.getString(); }
-void baseFuncCallContext(const Base&, const nasal::CallContext&) {}
-
-struct Derived:
-  public Base
-{
-  int _x;
-  int getX() const { return _x; }
-  void setX(int x) { _x = x; }
-};
-
-naRef f_derivedGetRandom(const Derived&, naContext)
-{
-  return naNil();
-}
-void f_derivedSetX(Derived& d, naContext, naRef r)
-{
-  d._x = static_cast<int>(naNumValue(r).num);
-}
-
-struct DoubleDerived:
-  public Derived
-{
-
-};
-
-typedef boost::shared_ptr<Base> BasePtr;
-typedef std::vector<BasePtr> BaseVec;
-
-struct DoubleDerived2:
-  public Derived
-{
-  const BasePtr& getBase() const{return _base;}
-  BasePtr _base;
-  BaseVec doSomeBaseWork(const BaseVec& v) { return v; }
-};
-
-class SGReferenceBasedClass:
-  public SGReferenced
-{
-
-};
-
-class SGWeakReferenceBasedClass:
-  public SGWeakReferenced
-{
-
-};
-
-typedef boost::shared_ptr<Derived> DerivedPtr;
-typedef boost::shared_ptr<DoubleDerived> DoubleDerivedPtr;
-typedef boost::shared_ptr<DoubleDerived2> DoubleDerived2Ptr;
-typedef SGSharedPtr<SGReferenceBasedClass> SGRefBasedPtr;
-typedef SGSharedPtr<SGWeakReferenceBasedClass> SGWeakRefBasedPtr;
-
-typedef boost::weak_ptr<Derived> DerivedWeakPtr;
-
-naRef derivedFreeMember(Derived&, const nasal::CallContext&) { return naNil(); }
-naRef f_derivedGetX(const Derived& d, naContext c)
-{
-  return nasal::to_nasal(c, d.getX());
-}
-naRef f_freeFunction(nasal::CallContext c) { return c.requireArg<naRef>(0); }
-
-BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
-{
-  naContext c = naNewContext();
-  naRef r;
-
-  using namespace nasal;
-
-  r = to_nasal(c, ENUM_ANOTHER);
-  BOOST_CHECK_EQUAL(from_nasal<int>(c, r), ENUM_ANOTHER);
-
-  r = to_nasal(c, "Test");
-  BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
-  BOOST_CHECK_EQUAL(from_nasal<std::string>(c, r), "Test");
-
-  r = to_nasal(c, std::string("Test"));
-  BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
-  BOOST_CHECK_EQUAL(from_nasal<std::string>(c, r), "Test");
-
-  r = to_nasal(c, 42);
-  BOOST_CHECK_EQUAL(naNumValue(r).num, 42);
-  BOOST_CHECK_EQUAL(from_nasal<int>(c, r), 42);
-
-  r = to_nasal(c, 4.2f);
-  BOOST_CHECK_EQUAL(naNumValue(r).num, 4.2f);
-  BOOST_CHECK_EQUAL(from_nasal<float>(c, r), 4.2f);
-
-  float test_data[3] = {0, 4, 2};
-  r = to_nasal(c, test_data);
-
-  SGVec2f vec(0,2);
-  r = to_nasal(c, vec);
-  BOOST_CHECK_EQUAL(from_nasal<SGVec2f>(c, r), vec);
-
-  std::vector<int> std_vec;
-  r = to_nasal(c, std_vec);
-
-  r = to_nasal(c, "string");
-  BOOST_CHECK_THROW(from_nasal<int>(c, r), bad_nasal_cast);
-
-  Hash hash(c);
-  hash.set("vec", r);
-  hash.set("vec2", vec);
-  hash.set("name", "my-name");
-  hash.set("string", std::string("blub"));
-  hash.set("func", &f_freeFunction);
-
-  BOOST_CHECK_EQUAL(hash.size(), 5);
-  for(Hash::const_iterator it = hash.begin(); it != hash.end(); ++it)
-    BOOST_CHECK_EQUAL( hash.get<std::string>(it->getKey()),
-                       it->getValue<std::string>() );
-
-  Hash::iterator it1, it2;
-  Hash::const_iterator it3 = it1, it4(it2);
-  it1 = it2;
-  it3 = it2;
-
-  r = to_nasal(c, hash);
-  BOOST_REQUIRE( naIsHash(r) );
-
-  simgear::StringMap string_map = from_nasal<simgear::StringMap>(c, r);
-  BOOST_CHECK_EQUAL(string_map.at("vec"), "string");
-  BOOST_CHECK_EQUAL(string_map.at("name"), "my-name");
-  BOOST_CHECK_EQUAL(string_map.at("string"), "blub");
-
-  BOOST_CHECK_EQUAL(hash.get<std::string>("name"), "my-name");
-  BOOST_CHECK(naIsString(hash.get("name")));
-
-  Hash mod = hash.createHash("mod");
-  mod.set("parent", hash);
-
-
-  // 'func' is a C++ function registered to Nasal and now converted back to C++
-  boost::function<int (int)> f = hash.get<int (int)>("func");
-  BOOST_REQUIRE( f );
-  BOOST_CHECK_EQUAL(f(3), 3);
-
-  boost::function<std::string (int)> fs = hash.get<std::string (int)>("func");
-  BOOST_REQUIRE( fs );
-  BOOST_CHECK_EQUAL(fs(14), "14");
-
-  typedef boost::function<void (int)> FuncVoidInt;
-  FuncVoidInt fvi = hash.get<FuncVoidInt>("func");
-  BOOST_REQUIRE( fvi );
-  fvi(123);
-
-  typedef boost::function<std::string (const std::string&, int, float)> FuncMultiArg;
-  FuncMultiArg fma = hash.get<FuncMultiArg>("func");
-  BOOST_REQUIRE( fma );
-  BOOST_CHECK_EQUAL(fma("test", 3, .5), "test");
-
-  typedef boost::function<naRef (naRef)> naRefMemFunc;
-  naRefMemFunc fmem = hash.get<naRefMemFunc>("func");
-  BOOST_REQUIRE( fmem );
-  naRef ret = fmem(hash.get_naRef()),
-        hash_ref = hash.get_naRef();
-  BOOST_CHECK( naIsIdentical(ret, hash_ref) );
-
-  // Check if nasal::Me gets passed as self/me and remaining arguments are
-  // passed on to function
-  typedef boost::function<int (Me, int)> MeIntFunc;
-  MeIntFunc fmeint = hash.get<MeIntFunc>("func");
-  BOOST_CHECK_EQUAL(fmeint(naNil(), 5), 5);
-
-  //----------------------------------------------------------------------------
-  // Test exposing classes to Nasal
-  //----------------------------------------------------------------------------
-
-  Ghost<BasePtr>::init("BasePtr")
-    .method("member", &Base::member)
-    .method("strlen", &Base::test1Arg)
-    .member("str", &Base::getString, &Base::setString)
-    .method("str_m", &Base::getString)
-    .method("void", &Base::constVoidFunc)
-    .member("var_r", &Base::getVar)
-    .member("var_w", &Base::setVar)
-    .member("var", &Base::getVar, &Base::setVar)
-    .method("void", &baseVoidFunc)
-    .method("void_c", &baseConstVoidFunc)
-    .method("int2args", &baseFunc2Args)
-    .method("bool2args", &Base::test2Args)
-    .method("str_ptr", &testPtr)
-    .method("this", &Base::getThis)
-    ._set(&Base::genericSet)
-    ._get(&Base::genericGet);
-  Ghost<DerivedPtr>::init("DerivedPtr")
-    .bases<BasePtr>()
-    .member("x", &Derived::getX, &Derived::setX)
-    .member("x_alternate", &f_derivedGetX)
-    .member("x_mixed", &f_derivedGetRandom, &Derived::setX)
-    .member("x_mixed2", &Derived::getX, &f_derivedSetX)
-    .member("x_w", &f_derivedSetX)
-    .method("free_fn", &derivedFreeMember)
-    .method("free_member", &derivedFreeMember)
-    .method("baseDoIt", &baseFuncCallContext);
-  Ghost<DoubleDerivedPtr>::init("DoubleDerivedPtr")
-    .bases<DerivedPtr>();
-  Ghost<DoubleDerived2Ptr>::init("DoubleDerived2Ptr")
-    .bases< Ghost<DerivedPtr> >()
-    .member("base", &DoubleDerived2::getBase)
-    .method("doIt", &DoubleDerived2::doSomeBaseWork);
-
-  Ghost<SGRefBasedPtr>::init("SGRefBasedPtr");
-  Ghost<SGWeakRefBasedPtr>::init("SGWeakRefBasedPtr");
-
-  SGWeakRefBasedPtr weak_ptr(new SGWeakReferenceBasedClass());
-  naRef nasal_ref = to_nasal(c, weak_ptr),
-        nasal_ptr = to_nasal(c, weak_ptr.get());
-
-  BOOST_REQUIRE( naIsGhost(nasal_ref) );
-  BOOST_REQUIRE( naIsGhost(nasal_ptr) );
-
-  SGWeakRefBasedPtr ptr1 = from_nasal<SGWeakRefBasedPtr>(c, nasal_ref),
-                    ptr2 = from_nasal<SGWeakRefBasedPtr>(c, nasal_ptr);
-
-  BOOST_CHECK_EQUAL(weak_ptr, ptr1);
-  BOOST_CHECK_EQUAL(weak_ptr, ptr2);
-
-
-  BOOST_REQUIRE( Ghost<BasePtr>::isInit() );
-  nasal::to_nasal(c, DoubleDerived2Ptr());
-
-  BasePtr d( new Derived );
-  naRef derived = to_nasal(c, d);
-  BOOST_REQUIRE( naIsGhost(derived) );
-  BOOST_CHECK_EQUAL( std::string("DerivedPtr"), naGhost_type(derived)->name );
-
-  // Get member function from ghost...
-  naRef thisGetter = naNil();
-  BOOST_CHECK( naMember_get(c, derived, to_nasal(c, "this"), &thisGetter) );
-  BOOST_CHECK( naIsFunc(thisGetter) );
-
-  // ...and check if it really gets passed the correct instance
-  typedef boost::function<unsigned long (Me)> MemFunc;
-  MemFunc fGetThis = from_nasal<MemFunc>(c, thisGetter);
-  BOOST_REQUIRE( fGetThis );
-  BOOST_CHECK_EQUAL( fGetThis(derived), (unsigned long)d.get() );
-
-  BasePtr d2( new DoubleDerived );
-  derived = to_nasal(c, d2);
-  BOOST_CHECK( naIsGhost(derived) );
-  BOOST_CHECK_EQUAL( std::string("DoubleDerivedPtr"),
-                     naGhost_type(derived)->name );
-
-  BasePtr d3( new DoubleDerived2 );
-  derived = to_nasal(c, d3);
-  BOOST_CHECK( naIsGhost(derived) );
-  BOOST_CHECK_EQUAL( std::string("DoubleDerived2Ptr"),
-                     naGhost_type(derived)->name );
-
-  SGRefBasedPtr ref_based( new SGReferenceBasedClass );
-  naRef na_ref_based = to_nasal(c, ref_based.get());
-  BOOST_CHECK( naIsGhost(na_ref_based) );
-  BOOST_CHECK_EQUAL( from_nasal<SGReferenceBasedClass*>(c, na_ref_based),
-                     ref_based.get() );
-  BOOST_CHECK_EQUAL( from_nasal<SGRefBasedPtr>(c, na_ref_based), ref_based );
-
-  BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, derived), d3 );
-  BOOST_CHECK_NE( from_nasal<BasePtr>(c, derived), d2 );
-  BOOST_CHECK_EQUAL( from_nasal<DerivedPtr>(c, derived),
-                     boost::dynamic_pointer_cast<Derived>(d3) );
-  BOOST_CHECK_EQUAL( from_nasal<DoubleDerived2Ptr>(c, derived),
-                     boost::dynamic_pointer_cast<DoubleDerived2>(d3) );
-  BOOST_CHECK_THROW( from_nasal<DoubleDerivedPtr>(c, derived), bad_nasal_cast );
-
-  std::map<std::string, BasePtr> instances;
-  BOOST_CHECK( naIsHash(to_nasal(c, instances)) );
-
-  std::map<std::string, DerivedPtr> instances_d;
-  BOOST_CHECK( naIsHash(to_nasal(c, instances_d)) );
-
-  std::map<std::string, int> int_map;
-  BOOST_CHECK( naIsHash(to_nasal(c, int_map)) );
-
-  std::map<std::string, std::vector<int> > int_vector_map;
-  BOOST_CHECK( naIsHash(to_nasal(c, int_vector_map)) );
-
-  simgear::StringMap dict =
-    simgear::StringMap("hello", "value")
-                      ("key2", "value2");
-  naRef na_dict = to_nasal(c, dict);
-  BOOST_REQUIRE( naIsHash(na_dict) );
-  BOOST_CHECK_EQUAL( Hash(na_dict, c).get<std::string>("key2"), "value2" );
-
-  // Check converting to Ghost if using Nasal hashes with actual ghost inside
-  // the hashes parents vector
-  std::vector<naRef> parents;
-  parents.push_back(hash.get_naRef());
-  parents.push_back(derived);
-
-  Hash obj(c);
-  obj.set("parents", parents);
-  BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, obj.get_naRef()), d3 );
-
-  // Check recursive parents (aka parent-of-parent)
-  std::vector<naRef> parents2;
-  parents2.push_back(obj.get_naRef());
-  Hash derived_obj(c);
-  derived_obj.set("parents", parents2);
-  BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, derived_obj.get_naRef()), d3 );
-
-  std::vector<naRef> nasal_objects;
-  nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d) );
-  nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d2) );
-  nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d3) );
-  naRef obj_vec = to_nasal(c, nasal_objects);
-
-  std::vector<BasePtr> objects = from_nasal<std::vector<BasePtr> >(c, obj_vec);
-  BOOST_CHECK_EQUAL( objects[0], d );
-  BOOST_CHECK_EQUAL( objects[1], d2 );
-  BOOST_CHECK_EQUAL( objects[2], d3 );
-
-  {
-    // Calling fallback setter for unset values
-    const char* src_code = "me.test = 3;";
-    int errLine = -1;
-    naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0,
-                              (char*)src_code, strlen(src_code),
-                              &errLine );
-    ret = naCallMethod(code, derived, 0, 0, naNil());
-
-    BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to
-                                     //      always return 0...
-    BOOST_CHECK_EQUAL( from_nasal<int>(c, ret), 3 );
-  }
-  {
-    // Calling generic (fallback) getter
-    const char* src_code = "var a = me.get_test;";
-    int errLine = -1;
-    naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0,
-                              (char*)src_code, strlen(src_code),
-                              &errLine );
-    ret = naCallMethod(code, derived, 0, 0, naNil());
-
-    BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to
-                                     //      always return 0...
-    BOOST_CHECK_EQUAL( from_nasal<std::string>(c, ret), "generic-get" );
-  }
-
-  //----------------------------------------------------------------------------
-  // Test nasal::CallContext
-  //----------------------------------------------------------------------------
-
-
-  int int_vec[] = {1,2,3};
-  std::map<std::string, std::string> map;
-  naRef args[] = {
-    to_nasal(c, std::string("test-arg")),
-    to_nasal(c, 4),
-    to_nasal(c, int_vec),
-    to_nasal(c, map)
-  };
-  CallContext cc(c, naNil(), sizeof(args)/sizeof(args[0]), args);
-  BOOST_CHECK_EQUAL( cc.requireArg<std::string>(0), "test-arg" );
-  BOOST_CHECK_EQUAL( cc.getArg<std::string>(0), "test-arg" );
-  BOOST_CHECK_EQUAL( cc.getArg<std::string>(10), "" );
-  BOOST_CHECK( cc.isString(0) );
-  BOOST_CHECK( !cc.isNumeric(0) );
-  BOOST_CHECK( !cc.isVector(0) );
-  BOOST_CHECK( !cc.isHash(0) );
-  BOOST_CHECK( !cc.isGhost(0) );
-  BOOST_CHECK( cc.isNumeric(1) );
-  BOOST_CHECK( cc.isVector(2) );
-  BOOST_CHECK( cc.isHash(3) );
-
-  naRef args_vec = nasal::to_nasal(c, args);
-  BOOST_CHECK( naIsVector(args_vec) );
-
-  //----------------------------------------------------------------------------
-  // Test nasal::String
-  //----------------------------------------------------------------------------
-
-  String string( to_nasal(c, "Test") );
-  BOOST_CHECK_EQUAL( from_nasal<std::string>(c, string.get_naRef()), "Test" );
-  BOOST_CHECK_EQUAL( string.c_str(), std::string("Test") );
-  BOOST_CHECK( string.starts_with(string) );
-  BOOST_CHECK( string.starts_with(String(c, "T")) );
-  BOOST_CHECK( string.starts_with(String(c, "Te")) );
-  BOOST_CHECK( string.starts_with(String(c, "Tes")) );
-  BOOST_CHECK( string.starts_with(String(c, "Test")) );
-  BOOST_CHECK( !string.starts_with(String(c, "Test1")) );
-  BOOST_CHECK( !string.starts_with(String(c, "bb")) );
-  BOOST_CHECK( !string.starts_with(String(c, "bbasdasdafasd")) );
-  BOOST_CHECK( string.ends_with(String(c, "t")) );
-  BOOST_CHECK( string.ends_with(String(c, "st")) );
-  BOOST_CHECK( string.ends_with(String(c, "est")) );
-  BOOST_CHECK( string.ends_with(String(c, "Test")) );
-  BOOST_CHECK( !string.ends_with(String(c, "1Test")) );
-  BOOST_CHECK( !string.ends_with(String(c, "abc")) );
-  BOOST_CHECK( !string.ends_with(String(c, "estasdasd")) );
-  BOOST_CHECK_EQUAL( string.find('e'), 1 );
-  BOOST_CHECK_EQUAL( string.find('9'), String::npos );
-  BOOST_CHECK_EQUAL( string.find_first_of(String(c, "st")), 2 );
-  BOOST_CHECK_EQUAL( string.find_first_of(String(c, "st"), 3), 3 );
-  BOOST_CHECK_EQUAL( string.find_first_of(String(c, "xyz")), String::npos );
-  BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tst")), 1 );
-  BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tse"), 2), 3 );
-  BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc")), 0 );
-  BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc"), 20), String::npos );
-
-  naFreeContext(c);
-}
diff --git a/simgear/nasal/cppbind/cppbind_test_ghost.cxx b/simgear/nasal/cppbind/cppbind_test_ghost.cxx
deleted file mode 100644 (file)
index 9892498..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-#define BOOST_TEST_MODULE cppbind
-#include <BoostTestTargetConfig.h>
-
-#include "Ghost.hxx"
-#include "NasalContext.hxx"
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-class Base1:
-  public virtual SGVirtualWeakReferenced
-{};
-
-class Base2:
-  public virtual SGVirtualWeakReferenced
-{};
-
-class Derived:
-  public Base1,
-  public Base2
-{};
-
-typedef SGSharedPtr<Base1> Base1Ptr;
-typedef SGSharedPtr<Base2> Base2Ptr;
-typedef SGSharedPtr<Derived> DerivedPtr;
-typedef SGWeakPtr<Derived> DerivedWeakPtr;
-
-typedef SGSharedPtr<SGReferenced> SGReferencedPtr;
-
-// Check if shared_ptr_traits give correct types for strong and weak shared
-// pointer
-#define CHECK_PTR_TRAIT_TYPE(ptr_t, type_name, type)\
-  BOOST_STATIC_ASSERT((boost::is_same<\
-    nasal::shared_ptr_traits<ptr_t>::type_name,\
-    type\
-  >::value));
-
-#define CHECK_PTR_TRAIT(ref, weak)\
-  CHECK_PTR_TRAIT_TYPE(ref, strong_ref, ref)\
-  CHECK_PTR_TRAIT_TYPE(weak, weak_ref, weak)\
-
-CHECK_PTR_TRAIT(DerivedPtr, DerivedWeakPtr)
-CHECK_PTR_TRAIT(boost::shared_ptr<Base1>, boost::weak_ptr<Base1>)
-
-#undef CHECK_PTR_TRAIT
-#undef CHECK_PTR_TRAIT_TYPE
-
-BOOST_STATIC_ASSERT(( nasal::supports_weak_ref<Base1Ptr>::value));
-BOOST_STATIC_ASSERT(( nasal::supports_weak_ref<Base2Ptr>::value));
-BOOST_STATIC_ASSERT(( nasal::supports_weak_ref<DerivedPtr>::value));
-BOOST_STATIC_ASSERT(( nasal::supports_weak_ref<DerivedWeakPtr>::value));
-BOOST_STATIC_ASSERT((!nasal::supports_weak_ref<SGReferencedPtr>::value));
-
-static void setupGhosts()
-{
-  nasal::Ghost<Base1Ptr>::init("Base1");
-  nasal::Ghost<Base2Ptr>::init("Base2");
-  nasal::Ghost<DerivedPtr>::init("Derived")
-    .bases<Base1Ptr>()
-    .bases<Base2Ptr>();
-}
-
-//------------------------------------------------------------------------------
-BOOST_AUTO_TEST_CASE( ghost_weak_strong_nasal_conversion )
-{
-  setupGhosts();
-  naContext c = naNewContext();
-
-  DerivedPtr d = new Derived();
-  DerivedWeakPtr weak(d);
-
-  // store weak pointer and extract strong pointer
-  naRef na_d = nasal::to_nasal(c, DerivedWeakPtr(d));
-  BOOST_REQUIRE( naIsGhost(na_d) );
-  BOOST_CHECK_EQUAL( nasal::from_nasal<Base1Ptr>(c, na_d), d );
-  BOOST_CHECK_EQUAL( nasal::from_nasal<Base2Ptr>(c, na_d), d );
-  BOOST_CHECK_EQUAL( nasal::from_nasal<DerivedPtr>(c, na_d), d );
-
-  d.reset();
-  BOOST_REQUIRE_THROW( nasal::from_nasal<DerivedPtr>(c, na_d),
-                       nasal::bad_nasal_cast );
-
-  // store strong pointer and extract weak pointer
-  d.reset(new Derived);
-  na_d = nasal::to_nasal(c, d);
-  BOOST_REQUIRE( naIsGhost(na_d) );
-
-  weak = nasal::from_nasal<DerivedWeakPtr>(c, na_d);
-  BOOST_CHECK_EQUAL( weak.lock(), d );
-
-  d.reset();
-  BOOST_REQUIRE( nasal::from_nasal<DerivedPtr>(c, na_d) );
-  BOOST_REQUIRE( weak.lock() );
-
-  naFreeContext(c);
-  naGC();
-  nasal::ghostProcessDestroyList();
-
-  BOOST_REQUIRE( !weak.lock() );
-}
-
-//------------------------------------------------------------------------------
-BOOST_AUTO_TEST_CASE( ghost_casting_storage )
-{
-  setupGhosts();
-  nasal::Context c;
-
-  // Check converting to and from every class in the hierarchy for an instance
-  // of the leaf class
-  DerivedPtr d = new Derived();
-
-  naRef na_d = nasal::to_nasal(c, d),
-        na_b1 = nasal::to_nasal(c, Base1Ptr(d)),
-        na_b2 = nasal::to_nasal(c, Base2Ptr(d));
-
-  Derived *d0 = nasal::from_nasal<Derived*>(c, na_d),
-          *d1 = nasal::from_nasal<Derived*>(c, na_b1),
-          *d2 = nasal::from_nasal<Derived*>(c, na_b2);
-
-  BOOST_CHECK_EQUAL(d0, d.get());
-  BOOST_CHECK_EQUAL(d1, d.get());
-  BOOST_CHECK_EQUAL(d2, d.get());
-
-  Base1 *b1 = nasal::from_nasal<Base1*>(c, na_b1);
-  BOOST_CHECK_EQUAL(b1, static_cast<Base1*>(d.get()));
-
-  Base2 *b2 = nasal::from_nasal<Base2*>(c, na_b2);
-  BOOST_CHECK_EQUAL(b2, static_cast<Base2*>(d.get()));
-
-  // Check converting from base class instance to derived classes is not
-  // possible
-  Base1Ptr b1_ref = new Base1();
-  na_b1 = nasal::to_nasal(c, b1_ref);
-
-  BOOST_CHECK_EQUAL(nasal::from_nasal<Base1*>(c, na_b1), b1_ref.get());
-  BOOST_CHECK_THROW(nasal::from_nasal<Base2*>(c, na_b1), nasal::bad_nasal_cast);
-  BOOST_CHECK_THROW(nasal::from_nasal<Derived*>(c, na_b1), nasal::bad_nasal_cast);
-}
-
-//------------------------------------------------------------------------------
-#define CHECK_PTR_STORAGE_TRAIT_TYPE(ptr_t, storage)\
-  BOOST_STATIC_ASSERT((boost::is_same<\
-    nasal::shared_ptr_storage<ptr_t>::storage_type,\
-    storage\
-  >::value));
-
-CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedPtr, Derived)
-CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedWeakPtr, DerivedWeakPtr)
-
-typedef boost::shared_ptr<Derived> BoostDerivedPtr;
-CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedPtr, BoostDerivedPtr)
-
-typedef boost::weak_ptr<Derived> BoostDerivedWeakPtr;
-CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedWeakPtr, BoostDerivedWeakPtr)
-
-#undef CHECK_PTR_STORAGE_TRAIT_TYPE
-
-BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<Base1Ptr>::is_intrusive::value));
-BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<Base2Ptr>::is_intrusive::value));
-BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<DerivedPtr>::is_intrusive::value));
-BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<DerivedWeakPtr>::is_intrusive::value));
-BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<SGReferencedPtr>::is_intrusive::value));
-
-BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits<boost::shared_ptr<Derived> >::is_intrusive::value));
-BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits<boost::weak_ptr<Derived> >::is_intrusive::value));
-
-BOOST_AUTO_TEST_CASE( storage_traits )
-{
-  DerivedPtr d = new Derived();
-
-  Derived* d_raw = nasal::shared_ptr_storage<DerivedPtr>::ref(d);
-  BOOST_REQUIRE_EQUAL(d_raw, d.get());
-  BOOST_REQUIRE_EQUAL(d.getNumRefs(), 2);
-
-  DerivedWeakPtr* d_weak = nasal::shared_ptr_storage<DerivedWeakPtr>::ref(d);
-  BOOST_REQUIRE_EQUAL(
-    nasal::shared_ptr_storage<DerivedWeakPtr>::get<Derived*>(d_weak),
-    d_raw
-  );
-
-  d.reset();
-  BOOST_REQUIRE_EQUAL(Derived::count(d_raw), 1);
-
-  nasal::shared_ptr_storage<DerivedPtr>::unref(d_raw);
-  BOOST_REQUIRE(d_weak->expired());
-
-  nasal::shared_ptr_storage<DerivedWeakPtr>::unref(d_weak);
-}
diff --git a/simgear/nasal/cppbind/nasal_num_test.cxx b/simgear/nasal/cppbind/nasal_num_test.cxx
deleted file mode 100644 (file)
index e1e7dd0..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#define BOOST_TEST_MODULE cppbind
-#include <BoostTestTargetConfig.h>
-
-#include "NasalCallContext.hxx"
-
-class TestContext:
-  public nasal::CallContext
-{
-  public:
-    TestContext():
-      CallContext(naNewContext(), naNil(), 0, 0)
-    {}
-
-    ~TestContext()
-    {
-      naFreeContext(c);
-    }
-
-    template<class T>
-    T from_str(const std::string& str)
-    {
-      return from_nasal<T>(to_nasal(str));
-    }
-
-    naRef exec(const std::string& code_str, nasal::Me me)
-    {
-      int err_line = -1;
-      naRef code = naParseCode( c, to_nasal("<TextContext::exec>"), 0,
-                                (char*)code_str.c_str(), code_str.length(),
-                                &err_line );
-      if( !naIsCode(code) )
-        throw std::runtime_error("Failed to parse code: " + code_str);
-
-      return naCallMethod(code, me, 0, 0, naNil());
-    }
-
-    template<class T>
-    T exec(const std::string& code)
-    {
-      return from_nasal<T>(exec(code, naNil()));
-    }
-
-    template<class T>
-    T convert(const std::string& str)
-    {
-      return from_nasal<T>(to_nasal(str));
-    }
-};
-
-static void runNumTests( double (TestContext::*test_double)(const std::string&),
-                         int (TestContext::*test_int)(const std::string&) )
-{
-  TestContext c;
-
-  BOOST_CHECK_CLOSE((c.*test_double)("0.5"), 0.5, 1e-5);
-  BOOST_CHECK_CLOSE((c.*test_double)(".6"),  0.6, 1e-5);
-  BOOST_CHECK_CLOSE((c.*test_double)("-.7"), -0.7, 1e-5);
-  BOOST_CHECK_CLOSE((c.*test_double)("-0.8"), -0.8, 1e-5);
-  BOOST_CHECK_SMALL((c.*test_double)("0.0"), 1e-5);
-  BOOST_CHECK_SMALL((c.*test_double)("-.0"), 1e-5);
-
-  BOOST_CHECK_CLOSE((c.*test_double)("1.23e4"),  1.23e4, 1e-5);
-  BOOST_CHECK_CLOSE((c.*test_double)("1.23e-4"), 1.23e-4, 1e-5);
-  BOOST_CHECK_CLOSE((c.*test_double)("-1.23e4"),  -1.23e4, 1e-5);
-  BOOST_CHECK_CLOSE((c.*test_double)("-1.23e-4"), -1.23e-4, 1e-5);
-  BOOST_CHECK_CLOSE((c.*test_double)("1e-4"), 1e-4, 1e-5);
-  BOOST_CHECK_CLOSE((c.*test_double)("-1e-4"), -1e-4, 1e-5);
-
-  BOOST_CHECK_EQUAL((c.*test_int)("123"), 123);
-  BOOST_CHECK_EQUAL((c.*test_int)("-958"), -958);
-
-  BOOST_CHECK_CLOSE((c.*test_int)("-1e7"), -1e7, 1e-5);
-  BOOST_CHECK_CLOSE((c.*test_int)("2E07"), 2e07, 1e-5);
-
-  BOOST_CHECK_EQUAL((c.*test_int)("0755"), 755);
-  BOOST_CHECK_EQUAL((c.*test_int)("0055"), 55);
-  BOOST_CHECK_EQUAL((c.*test_int)("-0155"), -155);
-
-  BOOST_CHECK_EQUAL((c.*test_int)("0o755"), 0755);
-  BOOST_CHECK_EQUAL((c.*test_int)("0o055"), 055);
-  BOOST_CHECK_EQUAL((c.*test_int)("-0o155"), -0155);
-
-  BOOST_CHECK_EQUAL((c.*test_int)("0x755"), 0x755);
-  BOOST_CHECK_EQUAL((c.*test_int)("0x055"), 0x55);
-  BOOST_CHECK_EQUAL((c.*test_int)("-0x155"), -0x155);
-}
-
-BOOST_AUTO_TEST_CASE( parse_num )
-{
-  runNumTests(&TestContext::convert<double>, &TestContext::convert<int>);
-}
-
-BOOST_AUTO_TEST_CASE( lex_num )
-{
-  runNumTests(&TestContext::exec<double>, &TestContext::exec<int>);
-}
diff --git a/simgear/nasal/cppbind/test/TestContext.hxx b/simgear/nasal/cppbind/test/TestContext.hxx
new file mode 100644 (file)
index 0000000..69c2113
--- /dev/null
@@ -0,0 +1,76 @@
+///@file
+/// Nasal context for testing and executing code
+///
+// Copyright (C) 2014  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef SG_NASAL_TESTCONTEXT_HXX_
+#define SG_NASAL_TESTCONTEXT_HXX_
+
+#include <simgear/nasal/cppbind/NasalCallContext.hxx>
+
+class TestContext:
+  public nasal::CallContext
+{
+  public:
+    TestContext():
+      CallContext(naNewContext(), naNil(), 0, 0)
+    {}
+
+    ~TestContext()
+    {
+      naFreeContext(c);
+    }
+
+    void runGC()
+    {
+      naFreeContext(c);
+      naGC();
+      c = naNewContext();
+    }
+
+    template<class T>
+    T from_str(const std::string& str)
+    {
+      return from_nasal<T>(to_nasal(str));
+    }
+
+    naRef exec(const std::string& code_str, nasal::Me me)
+    {
+      int err_line = -1;
+      naRef code = naParseCode( c, to_nasal("<TextContext::exec>"), 0,
+                                (char*)code_str.c_str(), code_str.length(),
+                                &err_line );
+      if( !naIsCode(code) )
+        throw std::runtime_error("Failed to parse code: " + code_str);
+
+      return naCallMethod(code, me, 0, 0, naNil());
+    }
+
+    template<class T>
+    T exec(const std::string& code)
+    {
+      return from_nasal<T>(exec(code, naNil()));
+    }
+
+    template<class T>
+    T convert(const std::string& str)
+    {
+      return from_nasal<T>(to_nasal(str));
+    }
+};
+
+#endif /* SG_NASAL_TESTCONTEXT_HXX_ */
diff --git a/simgear/nasal/cppbind/test/cppbind_test.cxx b/simgear/nasal/cppbind/test/cppbind_test.cxx
new file mode 100644 (file)
index 0000000..b4371f1
--- /dev/null
@@ -0,0 +1,458 @@
+#define BOOST_TEST_MODULE cppbind
+#include <BoostTestTargetConfig.h>
+
+#include <simgear/nasal/cppbind/Ghost.hxx>
+#include <simgear/nasal/cppbind/NasalHash.hxx>
+#include <simgear/nasal/cppbind/NasalString.hxx>
+#include <simgear/math/SGMath.hxx>
+#include <simgear/structure/map.hxx>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <cstring>
+
+enum MyEnum
+{
+  ENUM_FIRST,
+  ENUM_ANOTHER,
+  ENUM_LAST
+};
+struct Base
+{
+  naRef member(const nasal::CallContext&) { return naNil(); }
+  virtual ~Base(){};
+
+  std::string getString() const { return ""; }
+  void setString(const std::string&) {}
+  void constVoidFunc() const {}
+  size_t test1Arg(const std::string& str) const { return str.length(); }
+  bool test2Args(const std::string& s, bool c) { return c && s.empty(); }
+
+  std::string var;
+  const std::string& getVar() const { return var; }
+  void setVar(const std::string v) { var = v; }
+
+  unsigned long getThis() const { return (unsigned long)this; }
+  bool genericSet(const std::string& key, const std::string& val)
+  {
+    return key == "test";
+  }
+  bool genericGet(const std::string& key, std::string& val_out) const
+  {
+    if( key != "get_test" )
+      return false;
+
+    val_out = "generic-get";
+    return true;
+  }
+};
+
+void baseVoidFunc(Base& b) {}
+void baseConstVoidFunc(const Base& b) {}
+size_t baseFunc2Args(Base& b, int x, const std::string& s) { return x + s.size(); }
+std::string testPtr(Base& b) { return b.getString(); }
+void baseFuncCallContext(const Base&, const nasal::CallContext&) {}
+
+struct Derived:
+  public Base
+{
+  int _x;
+  int getX() const { return _x; }
+  void setX(int x) { _x = x; }
+};
+
+naRef f_derivedGetRandom(const Derived&, naContext)
+{
+  return naNil();
+}
+void f_derivedSetX(Derived& d, naContext, naRef r)
+{
+  d._x = static_cast<int>(naNumValue(r).num);
+}
+
+struct DoubleDerived:
+  public Derived
+{
+
+};
+
+typedef boost::shared_ptr<Base> BasePtr;
+typedef std::vector<BasePtr> BaseVec;
+
+struct DoubleDerived2:
+  public Derived
+{
+  const BasePtr& getBase() const{return _base;}
+  BasePtr _base;
+  BaseVec doSomeBaseWork(const BaseVec& v) { return v; }
+};
+
+class SGReferenceBasedClass:
+  public SGReferenced
+{
+
+};
+
+class SGWeakReferenceBasedClass:
+  public SGWeakReferenced
+{
+
+};
+
+typedef boost::shared_ptr<Derived> DerivedPtr;
+typedef boost::shared_ptr<DoubleDerived> DoubleDerivedPtr;
+typedef boost::shared_ptr<DoubleDerived2> DoubleDerived2Ptr;
+typedef SGSharedPtr<SGReferenceBasedClass> SGRefBasedPtr;
+typedef SGSharedPtr<SGWeakReferenceBasedClass> SGWeakRefBasedPtr;
+
+typedef boost::weak_ptr<Derived> DerivedWeakPtr;
+
+naRef derivedFreeMember(Derived&, const nasal::CallContext&) { return naNil(); }
+naRef f_derivedGetX(const Derived& d, naContext c)
+{
+  return nasal::to_nasal(c, d.getX());
+}
+naRef f_freeFunction(nasal::CallContext c) { return c.requireArg<naRef>(0); }
+
+BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
+{
+  naContext c = naNewContext();
+  naRef r;
+
+  using namespace nasal;
+
+  r = to_nasal(c, ENUM_ANOTHER);
+  BOOST_CHECK_EQUAL(from_nasal<int>(c, r), ENUM_ANOTHER);
+
+  r = to_nasal(c, "Test");
+  BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
+  BOOST_CHECK_EQUAL(from_nasal<std::string>(c, r), "Test");
+
+  r = to_nasal(c, std::string("Test"));
+  BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
+  BOOST_CHECK_EQUAL(from_nasal<std::string>(c, r), "Test");
+
+  r = to_nasal(c, 42);
+  BOOST_CHECK_EQUAL(naNumValue(r).num, 42);
+  BOOST_CHECK_EQUAL(from_nasal<int>(c, r), 42);
+
+  r = to_nasal(c, 4.2f);
+  BOOST_CHECK_EQUAL(naNumValue(r).num, 4.2f);
+  BOOST_CHECK_EQUAL(from_nasal<float>(c, r), 4.2f);
+
+  float test_data[3] = {0, 4, 2};
+  r = to_nasal(c, test_data);
+
+  SGVec2f vec(0,2);
+  r = to_nasal(c, vec);
+  BOOST_CHECK_EQUAL(from_nasal<SGVec2f>(c, r), vec);
+
+  std::vector<int> std_vec;
+  r = to_nasal(c, std_vec);
+
+  r = to_nasal(c, "string");
+  BOOST_CHECK_THROW(from_nasal<int>(c, r), bad_nasal_cast);
+
+  Hash hash(c);
+  hash.set("vec", r);
+  hash.set("vec2", vec);
+  hash.set("name", "my-name");
+  hash.set("string", std::string("blub"));
+  hash.set("func", &f_freeFunction);
+
+  BOOST_CHECK_EQUAL(hash.size(), 5);
+  for(Hash::const_iterator it = hash.begin(); it != hash.end(); ++it)
+    BOOST_CHECK_EQUAL( hash.get<std::string>(it->getKey()),
+                       it->getValue<std::string>() );
+
+  Hash::iterator it1, it2;
+  Hash::const_iterator it3 = it1, it4(it2);
+  it1 = it2;
+  it3 = it2;
+
+  r = to_nasal(c, hash);
+  BOOST_REQUIRE( naIsHash(r) );
+
+  simgear::StringMap string_map = from_nasal<simgear::StringMap>(c, r);
+  BOOST_CHECK_EQUAL(string_map.at("vec"), "string");
+  BOOST_CHECK_EQUAL(string_map.at("name"), "my-name");
+  BOOST_CHECK_EQUAL(string_map.at("string"), "blub");
+
+  BOOST_CHECK_EQUAL(hash.get<std::string>("name"), "my-name");
+  BOOST_CHECK(naIsString(hash.get("name")));
+
+  Hash mod = hash.createHash("mod");
+  mod.set("parent", hash);
+
+
+  // 'func' is a C++ function registered to Nasal and now converted back to C++
+  boost::function<int (int)> f = hash.get<int (int)>("func");
+  BOOST_REQUIRE( f );
+  BOOST_CHECK_EQUAL(f(3), 3);
+
+  boost::function<std::string (int)> fs = hash.get<std::string (int)>("func");
+  BOOST_REQUIRE( fs );
+  BOOST_CHECK_EQUAL(fs(14), "14");
+
+  typedef boost::function<void (int)> FuncVoidInt;
+  FuncVoidInt fvi = hash.get<FuncVoidInt>("func");
+  BOOST_REQUIRE( fvi );
+  fvi(123);
+
+  typedef boost::function<std::string (const std::string&, int, float)> FuncMultiArg;
+  FuncMultiArg fma = hash.get<FuncMultiArg>("func");
+  BOOST_REQUIRE( fma );
+  BOOST_CHECK_EQUAL(fma("test", 3, .5), "test");
+
+  typedef boost::function<naRef (naRef)> naRefMemFunc;
+  naRefMemFunc fmem = hash.get<naRefMemFunc>("func");
+  BOOST_REQUIRE( fmem );
+  naRef ret = fmem(hash.get_naRef()),
+        hash_ref = hash.get_naRef();
+  BOOST_CHECK( naIsIdentical(ret, hash_ref) );
+
+  // Check if nasal::Me gets passed as self/me and remaining arguments are
+  // passed on to function
+  typedef boost::function<int (Me, int)> MeIntFunc;
+  MeIntFunc fmeint = hash.get<MeIntFunc>("func");
+  BOOST_CHECK_EQUAL(fmeint(naNil(), 5), 5);
+
+  //----------------------------------------------------------------------------
+  // Test exposing classes to Nasal
+  //----------------------------------------------------------------------------
+
+  Ghost<BasePtr>::init("BasePtr")
+    .method("member", &Base::member)
+    .method("strlen", &Base::test1Arg)
+    .member("str", &Base::getString, &Base::setString)
+    .method("str_m", &Base::getString)
+    .method("void", &Base::constVoidFunc)
+    .member("var_r", &Base::getVar)
+    .member("var_w", &Base::setVar)
+    .member("var", &Base::getVar, &Base::setVar)
+    .method("void", &baseVoidFunc)
+    .method("void_c", &baseConstVoidFunc)
+    .method("int2args", &baseFunc2Args)
+    .method("bool2args", &Base::test2Args)
+    .method("str_ptr", &testPtr)
+    .method("this", &Base::getThis)
+    ._set(&Base::genericSet)
+    ._get(&Base::genericGet);
+  Ghost<DerivedPtr>::init("DerivedPtr")
+    .bases<BasePtr>()
+    .member("x", &Derived::getX, &Derived::setX)
+    .member("x_alternate", &f_derivedGetX)
+    .member("x_mixed", &f_derivedGetRandom, &Derived::setX)
+    .member("x_mixed2", &Derived::getX, &f_derivedSetX)
+    .member("x_w", &f_derivedSetX)
+    .method("free_fn", &derivedFreeMember)
+    .method("free_member", &derivedFreeMember)
+    .method("baseDoIt", &baseFuncCallContext);
+  Ghost<DoubleDerivedPtr>::init("DoubleDerivedPtr")
+    .bases<DerivedPtr>();
+  Ghost<DoubleDerived2Ptr>::init("DoubleDerived2Ptr")
+    .bases< Ghost<DerivedPtr> >()
+    .member("base", &DoubleDerived2::getBase)
+    .method("doIt", &DoubleDerived2::doSomeBaseWork);
+
+  Ghost<SGRefBasedPtr>::init("SGRefBasedPtr");
+  Ghost<SGWeakRefBasedPtr>::init("SGWeakRefBasedPtr");
+
+  SGWeakRefBasedPtr weak_ptr(new SGWeakReferenceBasedClass());
+  naRef nasal_ref = to_nasal(c, weak_ptr),
+        nasal_ptr = to_nasal(c, weak_ptr.get());
+
+  BOOST_REQUIRE( naIsGhost(nasal_ref) );
+  BOOST_REQUIRE( naIsGhost(nasal_ptr) );
+
+  SGWeakRefBasedPtr ptr1 = from_nasal<SGWeakRefBasedPtr>(c, nasal_ref),
+                    ptr2 = from_nasal<SGWeakRefBasedPtr>(c, nasal_ptr);
+
+  BOOST_CHECK_EQUAL(weak_ptr, ptr1);
+  BOOST_CHECK_EQUAL(weak_ptr, ptr2);
+
+
+  BOOST_REQUIRE( Ghost<BasePtr>::isInit() );
+  nasal::to_nasal(c, DoubleDerived2Ptr());
+
+  BasePtr d( new Derived );
+  naRef derived = to_nasal(c, d);
+  BOOST_REQUIRE( naIsGhost(derived) );
+  BOOST_CHECK_EQUAL( std::string("DerivedPtr"), naGhost_type(derived)->name );
+
+  // Get member function from ghost...
+  naRef thisGetter = naNil();
+  BOOST_CHECK( naMember_get(c, derived, to_nasal(c, "this"), &thisGetter) );
+  BOOST_CHECK( naIsFunc(thisGetter) );
+
+  // ...and check if it really gets passed the correct instance
+  typedef boost::function<unsigned long (Me)> MemFunc;
+  MemFunc fGetThis = from_nasal<MemFunc>(c, thisGetter);
+  BOOST_REQUIRE( fGetThis );
+  BOOST_CHECK_EQUAL( fGetThis(derived), (unsigned long)d.get() );
+
+  BasePtr d2( new DoubleDerived );
+  derived = to_nasal(c, d2);
+  BOOST_CHECK( naIsGhost(derived) );
+  BOOST_CHECK_EQUAL( std::string("DoubleDerivedPtr"),
+                     naGhost_type(derived)->name );
+
+  BasePtr d3( new DoubleDerived2 );
+  derived = to_nasal(c, d3);
+  BOOST_CHECK( naIsGhost(derived) );
+  BOOST_CHECK_EQUAL( std::string("DoubleDerived2Ptr"),
+                     naGhost_type(derived)->name );
+
+  SGRefBasedPtr ref_based( new SGReferenceBasedClass );
+  naRef na_ref_based = to_nasal(c, ref_based.get());
+  BOOST_CHECK( naIsGhost(na_ref_based) );
+  BOOST_CHECK_EQUAL( from_nasal<SGReferenceBasedClass*>(c, na_ref_based),
+                     ref_based.get() );
+  BOOST_CHECK_EQUAL( from_nasal<SGRefBasedPtr>(c, na_ref_based), ref_based );
+
+  BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, derived), d3 );
+  BOOST_CHECK_NE( from_nasal<BasePtr>(c, derived), d2 );
+  BOOST_CHECK_EQUAL( from_nasal<DerivedPtr>(c, derived),
+                     boost::dynamic_pointer_cast<Derived>(d3) );
+  BOOST_CHECK_EQUAL( from_nasal<DoubleDerived2Ptr>(c, derived),
+                     boost::dynamic_pointer_cast<DoubleDerived2>(d3) );
+  BOOST_CHECK_THROW( from_nasal<DoubleDerivedPtr>(c, derived), bad_nasal_cast );
+
+  std::map<std::string, BasePtr> instances;
+  BOOST_CHECK( naIsHash(to_nasal(c, instances)) );
+
+  std::map<std::string, DerivedPtr> instances_d;
+  BOOST_CHECK( naIsHash(to_nasal(c, instances_d)) );
+
+  std::map<std::string, int> int_map;
+  BOOST_CHECK( naIsHash(to_nasal(c, int_map)) );
+
+  std::map<std::string, std::vector<int> > int_vector_map;
+  BOOST_CHECK( naIsHash(to_nasal(c, int_vector_map)) );
+
+  simgear::StringMap dict =
+    simgear::StringMap("hello", "value")
+                      ("key2", "value2");
+  naRef na_dict = to_nasal(c, dict);
+  BOOST_REQUIRE( naIsHash(na_dict) );
+  BOOST_CHECK_EQUAL( Hash(na_dict, c).get<std::string>("key2"), "value2" );
+
+  // Check converting to Ghost if using Nasal hashes with actual ghost inside
+  // the hashes parents vector
+  std::vector<naRef> parents;
+  parents.push_back(hash.get_naRef());
+  parents.push_back(derived);
+
+  Hash obj(c);
+  obj.set("parents", parents);
+  BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, obj.get_naRef()), d3 );
+
+  // Check recursive parents (aka parent-of-parent)
+  std::vector<naRef> parents2;
+  parents2.push_back(obj.get_naRef());
+  Hash derived_obj(c);
+  derived_obj.set("parents", parents2);
+  BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, derived_obj.get_naRef()), d3 );
+
+  std::vector<naRef> nasal_objects;
+  nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d) );
+  nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d2) );
+  nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d3) );
+  naRef obj_vec = to_nasal(c, nasal_objects);
+
+  std::vector<BasePtr> objects = from_nasal<std::vector<BasePtr> >(c, obj_vec);
+  BOOST_CHECK_EQUAL( objects[0], d );
+  BOOST_CHECK_EQUAL( objects[1], d2 );
+  BOOST_CHECK_EQUAL( objects[2], d3 );
+
+  {
+    // Calling fallback setter for unset values
+    const char* src_code = "me.test = 3;";
+    int errLine = -1;
+    naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0,
+                              (char*)src_code, strlen(src_code),
+                              &errLine );
+    ret = naCallMethod(code, derived, 0, 0, naNil());
+
+    BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to
+                                     //      always return 0...
+    BOOST_CHECK_EQUAL( from_nasal<int>(c, ret), 3 );
+  }
+  {
+    // Calling generic (fallback) getter
+    const char* src_code = "var a = me.get_test;";
+    int errLine = -1;
+    naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0,
+                              (char*)src_code, strlen(src_code),
+                              &errLine );
+    ret = naCallMethod(code, derived, 0, 0, naNil());
+
+    BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to
+                                     //      always return 0...
+    BOOST_CHECK_EQUAL( from_nasal<std::string>(c, ret), "generic-get" );
+  }
+
+  //----------------------------------------------------------------------------
+  // Test nasal::CallContext
+  //----------------------------------------------------------------------------
+
+
+  int int_vec[] = {1,2,3};
+  std::map<std::string, std::string> map;
+  naRef args[] = {
+    to_nasal(c, std::string("test-arg")),
+    to_nasal(c, 4),
+    to_nasal(c, int_vec),
+    to_nasal(c, map)
+  };
+  CallContext cc(c, naNil(), sizeof(args)/sizeof(args[0]), args);
+  BOOST_CHECK_EQUAL( cc.requireArg<std::string>(0), "test-arg" );
+  BOOST_CHECK_EQUAL( cc.getArg<std::string>(0), "test-arg" );
+  BOOST_CHECK_EQUAL( cc.getArg<std::string>(10), "" );
+  BOOST_CHECK( cc.isString(0) );
+  BOOST_CHECK( !cc.isNumeric(0) );
+  BOOST_CHECK( !cc.isVector(0) );
+  BOOST_CHECK( !cc.isHash(0) );
+  BOOST_CHECK( !cc.isGhost(0) );
+  BOOST_CHECK( cc.isNumeric(1) );
+  BOOST_CHECK( cc.isVector(2) );
+  BOOST_CHECK( cc.isHash(3) );
+
+  naRef args_vec = nasal::to_nasal(c, args);
+  BOOST_CHECK( naIsVector(args_vec) );
+
+  //----------------------------------------------------------------------------
+  // Test nasal::String
+  //----------------------------------------------------------------------------
+
+  String string( to_nasal(c, "Test") );
+  BOOST_CHECK_EQUAL( from_nasal<std::string>(c, string.get_naRef()), "Test" );
+  BOOST_CHECK_EQUAL( string.c_str(), std::string("Test") );
+  BOOST_CHECK( string.starts_with(string) );
+  BOOST_CHECK( string.starts_with(String(c, "T")) );
+  BOOST_CHECK( string.starts_with(String(c, "Te")) );
+  BOOST_CHECK( string.starts_with(String(c, "Tes")) );
+  BOOST_CHECK( string.starts_with(String(c, "Test")) );
+  BOOST_CHECK( !string.starts_with(String(c, "Test1")) );
+  BOOST_CHECK( !string.starts_with(String(c, "bb")) );
+  BOOST_CHECK( !string.starts_with(String(c, "bbasdasdafasd")) );
+  BOOST_CHECK( string.ends_with(String(c, "t")) );
+  BOOST_CHECK( string.ends_with(String(c, "st")) );
+  BOOST_CHECK( string.ends_with(String(c, "est")) );
+  BOOST_CHECK( string.ends_with(String(c, "Test")) );
+  BOOST_CHECK( !string.ends_with(String(c, "1Test")) );
+  BOOST_CHECK( !string.ends_with(String(c, "abc")) );
+  BOOST_CHECK( !string.ends_with(String(c, "estasdasd")) );
+  BOOST_CHECK_EQUAL( string.find('e'), 1 );
+  BOOST_CHECK_EQUAL( string.find('9'), String::npos );
+  BOOST_CHECK_EQUAL( string.find_first_of(String(c, "st")), 2 );
+  BOOST_CHECK_EQUAL( string.find_first_of(String(c, "st"), 3), 3 );
+  BOOST_CHECK_EQUAL( string.find_first_of(String(c, "xyz")), String::npos );
+  BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tst")), 1 );
+  BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tse"), 2), 3 );
+  BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc")), 0 );
+  BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc"), 20), String::npos );
+
+  naFreeContext(c);
+}
diff --git a/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx b/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx
new file mode 100644 (file)
index 0000000..d74f6a1
--- /dev/null
@@ -0,0 +1,188 @@
+#define BOOST_TEST_MODULE cppbind
+#include <BoostTestTargetConfig.h>
+
+#include <simgear/nasal/cppbind/Ghost.hxx>
+#include <simgear/nasal/cppbind/NasalContext.hxx>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+class Base1:
+  public virtual SGVirtualWeakReferenced
+{};
+
+class Base2:
+  public virtual SGVirtualWeakReferenced
+{};
+
+class Derived:
+  public Base1,
+  public Base2
+{};
+
+typedef SGSharedPtr<Base1> Base1Ptr;
+typedef SGSharedPtr<Base2> Base2Ptr;
+typedef SGSharedPtr<Derived> DerivedPtr;
+typedef SGWeakPtr<Derived> DerivedWeakPtr;
+
+typedef SGSharedPtr<SGReferenced> SGReferencedPtr;
+
+// Check if shared_ptr_traits give correct types for strong and weak shared
+// pointer
+#define CHECK_PTR_TRAIT_TYPE(ptr_t, type_name, type)\
+  BOOST_STATIC_ASSERT((boost::is_same<\
+    nasal::shared_ptr_traits<ptr_t>::type_name,\
+    type\
+  >::value));
+
+#define CHECK_PTR_TRAIT(ref, weak)\
+  CHECK_PTR_TRAIT_TYPE(ref, strong_ref, ref)\
+  CHECK_PTR_TRAIT_TYPE(weak, weak_ref, weak)\
+
+CHECK_PTR_TRAIT(DerivedPtr, DerivedWeakPtr)
+CHECK_PTR_TRAIT(boost::shared_ptr<Base1>, boost::weak_ptr<Base1>)
+
+#undef CHECK_PTR_TRAIT
+#undef CHECK_PTR_TRAIT_TYPE
+
+BOOST_STATIC_ASSERT(( nasal::supports_weak_ref<Base1Ptr>::value));
+BOOST_STATIC_ASSERT(( nasal::supports_weak_ref<Base2Ptr>::value));
+BOOST_STATIC_ASSERT(( nasal::supports_weak_ref<DerivedPtr>::value));
+BOOST_STATIC_ASSERT(( nasal::supports_weak_ref<DerivedWeakPtr>::value));
+BOOST_STATIC_ASSERT((!nasal::supports_weak_ref<SGReferencedPtr>::value));
+
+static void setupGhosts()
+{
+  nasal::Ghost<Base1Ptr>::init("Base1");
+  nasal::Ghost<Base2Ptr>::init("Base2");
+  nasal::Ghost<DerivedPtr>::init("Derived")
+    .bases<Base1Ptr>()
+    .bases<Base2Ptr>();
+}
+
+//------------------------------------------------------------------------------
+BOOST_AUTO_TEST_CASE( ghost_weak_strong_nasal_conversion )
+{
+  setupGhosts();
+  naContext c = naNewContext();
+
+  DerivedPtr d = new Derived();
+  DerivedWeakPtr weak(d);
+
+  // store weak pointer and extract strong pointer
+  naRef na_d = nasal::to_nasal(c, DerivedWeakPtr(d));
+  BOOST_REQUIRE( naIsGhost(na_d) );
+  BOOST_CHECK_EQUAL( nasal::from_nasal<Base1Ptr>(c, na_d), d );
+  BOOST_CHECK_EQUAL( nasal::from_nasal<Base2Ptr>(c, na_d), d );
+  BOOST_CHECK_EQUAL( nasal::from_nasal<DerivedPtr>(c, na_d), d );
+
+  d.reset();
+  BOOST_REQUIRE_THROW( nasal::from_nasal<DerivedPtr>(c, na_d),
+                       nasal::bad_nasal_cast );
+
+  // store strong pointer and extract weak pointer
+  d.reset(new Derived);
+  na_d = nasal::to_nasal(c, d);
+  BOOST_REQUIRE( naIsGhost(na_d) );
+
+  weak = nasal::from_nasal<DerivedWeakPtr>(c, na_d);
+  BOOST_CHECK_EQUAL( weak.lock(), d );
+
+  d.reset();
+  BOOST_REQUIRE( nasal::from_nasal<DerivedPtr>(c, na_d) );
+  BOOST_REQUIRE( weak.lock() );
+
+  naFreeContext(c);
+  naGC();
+  nasal::ghostProcessDestroyList();
+
+  BOOST_REQUIRE( !weak.lock() );
+}
+
+//------------------------------------------------------------------------------
+BOOST_AUTO_TEST_CASE( ghost_casting_storage )
+{
+  setupGhosts();
+  nasal::Context c;
+
+  // Check converting to and from every class in the hierarchy for an instance
+  // of the leaf class
+  DerivedPtr d = new Derived();
+
+  naRef na_d = nasal::to_nasal(c, d),
+        na_b1 = nasal::to_nasal(c, Base1Ptr(d)),
+        na_b2 = nasal::to_nasal(c, Base2Ptr(d));
+
+  Derived *d0 = nasal::from_nasal<Derived*>(c, na_d),
+          *d1 = nasal::from_nasal<Derived*>(c, na_b1),
+          *d2 = nasal::from_nasal<Derived*>(c, na_b2);
+
+  BOOST_CHECK_EQUAL(d0, d.get());
+  BOOST_CHECK_EQUAL(d1, d.get());
+  BOOST_CHECK_EQUAL(d2, d.get());
+
+  Base1 *b1 = nasal::from_nasal<Base1*>(c, na_b1);
+  BOOST_CHECK_EQUAL(b1, static_cast<Base1*>(d.get()));
+
+  Base2 *b2 = nasal::from_nasal<Base2*>(c, na_b2);
+  BOOST_CHECK_EQUAL(b2, static_cast<Base2*>(d.get()));
+
+  // Check converting from base class instance to derived classes is not
+  // possible
+  Base1Ptr b1_ref = new Base1();
+  na_b1 = nasal::to_nasal(c, b1_ref);
+
+  BOOST_CHECK_EQUAL(nasal::from_nasal<Base1*>(c, na_b1), b1_ref.get());
+  BOOST_CHECK_THROW(nasal::from_nasal<Base2*>(c, na_b1), nasal::bad_nasal_cast);
+  BOOST_CHECK_THROW(nasal::from_nasal<Derived*>(c, na_b1), nasal::bad_nasal_cast);
+}
+
+//------------------------------------------------------------------------------
+#define CHECK_PTR_STORAGE_TRAIT_TYPE(ptr_t, storage)\
+  BOOST_STATIC_ASSERT((boost::is_same<\
+    nasal::shared_ptr_storage<ptr_t>::storage_type,\
+    storage\
+  >::value));
+
+CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedPtr, Derived)
+CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedWeakPtr, DerivedWeakPtr)
+
+typedef boost::shared_ptr<Derived> BoostDerivedPtr;
+CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedPtr, BoostDerivedPtr)
+
+typedef boost::weak_ptr<Derived> BoostDerivedWeakPtr;
+CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedWeakPtr, BoostDerivedWeakPtr)
+
+#undef CHECK_PTR_STORAGE_TRAIT_TYPE
+
+BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<Base1Ptr>::is_intrusive::value));
+BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<Base2Ptr>::is_intrusive::value));
+BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<DerivedPtr>::is_intrusive::value));
+BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<DerivedWeakPtr>::is_intrusive::value));
+BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<SGReferencedPtr>::is_intrusive::value));
+
+BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits<boost::shared_ptr<Derived> >::is_intrusive::value));
+BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits<boost::weak_ptr<Derived> >::is_intrusive::value));
+
+BOOST_AUTO_TEST_CASE( storage_traits )
+{
+  DerivedPtr d = new Derived();
+
+  Derived* d_raw = nasal::shared_ptr_storage<DerivedPtr>::ref(d);
+  BOOST_REQUIRE_EQUAL(d_raw, d.get());
+  BOOST_REQUIRE_EQUAL(d.getNumRefs(), 2);
+
+  DerivedWeakPtr* d_weak = nasal::shared_ptr_storage<DerivedWeakPtr>::ref(d);
+  BOOST_REQUIRE_EQUAL(
+    nasal::shared_ptr_storage<DerivedWeakPtr>::get<Derived*>(d_weak),
+    d_raw
+  );
+
+  d.reset();
+  BOOST_REQUIRE_EQUAL(Derived::count(d_raw), 1);
+
+  nasal::shared_ptr_storage<DerivedPtr>::unref(d_raw);
+  BOOST_REQUIRE(d_weak->expired());
+
+  nasal::shared_ptr_storage<DerivedWeakPtr>::unref(d_weak);
+}
diff --git a/simgear/nasal/cppbind/test/nasal_gc_test.cxx b/simgear/nasal/cppbind/test/nasal_gc_test.cxx
new file mode 100644 (file)
index 0000000..c1cb6ca
--- /dev/null
@@ -0,0 +1,93 @@
+#define BOOST_TEST_MODULE nasal
+#include <BoostTestTargetConfig.h>
+
+#include "TestContext.hxx"
+#include <iostream>
+#include <set>
+
+static std::set<intptr_t> active_instances;
+
+static void ghost_destroy(void* p)
+{
+  active_instances.erase((intptr_t)p);
+}
+
+static naGhostType ghost_type = {
+  &ghost_destroy,
+  "TestGhost",
+  0, // get_member
+  0  // set_member
+};
+
+static naRef createTestGhost(TestContext& c, intptr_t p)
+{
+  active_instances.insert(p);
+  return naNewGhost(c.c, &ghost_type, (void*)p);
+}
+
+//------------------------------------------------------------------------------
+BOOST_AUTO_TEST_CASE( ghost_gc )
+{
+  TestContext c;
+  BOOST_REQUIRE(active_instances.empty());
+
+  //-----------------------------------------------
+  // Just create ghosts and let the gc destroy them
+
+  naRef g1 = createTestGhost(c, 1),
+        g2 = createTestGhost(c, 2);
+
+  BOOST_CHECK_EQUAL(active_instances.count(1), 1);
+  BOOST_CHECK_EQUAL(active_instances.count(2), 1);
+  BOOST_CHECK_EQUAL(active_instances.size(), 2);
+
+  c.runGC();
+
+  BOOST_REQUIRE(active_instances.empty());
+
+
+  //-----------------------------------------------
+  // Create some more ghosts and save one instance
+  // from being garbage collected.
+
+  g1 = createTestGhost(c, 1);
+  g2 = createTestGhost(c, 2);
+
+  int gc1 = naGCSave(g1);
+  c.runGC();
+
+  BOOST_CHECK_EQUAL(active_instances.count(1), 1);
+  BOOST_CHECK_EQUAL(active_instances.size(), 1);
+
+  naGCRelease(gc1);
+  c.runGC();
+
+  BOOST_REQUIRE(active_instances.empty());
+
+
+  //-----------------------------------------------
+  // Now test attaching one ghost to another
+
+  g1 = createTestGhost(c, 1);
+  g2 = createTestGhost(c, 2);
+
+  gc1 = naGCSave(g1);
+  naGhost_setData(g1, g2); // bind lifetime of g2 to g1...
+
+  c.runGC();
+
+  BOOST_CHECK_EQUAL(active_instances.count(1), 1);
+  BOOST_CHECK_EQUAL(active_instances.count(2), 1);
+  BOOST_CHECK_EQUAL(active_instances.size(), 2);
+
+  naGhost_setData(g1, naNil()); // cut connection
+  c.runGC();
+
+  BOOST_CHECK_EQUAL(active_instances.count(1), 1);
+  BOOST_CHECK_EQUAL(active_instances.size(), 1);
+
+  naGCRelease(gc1);
+  c.runGC();
+
+  BOOST_REQUIRE(active_instances.empty());
+}
diff --git a/simgear/nasal/cppbind/test/nasal_num_test.cxx b/simgear/nasal/cppbind/test/nasal_num_test.cxx
new file mode 100644 (file)
index 0000000..9900066
--- /dev/null
@@ -0,0 +1,52 @@
+#define BOOST_TEST_MODULE nasal
+#include <BoostTestTargetConfig.h>
+
+#include "TestContext.hxx"
+
+static void runNumTests( double (TestContext::*test_double)(const std::string&),
+                         int (TestContext::*test_int)(const std::string&) )
+{
+  TestContext c;
+
+  BOOST_CHECK_CLOSE((c.*test_double)("0.5"), 0.5, 1e-5);
+  BOOST_CHECK_CLOSE((c.*test_double)(".6"),  0.6, 1e-5);
+  BOOST_CHECK_CLOSE((c.*test_double)("-.7"), -0.7, 1e-5);
+  BOOST_CHECK_CLOSE((c.*test_double)("-0.8"), -0.8, 1e-5);
+  BOOST_CHECK_SMALL((c.*test_double)("0.0"), 1e-5);
+  BOOST_CHECK_SMALL((c.*test_double)("-.0"), 1e-5);
+
+  BOOST_CHECK_CLOSE((c.*test_double)("1.23e4"),  1.23e4, 1e-5);
+  BOOST_CHECK_CLOSE((c.*test_double)("1.23e-4"), 1.23e-4, 1e-5);
+  BOOST_CHECK_CLOSE((c.*test_double)("-1.23e4"),  -1.23e4, 1e-5);
+  BOOST_CHECK_CLOSE((c.*test_double)("-1.23e-4"), -1.23e-4, 1e-5);
+  BOOST_CHECK_CLOSE((c.*test_double)("1e-4"), 1e-4, 1e-5);
+  BOOST_CHECK_CLOSE((c.*test_double)("-1e-4"), -1e-4, 1e-5);
+
+  BOOST_CHECK_EQUAL((c.*test_int)("123"), 123);
+  BOOST_CHECK_EQUAL((c.*test_int)("-958"), -958);
+
+  BOOST_CHECK_CLOSE((c.*test_int)("-1e7"), -1e7, 1e-5);
+  BOOST_CHECK_CLOSE((c.*test_int)("2E07"), 2e07, 1e-5);
+
+  BOOST_CHECK_EQUAL((c.*test_int)("0755"), 755);
+  BOOST_CHECK_EQUAL((c.*test_int)("0055"), 55);
+  BOOST_CHECK_EQUAL((c.*test_int)("-0155"), -155);
+
+  BOOST_CHECK_EQUAL((c.*test_int)("0o755"), 0755);
+  BOOST_CHECK_EQUAL((c.*test_int)("0o055"), 055);
+  BOOST_CHECK_EQUAL((c.*test_int)("-0o155"), -0155);
+
+  BOOST_CHECK_EQUAL((c.*test_int)("0x755"), 0x755);
+  BOOST_CHECK_EQUAL((c.*test_int)("0x055"), 0x55);
+  BOOST_CHECK_EQUAL((c.*test_int)("-0x155"), -0x155);
+}
+
+BOOST_AUTO_TEST_CASE( parse_num )
+{
+  runNumTests(&TestContext::convert<double>, &TestContext::convert<int>);
+}
+
+BOOST_AUTO_TEST_CASE( lex_num )
+{
+  runNumTests(&TestContext::exec<double>, &TestContext::exec<int>);
+}
index 25f7008e7bb2f3038f55a2ab1c94ee0049ae478f..9a91c3e8b7ff371f30c32ca7ea55be61adc5d709 100644 (file)
@@ -175,6 +175,7 @@ struct naGhost {
     GC_HEADER;
     naGhostType* gtype;
     void* ptr;
+    naRef data; //!< Nasal data bound to the lifetime of the ghost.
 };
 
 struct naPool {
index a965aa79c523e3f881a9b4d3e2ad1d53b69e4239..5ac9c43c21fcc7ee2495d7e5e347bb67e6a20622 100644 (file)
@@ -177,7 +177,7 @@ static void newBlock(struct naPool* p, int need)
     newb->next = p->blocks;
     p->blocks = newb;
     naBZero(newb->block, need * p->elemsz);
-    
+
     if(need > p->freesz - p->freetop) need = p->freesz - p->freetop;
     p->nfree = 0;
     p->free = p->free0 + p->freetop;
@@ -263,6 +263,9 @@ static void mark(naRef r)
         mark(PTR(r).func->namespace);
         mark(PTR(r).func->next);
         break;
+    case T_GHOST:
+        mark(PTR(r).ghost->data);
+        break;
     }
 }
 
@@ -300,7 +303,7 @@ static void reap(struct naPool* p)
 
     // allocs of this type until the next collection
     globals->allocCount += total/2;
-    
+
     // Allocate more if necessary (try to keep 25-50% of the objects
     // available)
     if(p->nfree < total/4) {
index 5a531d536d7d2a0cd0bccfb1c92a2323c8cbeb18..0a5c861597dfc85e08e1b3e2aaee40bc6a8e72ed 100644 (file)
@@ -143,10 +143,11 @@ naRef naNewGhost(naContext c, naGhostType* type, void* ptr)
     // ensure 'simple' ghost users don't see garbage for these fields
     type->get_member = 0;
     type->set_member = 0;
-    
+
     ghost = naNew(c, T_GHOST);
     PTR(ghost).ghost->gtype = type;
     PTR(ghost).ghost->ptr = ptr;
+    PTR(ghost).ghost->data = naNil();
     return ghost;
 }
 
@@ -155,6 +156,7 @@ naRef naNewGhost2(naContext c, naGhostType* t, void* ptr)
     naRef ghost = naNew(c, T_GHOST);
     PTR(ghost).ghost->gtype = t;
     PTR(ghost).ghost->ptr = ptr;
+    PTR(ghost).ghost->data = naNil();
     return ghost;
 }
 
@@ -170,9 +172,21 @@ void* naGhost_ptr(naRef ghost)
     return PTR(ghost).ghost->ptr;
 }
 
+void naGhost_setData(naRef ghost, naRef data)
+{
+    if(IS_GHOST(ghost))
+        PTR(ghost).ghost->data = data;
+}
+
+naRef naGhost_data(naRef ghost)
+{
+    if(!IS_GHOST(ghost)) return naNil();
+    return PTR(ghost).ghost->data;
+}
+
 naRef naNil()
 {
-    naRef r; 
+    naRef r;
     SETPTR(r, 0);
     return r;
 }
index 832724471ffdd3e340945008d84204500dfde976..8b6b2c92866e5825dede03f9ba8c7fa9e34b41fe 100644 (file)
@@ -281,6 +281,16 @@ naRef        naNewGhost(naContext c, naGhostType* t, void* ghost);
 naRef        naNewGhost2(naContext c, naGhostType* t, void* ghost);
 naGhostType* naGhost_type(naRef ghost);
 void*        naGhost_ptr(naRef ghost);
+/**
+ * Attach a nasal object to the given ghost. Binds the lifetime of @a data to
+ * the lifetime of the @a ghost.
+ */
+void         naGhost_setData(naRef ghost, naRef data);
+/**
+ * Retrieve the object attached to the @a ghost, previously set with
+ * naGhost_setData().
+ */
+naRef        naGhost_data(naRef ghost);
 int          naIsGhost(naRef r);
 
 // Acquires a "modification lock" on a context, allowing the C code to