Как создать безопасный интерфейс C для LUA - PullRequest
0 голосов
/ 16 декабря 2011

Я изучаю, как интегрировать полную поддержку сценариев в мое приложение, но у меня возникла небольшая проблема, когда я планирую, чтобы мой C API был дружественным по отношению к LUA.

В основном я получил кучу структур, которые создаются через init и free функции как это:

[test.h]

typedef struct
{
char name[ 50 ];
} Test;


Test *TestAdd( char *name );

Test *TestDelete( Test *test );

[test.c]

Test *TestAdd( char *name )
{
Test *test = ( Test * ) calloc( 1, sizeof( Test ) );

strcpy( test->name, name );

return test;
}


Test *TestDelete( Test *test )
{
free( test );
return NULL;
}

Я использую swig для генерации модуля LUA, поэтому я создаю следующий файл интерфейса:

[test.i]

%module test
%{

%}

Test *TestAdd( char *name );

Test *TestDelete( Test * test );

Все работает нормально, если код пользователя такой:

a = test.TestAdd( "test" )
a = test.TestDelete( a )

if( a != nil ) print( a.name )

Но если код пользователя такой:

a = test.TestAdd( "test" )
test.TestDelete( a )

if( a != nil ) print( a.name ) -- Crash the app with bad_access (not just a LuaVM error).

Или даже хуже:

a = test.TestAdd( "test" )
test.TestDelete( a )
test.TestDelete( a )
-- Another way of making crash my app completely!

Есть ли какой-нибудь способ, которым я могу создать набор API в C, который бы избежал такого рода проблем и позволил бы пользователю безопасно добавлять / удалять и обращаться к свойствам безопасным способом, который не создавал бы ошибку "плохого доступа" и сбой всей программы, лучше всего, если LUAVM просто вернет ошибку и выполнение продолжится.

Я искал и пробовал другой подход к моему C API, чтобы избежать этой проблемы, но не смог ...

Кто-нибудь может мне помочь или дать несколько советов о том, как это сделать.

Заранее спасибо,

1 Ответ

5 голосов
/ 16 декабря 2011

Есть способ сделать это: прекратить экспорт интерфейсов в стиле C напрямую в Lua.C не является Lua, и вы никогда не должны стремиться к тому, чтобы программы Lua действовали как программы на Си.

Если нет способа избежать этого, код Lua никогда не должен освобождать что-либо.Если код Lua явно что-то создает, тогда код Lua должен иметь управление в течение всего времени жизни.Это означает, что вы используете сборщик мусора для удаления памяти: когда Lua GC завершает работу с ним, вы используете метаметод для освобождения указателя при любом вызове, который вы используете.

В общем, вы даете Lua указатель наобъект одним из двух способов: либо Lua владеет этим указателем, либо этот указатель относится к объекту, который переживет сам lua_State (и, следовательно, всегда будет доступен для сценария Lua).Бывают случаи, когда вы можете передать Lua какой-то временный объект, на который он может ссылаться, но его следует избегать, когда это возможно.

Вам нужно использовать директиву %newobject , чтобы сообщитьSWIG, что TestAdd возвращает указатель, который необходимо удалить.Затем вам нужно связать объект Test с TestDelete в качестве удалителя, с использованием% newfree typemap .Это правильно передаст право собственности на Test от C до Lua.В этот момент C никогда не удаляет его вручную.Просто позвольте Lua и SWIG выполнять свою работу.

Таким образом, TestDelete не должен подвергаться непосредственному воздействию Lua.Он будет вызываться неявно, когда GC обнаружит, что никто больше не ссылается на экземпляр Test.

...