Kot_Begemot
Kot_Begemot личный блог
07 апреля 2020, 07:53

Интеграция Lua и С++ (2)


Обмен данными между Lua и Сpp осуществляется через Lua-стэк, то есть через специальным образом структурированное (по принципу Last In — First Out) пространство. 


Интеграция Lua и С++ (2)

Иллюстрация процесса добавления переменных в Cтэк (Push) и извлечения переменных из Стэка (Pop).

Иными словами, Lua стэк — это одномерный массив переменных (список, строка) с прямой (от 1 до n) индексацией.



Заполняется стэк командами lua_push (С-side) :

void lua_pushnumber (lua_State *L, lua_Number n);
const char *lua_pushstring (lua_State *L,  const char *s);

и другими. 


Новой переменной в стэке Луа длинной n автоматически присваивается индекс [n+1] или [-1], где n+1 — абсолютный индекс переменной, а -1 — индекс новой переменной относительно конца (!) стэка. 




Доступ, к переменным, соответственно осуществляется функциями lua_to (C-side) :

lua_Number lua_tonumber (lua_State *L, int index);
const char *lua_tostring (lua_State *L, int index);
где L — указатель Lua-стэка, а index — абсолютный или относительный индекс переменной в стэке.

Справочное руководство по Lua API.





Функции С, предназначенные для использования Lua, определяются как :

static int Func(Lua State *L)
{

lua_push...

return(N);
}

где N — число переменных, находящихся в конце (на вершине) стэка Lua и поддерживающие параллельное присвоение в Lua.

Так, например, если стэк содержит 10 переменных, то return(2) вернет 9-ую и 10-ую переменную в Lua.

Lua-side:
CppDLL=require('CppDLL');
Var9,Var10 = CppDLL.Func();

Справка по регистрации функций в языке С++, для вызова их со стороны Lua.




Рассмотрим пример передачи 2D массива типа Double из С++ в Lua :

        lua_settop(L,0); // Очищаем Lua стэк
        lua_createtable( L, row, 0 ); // создаем луа - таблицу (строки)
        for (i=0;i<row;i++)
         {
         lua_pushinteger(L,i+1);
         lua_createtable(L,col,0); // создаем луа-таблицу соответствующую i-ой строке
         for(j=0;j<col;j++) // записываем строку в цикле
            {
             lua_pushinteger(L,j+1); // задаем индекс j-ого элемента i-ой строки
             lua_pushnumber( L, arr[i+j*row] ); // задаем значение j-ого элемена i-ой строки
             //lua_settable(L, -3); // записываем элемент [i][j] в строку i и очищаем пару [индекс j, значение] из стэка LUA
             lua_rawset(L, -3); // записываем элемент [i][j] в строку i и очищаем пару [индекс j, значение] из стэка LUA
             }
         //lua_settable(L,-3); // добавляем строку i к существующей Lua таблице и отчищаем пару [индеск строки, строка в Lua стэке]
         lua_rawset(L,-3); // добавляем строку i к существующей Lua таблице и отчищаем пару [индеск строки, строка в Lua стэке]
         }

return(1); // Возвращаем в LUA таблицу, записанную в стэке под индексом 2


Поскольку Lua не поддерживает 2D массивы, то матрицы задаются в качестве массива массивов (строки, элементы которых тоже строки), для чего нам приходится реализовывать связку :

lua_createtable ( указатель стэка, число элементов, 0);

Цикл записи элементов 
  {
     lua_push integer (указатель стэка, индекс нового элемента в строке);
     lua_pushnumber ( указатель стэка, значение нового элемента в строке);
     lua_rawset( указатель стэка, индекс строки в стэке Lua); *
  }


во вложенном цикле.

* Функция rawset добавляет в указанную индексом строку элемент под индексом, определенным предпоследним в стэке и со значением, определенным последним в стэке. После чего удаляет пару [индекс, значение] из стэка, освобождая его. 







Аналогичным образом осуществляется чтение матрицы Lua со стороны Cpp :


Узнаем размеры матрицы :

int row=lua_objlen(L,-1); // Узнаем число строк в таблице
lua_pushnumber(L,1); lua_rawget(L,2); // выделяем 1-ую строку из таблицы
int col=lua_objlen(L,3); // Узнаем число столбцов в таблице     

В данном примере под индексом [1] в стэке находится имя таблицы, [2] — cама таблица, [3] — индекс строки таблицы до вызова rawget, строка таблицы после вызова rawget.  

Считываем матрицу Lua в массив C++ «ptr» :

