2014-07-24 73 views
4

我正在爲物理引擎編寫Ruby擴展。這個物理引擎有機構鏈接到一個世界,所以我的Ruby對象是WorldBody。一個身體構建(用C++)world->CreateBody和銷燬與world->DestroyBodyData_Wrap_Struct和銷燬命令

問題是Ruby GC正在破壞身體之前的世界。所以,當GC摧毀身體時,世界不再存在,我得到了一個分割錯誤。我知道我需要在GC的某處標記某些東西(使用rb_gc_mark),但我不知道在哪裏。


World類是非常標準的,它看起來像這樣:

extern "C" void world_free(void *w) 
{ 
    static_cast<World*>(w)->~World(); 
    ruby_xfree(w); 
} 

extern "C" void world_mark(void *w) 
{ 
    // ??? 
} 

extern "C" VALUE world_alloc(VALUE klass) 
{ 
    return Data_Wrap_Struct(klass, world_mark, world_free, ruby_xmalloc(sizeof(World))); 
} 

extern "C" VALUE world_initialize(VALUE self) 
{ 
    World* w; 
    Data_Get_Struct(self, World, w); 
    new (w) World(); 
    return self; 
} 

Body類是有一點不同,因爲它需要從世界對象創建(我不能簡單地它)。所以它看起來像這樣:

extern "C" void body_free(void* b) 
{ 
    Body* body = static_cast<Body*>(b); 
    World* world = body->GetWorld(); 
    world->DestroyBody(body); 
} 

extern "C" void body_mark(void* b) 
{ 
    // ??? 
} 

extern "C" VALUE body_alloc(VALUE klass) 
{ 
    return Data_Wrap_Struct(klass, body_mark, body_free, 0); 
} 

extern "C" VALUE static_obj_initialize(VALUE self, VALUE world) 
{ 
    Body* b; 
    World* w; 

    Data_Get_Struct(self, Body, b); 
    Data_Get_Struct(world, World, w); 

    b = w->CreateBody(); 
    DATA_PTR(self) = b; 

    return self; 
} 

所以我的問題是:

  1. 這兩個對象,我應該在GC標記的?
  2. 我該怎麼做?我是否只需標記rb_gc_mark,還是應該在某些情況下才做?哪個?
  3. 我應該做什麼?標記函數僅接收到一個指向我的結構的裸指針,但函數rb_gc_mark需要VALUE
+2

我不認爲'rb_gc_mark'會在這裏幫助 - 如果整個世界主體結構變得無法訪問,那麼就沒有辦法(據我所知)來控制自由函數被調用的順序。問題是釋放一個'Body'的函數取決於相應的'World'的存在。它看起來像庫的C++ API沒有乾淨地映射到一個Ruby API,其中'Worlds'和'Body's是獨立的對象 - 您可能需要重新考慮如何創建和處理對象以及它們的生命週期。 – matt

+0

我明白了。問題在於對象在外部庫中 - 也就是說,我無法控制它。 –

回答

1

你應該能夠註冊爲忙碌綁的Body所有情況下,依賴於它的生命週期WorldVALUE

這意味着,在創建時,你會想要做這樣的事情:

extern "C" VALUE static_obj_initialize(VALUE self, VALUE world) 
{ 
    Body* b; 
    World* w; 

    Data_Get_Struct(self, Body, b); 
    Data_Get_Struct(world, World, w); 

    b = w->CreateBody(); 
    DATA_PTR(self) = b; 
    rb_gc_register_address(&world); 

    return self; 
} 

的你想要註銷時Body被破壞:

extern "C" void body_free(void* b) 
{ 
    Body* body = static_cast<Body*>(b); 
    World* world = body->GetWorld(); 
    world->DestroyBody(body); 
    rb_gc_unregister_address(&world); //almost right 
} 

這不太準確,但是,因爲rb_gc_unregister_address()想要一個VALUE *。你應該可以寫一個簡單的包裝器(或使用std :: pair <>),它將攜帶世界VALUE

警告:我沒有測試任何上述代碼,但它應該是方向正確的。