Вызов lua_CFunction из С++

Страницы: 1
RSS
Вызов lua_CFunction из С++
 
Я пытаюсь запустить из С++ функцию, ссылку на которую возвращает другая функция.
Код
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;

Источник данных не закрывается. Может, кто сталкивался с подобной проблемой? В чём ошибка?
 
Собственно, почему не работает, стало немного понятно. Тут нужно работать со всей метатаблицей, т.к. её методы нуждаются в ссылке не конкретный экземпляр источника данных. Но, как это сделать, ещё не ясно. Можно ли функции "Close" как нибудь подсунуть "_DataSource" - "userdata" из таблицы?
 
Цитата
dj.lexus написал:
Собственно, почему не работает, стало немного понятно. Тут нужно работать со всей метатаблицей, т.к. её методы нуждаются в ссылке не конкретный экземпляр источника данных. Но, как это сделать, ещё не ясно. Можно ли функции "Close" как нибудь подсунуть "_DataSource" - "userdata" из таблицы?
Может, не мучиться, а написать этот фрагмент на lua и всунуть его вовнутрь вашей длл?
www.bot4sale.ru

Пасхалочка для Алексея Иванникова: https://forum.quik.ru/messages/forum10/message63088/topic7052/#message63088
 
Цитата
s_mike@rambler.ru написал:
Цитата
dj.lexus   написал:
Собственно, почему не работает, стало немного понятно. Тут нужно работать со всей метатаблицей, т.к. её методы нуждаются в ссылке не конкретный экземпляр источника данных. Но, как это сделать, ещё не ясно. Можно ли функции "Close" как нибудь подсунуть "_DataSource" - "userdata" из таблицы?
Может, не мучиться, а написать этот фрагмент на lua и всунуть его вовнутрь вашей длл?
Да, это тоже выход. А как кусок LUA кода вставить в DLL?
 
google:   bin2c
www.bot4sale.ru

Пасхалочка для Алексея Иванникова: https://forum.quik.ru/messages/forum10/message63088/topic7052/#message63088
 
Спасибо! Оставлю этот вариант на случай, если не получится никак иначе.

Вроде бы, простая задача: в стеке лежит указатель на таблицу. В таблице есть функция. Осталось её выполнить.
 
а еще проще не вставлять  в DLL, а поместить в скрипт и вызывать из dll
 
а еще лучше , не делать на Си то, что можно сделать на луа
 
Да, я согласен, что во многих случаях можно обойтись возможностями LUA. Но, у меня задача - передавать данные в приложение на С. Поэтому, решено было оставить скрипт LUA пустым и все функции реализовать в DLL.
 
Я тоже передаю данные и туда и обратно, но это не мешает использовать и си функции и луа.
Из опыта могу сказать что на Си надо  делать то, что нет в луа
например работу с различными форматами, потоками памятью.
 
Цитата
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 - помогу перевести на С++.
 
Цитата
Вячеслав + написал:
Напишите, что Вы хотите выполнить на 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
 
dj.lexus,
Код
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;
}
 
  • Перед "return 0;" на стеке останутся лежать DS и С ( который = DS:C(1) ).
  • Можно стек не чистить, если myfunction вызывалась из Lua-кода.
  • Если планируете на стеке хранить много аргументов ( > LUA_MINSTACK), то гляньте документацию к lua_checkstack.
 
Цитата
Вячеслав + написал:
dj.lexus  ,
Код
  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 существует, пока указатель на неё хранится в стеке?
 
Цитата
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 выглядел лаконичней и чуть производительней.
 
Цитата
Вячеслав + написал:
Рекомендую к прочтению книгу "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"
 
Цитата
dj.lexus написал:
Получается, нужно знать индекс элемента, который требуется положить в стек. Как же сохранить ссылку на таблицу, чтобы она "не потерялась" до конца работы программы? Или не перепуталась со ссылкой на другой источник данных.
Если между вызовами Си функций из Lua, то можно сохранить это значение в LUA_REGISTRYINDEX - это специальная таблица, видимая только из Си и хранящая важную информацию (удалять из неё что-то не своё точно не надо). См. API
  • idx = luaL_ref(L, LUA_REGISTRYINDEX)
  • luaL_unref(L, LUA_REGISTRYINDEX, idx)
  • lua_rawgeti(L, LUA_REGISTRYINDEX, idx)
Цитата
dj.lexus написал:
говорит "table"
Значит таблица.
 
А idx уже можно хранить в глобальной переменной в DLL, т. к. это int.
 
Кстати, учтите, если Вы будете вызывать Ваши Си функции из разных потоков - поток main и поток callback'ов - то Вам нужно будет обеспечивать синхронный доступ к Вашим глобальным переменным. Можно с помощью std::mutex / std::recursive_mutex.
 
Цитата
dj.lexus написал:
Как же сохранить ссылку на таблицу, чтобы она "не потерялась" до конца работы программы?
Также можно хранить ссылку в глобальной переменной Lua - см. lua_setglobal(L, "myDS");
 
Цитата
Вячеслав + написал:
Если между вызовами Си функций из 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'и выполняются в одном потоке?
 
Цитата
dj.lexus написал:
Цитата
Вячеслав +   написал:
Если между вызовами Си функций из 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. Не знаю для чего так сделано, но уж сделано.
 
Цитата
Николай Камынин написал:
Стек в луа - это просто область памяти, указатель на которую находится по адресу L->base
При вталкивании в стек данные записываются по адресу, который записан в L->top
Сначала там записан L->base, после каждого заталкивания L->top=L->top+1
При выталкивание происходит обратное действие.
При этом происходит контроль границ стека и если адрес выходит за границы выдается сообщение луа.
-------------------------------
Таким образом, КВИК падать не может из-за операций со стеком.
----------------------------
Если у Вас квик падает без сообщений, то ошибка в Вашем коде CИ, которай приводит к фатальной ошибке, которую никто не обрабатывает. Возможно Вы в своей программе куда-то не туда чего-то пишите.
--------------------------------
Все колбеки выполняются в одном потоке.
Но вот открытие источников надо делать в main. Не знаю для чего так сделано, но уж сделано.
Спасибо за разъяснения, проверю свой код. Да, ошибки не обрабатываю, нужно это добавить.
Источник открываю в main, пока всё работает.
 
dj.lexus,
Читайте внимательно документацию. У Вас сейчас есть всё для того (и книга и google), чтобы писать правильный код.
Также отладочной печатью в контрольных точках можно понять, после какой операции "падает" программа, а с помощью отладочной печати значений lua_gettop(L) - что происходит со стеком Lua во время выполнения.
Или наймите программиста / консультанта, который сделает это за Вас.

Перечитайте ещё раз документацию к luaL_ref.
Страницы: 1
Читают тему
Наверх