for (i=0;i<row;i++) // пробегаем по строкам
          {
           lua_settop(L,2); // очищаем побочный стэк
           lua_pushnumber(L,i+1); // номер строки к загрузке
           lua_rawget(L,2); // Загрузка строки в переменную 3

           for (j=0;j<col;j++) // пробегаем по столбцам
             {
               lua_pushnumber(L,j+1); // номер столбца к загрузке
               lua_rawget(L,3); // Загрузка элемента [i][j] в переменную 4
               ptr[i+j*row]=lua_tonumber(L,4); //Записываем значения из Lua в Матлаб
               lua_settop(L,3); //Убираем лишние переменные из стэка
             }
          }


Демонстрация работы программы, считывающей массивы строк и чисел из Cpp в Lua под именами «ACell», «ADouble» и возвращающей в Cpp те же массивы под именами «BCell», «BDouble»:


Интеграция Lua и С++ (2)
Интеграция Lua и С++ (2)


Предложения и критика приветствуются.
Торгуйте алгоритмами.
С уважением, Кот-Бегемот.

19 Комментариев
  • Бабло ахаха, что ты ...
    07 апреля 2020, 10:21
    Для одного интерпретируемого языка программирования нужен еще один интерпретируемый язык: Жопа гора или на горе жопа.
  • Сергей Симонов
    07 апреля 2020, 14:34
    ИМХО, QLUA можно подружить с любой приблудой через файловый ввод-вывод. Это дешево, быстро, просто и сердито. Цените свое время, друзья…
  • meat
    07 апреля 2020, 17:21
    а почему нельзя сразу на си писать программы?
    • Dmitryy
      07 апреля 2020, 18:18
      meat, как? Плаза, денег стоит. Если торговля уже отлажена, то можно себе позволить, а для экспериментов и не HFT вполне рабочий вариант. Я пользуюсь подобным, только C# + QuikSharp.
      • meat
        07 апреля 2020, 18:27
        Dmitryy, мне кажется то, что не hft можно и внутри quik написать на lua




        • Dmitryy
          07 апреля 2020, 18:39
          meat, ну да… но это боль)
          • meat
            07 апреля 2020, 22:53
            Dmitryy, не сложнее чем на js писать наверное, а в чем боль?
            • Dmitryy
              07 апреля 2020, 23:09
              meat, может и так, для тех кто его знает.
  • band
    07 апреля 2020, 21:11
    где i1,i2,… — индексы выходных переменных (начиная с 0), поддерживающие параллельное присвоение в Lua.
    не индексы а кол-во переменных которые возвращает функция и которые лежат на вершине стека. соответственно далее правим
    return(1); // Возвращаем в LUA таблицу, записанную в стэке под индексом 2
    
    возвращает кол-во элементов, один, таблицу, лежащую на вершине стека
    В данном примере под индексом [1] в стэке находится имя таблицы, [2] — cама таблица, [3] — индекс строки таблицы до вызова rawget, строка таблицы после вызова rawget. 
    сначала на стеке только таблица(а не ее имя). но можно и по имени ее получить дополнив вызовом lua_getfield, чтобы получить уже таблицу(или что там будет лежать по имени которое передаем.

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

    и еще пример последний стоит заменить на что-нить использующее описанный выше него функционал а не матлабовский скрипт. чтобы могли сразу попробовать то что изучили. мол вот так все легко.

    на офф сайте есть шикарная документация по функциям и что они делают со стеком. www.lua.org/manual/5.1/manual.html

  • chizhan
    08 апреля 2020, 05:12
    А через DDE работать уже муветон? Указали бы для приличия, плюсы-минусы всех подключений. Не всем нравится тратить время, чтобы освоить еще один псевдоязык(луа)

    Впрочем, было время (а может и сейчас можно), когда данные можно было напрямую из виртуальной памяти квика высчитывать. На древних версиях без проблем было, сейчас возможно шифруют. Скорость была бешенной.
      • chizhan
        08 апреля 2020, 06:29
        Kot_Begemot, DDE для получения данных. Все что можно вывести в Excel, можно вывести в свою программу. Но стаканы RI/Si не все передаются, т.к. торможение дикое. Для приказов и прочего обычный Quik API c его dll.

          • chizhan
            08 апреля 2020, 08:50
            Kot_Begemot, dll-ка становится частью вашей программы, из нее доступ к квику на уровне функций: установка/отмена ордеров и считывание ваших сделок.
          • 3Qu
            08 апреля 2020, 14:05
            Kot_Begemot, DLL для работы с заявками-сделками называется Trans2Quik.dll.
            Скачать ее и мануал можно с сайта ARQA.
            Хотя, с заявками сделками можно и непосредственно через Луа работать.

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн