Я пытаюсь запустить из С++ функцию, ссылку на которую возвращает другая функция.
Код
lua_CFunction CloseHandle;
//Создаём источник данных
lua_getglobal(L, "CreateDataSource");
lua_pushstring(L, class_code); //class_code
lua_pushstring(L, sec_code); //sec_code
lua_pushinteger(L, interval); //interval
lua_call(L, 3, 2);
if(lua_istable(L,1))
{
lua_pushnil(L);
while (lua_next(L, 1) != 0)
{
if(strcmp(lua_tostring(L, -2), "Close") == 0) //Проверяем название поля
{
CloseHandle = lua_tocfunction(L, -1);
}
lua_pop(L, 1);
}
}
bool err = lua_isstring(L,2);
//Закрываем источник данных
if(CloseHandle)
{
lua_pushcfunction(L, CloseHandle);
lua_call (L, 0, 1);
if(lua_isboolean(L, 1))
{
result = lua_toboolean(L, 1)
}
}
return result;
Источник данных не закрывается. Может, кто сталкивался с подобной проблемой? В чём ошибка?
Пользователь
Сообщений: Регистрация: 24.03.2016
24.03.2016 11:38:44
Собственно, почему не работает, стало немного понятно. Тут нужно работать со всей метатаблицей, т.к. её методы нуждаются в ссылке не конкретный экземпляр источника данных. Но, как это сделать, ещё не ясно. Можно ли функции "Close" как нибудь подсунуть "_DataSource" - "userdata" из таблицы?
Пользователь
Сообщений: Регистрация: 30.01.2015
24.03.2016 13:02:07
Цитата
dj.lexus написал: Собственно, почему не работает, стало немного понятно. Тут нужно работать со всей метатаблицей, т.к. её методы нуждаются в ссылке не конкретный экземпляр источника данных. Но, как это сделать, ещё не ясно. Можно ли функции "Close" как нибудь подсунуть "_DataSource" - "userdata" из таблицы?
Может, не мучиться, а написать этот фрагмент на lua и всунуть его вовнутрь вашей длл?
Пасхалочка для Алексея Иванникова:
Пользователь
Сообщений: Регистрация: 24.03.2016
24.03.2016 13:52:17
Цитата
написал:
Цитата
dj.lexus написал: Собственно, почему не работает, стало немного понятно. Тут нужно работать со всей метатаблицей, т.к. её методы нуждаются в ссылке не конкретный экземпляр источника данных. Но, как это сделать, ещё не ясно. Можно ли функции "Close" как нибудь подсунуть "_DataSource" - "userdata" из таблицы?
Может, не мучиться, а написать этот фрагмент на lua и всунуть его вовнутрь вашей длл?
Да, это тоже выход. А как кусок LUA кода вставить в DLL?
Пользователь
Сообщений: Регистрация: 30.01.2015
24.03.2016 14:04:16
google: bin2c
Пасхалочка для Алексея Иванникова:
Пользователь
Сообщений: Регистрация: 24.03.2016
24.03.2016 14:31:44
Спасибо! Оставлю этот вариант на случай, если не получится никак иначе.
Вроде бы, простая задача: в стеке лежит указатель на таблицу. В таблице есть функция. Осталось её выполнить.
Пользователь
Сообщений: Регистрация: 30.01.2015
24.03.2016 14:36:27
а еще проще не вставлять в DLL, а поместить в скрипт и вызывать из dll
Пользователь
Сообщений: Регистрация: 30.01.2015
24.03.2016 14:37:31
а еще лучше , не делать на Си то, что можно сделать на луа
Пользователь
Сообщений: Регистрация: 24.03.2016
24.03.2016 14:46:58
Да, я согласен, что во многих случаях можно обойтись возможностями LUA. Но, у меня задача - передавать данные в приложение на С. Поэтому, решено было оставить скрипт LUA пустым и все функции реализовать в DLL.
Пользователь
Сообщений: Регистрация: 30.01.2015
24.03.2016 16:30:18
Я тоже передаю данные и туда и обратно, но это не мешает использовать и си функции и луа. Из опыта могу сказать что на Си надо делать то, что нет в луа например работу с различными форматами, потоками памятью.
Пользователь
Сообщений: Регистрация: 27.01.2016
24.03.2016 16:47:11
Цитата
dj.lexus написал: Я пытаюсь запустить из С++ функцию, ссылку на которую возвращает другая функция.
Код
lua_CFunction Close Handle;
//Создаём источник данных
lua_getglobal(L, "CreateDataSource" );
lua_pushstring(L, class_code); //class_code
lua_pushstring(L, sec_code); //sec_code
lua_pushinteger(L, interval); //interval
lua_call(L, 3 , 2 );
if (lua_istable(L, 1 ))
{
lua_push nil (L);
while (lua_next(L, 1 ) ! = 0 )
{
if (strcmp(lua_tostring(L, - 2 ), "Close" ) = = 0 ) //Проверяем название поля
{
Close Handle = lua_tocfunction(L, - 1 );
}
lua_pop(L, 1 );
}
}
bool err = lua_isstring(L, 2 );
//Закрываем источник данных
if ( Close Handle)
{
lua_pushcfunction(L, Close Handle);
lua_call (L, 0 , 1 );
if (lua_isboolean(L, 1 ))
{
result = lua_toboolean(L, 1 )
}
}
return result;
Источник данных не закрывается. Может, кто сталкивался с подобной проблемой? В чём ошибка?
Напишите, что Вы хотите выполнить на Lua - помогу перевести на С++.
Пользователь
Сообщений: Регистрация: 24.03.2016
24.03.2016 18:56:22
Цитата
Вячеслав + написал: Напишите, что Вы хотите выполнить на Lua - помогу перевести на С++.
Я пытаюсь сделать следующее:
Код
DS = CreateDataSource("SPBFUT", "RIM6", INTERVAL_M1); // Работает
...
C = DS:C(1); // Не получается
...
DS:Close(); // Не получается
Таблица DS содержит всё, как должно быть: C - function Close - function _DataSource - userdata T - function V - function SetUpdateCallback - function H - function SetEmptyCallback - function L - function O - function Size - function
int myfunction(lua_State* L) {
// DS = CreateDataSource("SPBFUT", "RIM6", INTERVAL_M1); // Работает
lua_getglobal(L, "CreateDataSource");
lua_pushstring(L, "SPBFUT");
lua_pushstring(L, "RIM6");
lua_getglobal(L, "INTERVAL_M1");
lua_call(L, 3, 1);
// результат на стеке
int DS = lua_gettop(L);
// C = DS:C(1); // Не получается
lua_getfield(L, DS, "C");
lua_pushvalue(L, DS);
lua_pushnumber(L, 1);
lua_call(L, 2, 1);
// результат на стеке
int C = lua_gettop(L);
// DS:Close(); // Не получается
lua_getfield(L, DS, "Close");
lua_pushvalue(L, DS);
lua_call(L, 1, 0);
// В Lua
// obj:func(param);
// это то же самое что
// obj.func(obj, param);
// как-то так
return 0;
}
Пользователь
Сообщений: Регистрация: 27.01.2016
24.03.2016 20:03:40
Перед "return 0;" на стеке останутся лежать DS и С ( который = DS:C(1) ).
Можно стек не чистить, если myfunction вызывалась из Lua-кода.
Если планируете на стеке хранить много аргументов ( > LUA_MINSTACK), то гляньте документацию к lua_checkstack.
int myfunction(lua_State * L) {
// DS = CreateDataSource ( "SPBFUT" , "RIM6" , INTERVAL_M1); // Работает
lua_getglobal(L, "CreateDataSource" );
lua_pushstring(L, "SPBFUT" );
lua_pushstring(L, "RIM6" );
lua_getglobal(L, "INTERVAL_M1" );
lua_call(L, 3 , 1 );
// результат на стеке
int DS = lua_gettop(L);
// C = DS:C( 1 ); // Не получается
lua_getfield(L, DS, "C" );
lua_pushvalue(L, DS);
lua_pushnumber(L, 1 );
lua_call(L, 2 , 1 );
// результат на стеке
int C = lua_gettop(L);
// DS: Close (); // Не получается
lua_getfield(L, DS, "Close" );
lua_pushvalue(L, DS);
lua_call(L, 1 , 0 );
// В Lua
// obj:func(param);
// это то же самое что
// obj.func (obj, param);
// как - то так
return 0 ;
}
Спасибо, заработало! Видимо, я не до конца понимаю организацию стека Lua. Правильно ли я понял, что таблица DS существует, пока указатель на неё хранится в стеке?
Пользователь
Сообщений: Регистрация: 27.01.2016
24.03.2016 21:35:50
Цитата
dj.lexus написал: Видимо, я не до конца понимаю организацию стека Lua.
Рекомендую к прочтению книгу "Roberto Ierusalimschy - Programming in Lua - 2013". В своё время потратил на неё неделю, и не жалею.
Цитата
dj.lexus написал: Правильно ли я понял, что таблица DS существует, пока указатель на неё хранится в стеке?
Немного подкорректирую Ваш вопрос:
Цитата
Правильно ли я понял, что значение DS существует, пока оно хранится в стеке?
Да. Это правильно.
Само значение, кроме того, можно скопировать (например, в качестве значения одного из ключей другой таблицы; lua_pushvalue как раз копирует значение). Таблицы на стеке хранятся в виде ссылок на содержимое таблиц, т.е. при таком копировании сами элементы таблицы не копируются - копируется только ссылка на таблицу.
За освобождение памяти выделенной под Lua-таблицы, userdata и строки отвечает сборщик мусора. Освобожение памяти обычно происходит позже удаления всех ссылкок на таблицу / userdata / string.
Кстати, а Вы уверены, что DS - это таблица? Что говорит
Код
message(type(DS),1);
?
Если DS - это таблица, то QLUA API в этом месте сделано немного кривовато, т. к. DS можно бы было сразу сделать userdata, чтобы Си код обработки методов DS выглядел лаконичней и чуть производительней.
Пользователь
Сообщений: Регистрация: 24.03.2016
24.03.2016 21:51:42
Цитата
Вячеслав + написал: Рекомендую к прочтению книгу "Roberto Ierusalimschy - Programming in Lua - 2013". В своё время потратил на неё неделю, и не жалею.
Спасибо за Ваш совет, книга лежит, ждёт прочтения.
Цитата
Вячеслав + написал: lua_pushvalue как раз копирует значение
Нашёл описание: void lua_pushvalue (lua_State *L, int index); Pushes a copy of the element at the given valid index onto the stack.
Получается, нужно знать индекс элемента, который требуется положить в стек. Как же сохранить ссылку на таблицу, чтобы она "не потерялась" до конца работы программы? Или не перепуталась со ссылкой на другой источник данных.
Код
message(type(DS),1);
говорит "table"
Пользователь
Сообщений: Регистрация: 27.01.2016
24.03.2016 21:57:12
Цитата
dj.lexus написал: Получается, нужно знать индекс элемента, который требуется положить в стек. Как же сохранить ссылку на таблицу, чтобы она "не потерялась" до конца работы программы? Или не перепуталась со ссылкой на другой источник данных.
Если между вызовами Си функций из Lua, то можно сохранить это значение в LUA_REGISTRYINDEX - это специальная таблица, видимая только из Си и хранящая важную информацию (удалять из неё что-то не своё точно не надо). См. API
А idx уже можно хранить в глобальной переменной в DLL, т. к. это int.
Пользователь
Сообщений: Регистрация: 27.01.2016
24.03.2016 22:00:50
Кстати, учтите, если Вы будете вызывать Ваши Си функции из разных потоков - поток main и поток callback'ов - то Вам нужно будет обеспечивать синхронный доступ к Вашим глобальным переменным. Можно с помощью std::mutex / std::recursive_mutex.
Пользователь
Сообщений: Регистрация: 27.01.2016
24.03.2016 22:02:40
Цитата
dj.lexus написал: Как же сохранить ссылку на таблицу, чтобы она "не потерялась" до конца работы программы?
Также можно хранить ссылку в глобальной переменной Lua - см. lua_setglobal(L, "myDS");
Пользователь
Сообщений: Регистрация: 24.03.2016
25.03.2016 06:51:19
Цитата
Вячеслав + написал: Если между вызовами Си функций из Lua, то можно сохранить это значение в LUA_REGISTRYINDEX - это специальная таблица, видимая только из Си и хранящая важную информацию (удалять из неё что-то не своё точно не надо). См. API idx = luaL_ref(L, LUA_REGISTRYINDEX) luaL_unref(L, LUA_REGISTRYINDEX, idx) lua_rawgeti(L, LUA_REGISTRYINDEX, idx)
Спасибо, эти функции у меня в коде есть. Меня насторожило то, что если сохранить ссылку
Код
idx = luaL_ref(L, LUA_REGISTRYINDEX) ,
потом вытолкнуть её из стека
Код
lua_pop(L, 1),
затем попытаться вернуть
Код
lua_rawgeti(L, LUA_REGISTRYINDEX, idx) ,
то Quik падает при выполнении таких операций.
Видимо, при удалении единственной ссылки на таблицу, Lua удаляет её содержимое.
Видимо, выходом в такой ситуации будет, как Вы писали:
Код
lua_setglobal(L, "myDS");
Правда, в этом случае, придётся склеивать индивидуальное символьное имя для каждого источника данных.
Про разные потоки callback'ов и main'a знаю, но про синхронизацию доступа пока не думал. Нужно будет это учесть.
Вячеслав + написал: Если между вызовами Си функций из Lua, то можно сохранить это значение в LUA_REGISTRYINDEX - это специальная таблица, видимая только из Си и хранящая важную информацию (удалять из неё что-то не своё точно не надо). См. API idx = luaL_ref(L, LUA_REGISTRYINDEX) luaL_unref(L, LUA_REGISTRYINDEX, idx) lua_rawgeti(L, LUA_REGISTRYINDEX, idx)
Спасибо, эти функции у меня в коде есть. Меня насторожило то, что если сохранить ссылку
Код
idx = luaL_ref(L, LUA_REGISTRYINDEX) ,
потом вытолкнуть её из стека
Код
lua_pop(L, 1 ),
затем попытаться вернуть
Код
lua_rawgeti(L, LUA_REGISTRYINDEX, idx) ,
то Quik падает при выполнении таких операций.
Видимо, при удалении единственной ссылки на таблицу, Lua удаляет её содержимое.
Видимо, выходом в такой ситуации будет, как Вы писали:
Код
lua_setglobal(L, "myDS" );
Правда, в этом случае, придётся склеивать индивидуальное символьное имя для каждого источника данных.
Про разные потоки callback'ов и main'a знаю, но про синхронизацию доступа пока не думал. Нужно будет это учесть.
А все callback'и выполняются в одном потоке?
Стек в луа - это просто область памяти, указатель на которую находится по адресу L->base При вталкивании в стек данные записываются по адресу, который записан в L->top Сначала там записан L->base, после каждого заталкивания L->top=L->top+1 При выталкивание происходит обратное действие. При этом происходит контроль границ стека и если адрес выходит за границы выдается сообщение луа. ------------------------------- Таким образом, КВИК падать не может из-за операций со стеком. ---------------------------- Если у Вас квик падает без сообщений, то ошибка в Вашем коде CИ, которай приводит к фатальной ошибке, которую никто не обрабатывает. Возможно Вы в своей программе куда-то не туда чего-то пишите. -------------------------------- Все колбеки выполняются в одном потоке. Но вот открытие источников надо делать в main. Не знаю для чего так сделано, но уж сделано.
Пользователь
Сообщений: Регистрация: 24.03.2016
25.03.2016 09:20:06
Цитата
Николай Камынин написал: Стек в луа - это просто область памяти, указатель на которую находится по адресу L->base При вталкивании в стек данные записываются по адресу, который записан в L->top Сначала там записан L->base, после каждого заталкивания L->top=L->top+1 При выталкивание происходит обратное действие. При этом происходит контроль границ стека и если адрес выходит за границы выдается сообщение луа. ------------------------------- Таким образом, КВИК падать не может из-за операций со стеком. ---------------------------- Если у Вас квик падает без сообщений, то ошибка в Вашем коде CИ, которай приводит к фатальной ошибке, которую никто не обрабатывает. Возможно Вы в своей программе куда-то не туда чего-то пишите. -------------------------------- Все колбеки выполняются в одном потоке. Но вот открытие источников надо делать в main. Не знаю для чего так сделано, но уж сделано.
Спасибо за разъяснения, проверю свой код. Да, ошибки не обрабатываю, нужно это добавить. Источник открываю в main, пока всё работает.
Пользователь
Сообщений: Регистрация: 27.01.2016
25.03.2016 12:47:51
dj.lexus, Читайте внимательно документацию. У Вас сейчас есть всё для того (и книга и google), чтобы писать правильный код. Также отладочной печатью в контрольных точках можно понять, после какой операции "падает" программа, а с помощью отладочной печати значений lua_gettop(L) - что происходит со стеком Lua во время выполнения. Или наймите программиста / консультанта, который сделает это за Вас.