Блог им. Kot_Begemot

Интеграция 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)


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

  • обсудить на форуме:
  • Quik Lua
★24
19 комментариев
Для одного интерпретируемого языка программирования нужен еще один интерпретируемый язык: Жопа гора или на горе жопа.
ИМХО, QLUA можно подружить с любой приблудой через файловый ввод-вывод. Это дешево, быстро, просто и сердито. Цените свое время, друзья…
а почему нельзя сразу на си писать программы?
avatar
meat, как? Плаза, денег стоит. Если торговля уже отлажена, то можно себе позволить, а для экспериментов и не HFT вполне рабочий вариант. Я пользуюсь подобным, только C# + QuikSharp.
avatar
Dmitryy, мне кажется то, что не hft можно и внутри quik написать на lua




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

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

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

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

avatar
band, 

проверил кодом:

static int test(lua_State *L)
{
lua_settop(L,0);
lua_pushnumber(L,0.1);
lua_pushinteger(L,1);
const char *a=«string»;
lua_pushstring(L,a);

return(2);
};


Вы правы — возвращает вершину стэка.
Текст поправил.
avatar
А через DDE работать уже муветон? Указали бы для приличия, плюсы-минусы всех подключений. Не всем нравится тратить время, чтобы освоить еще один псевдоязык(луа)

Впрочем, было время (а может и сейчас можно), когда данные можно было напрямую из виртуальной памяти квика высчитывать. На древних версиях без проблем было, сейчас возможно шифруют. Скорость была бешенной.
avatar
chizhan, никогда не работал с Quick DDE и даже не представляю его реализацию. Если его DDE позволяет принимать приказы на покупку/продажу, то это уже полноценный DDE. Но обычно, в торговых терминалах DDE реализуется по принципу одностороннего экспорта данных (и обычно крайне неудобного), поэтому в любом случае приходится изучать Lua,MQL и прочие языки.

avatar
Kot_Begemot, DDE для получения данных. Все что можно вывести в Excel, можно вывести в свою программу. Но стаканы RI/Si не все передаются, т.к. торможение дикое. Для приказов и прочего обычный Quik API c его dll.

avatar
chizhan, а Quick API как работает? Грузит приказ и CSV?
avatar
Kot_Begemot, dll-ка становится частью вашей программы, из нее доступ к квику на уровне функций: установка/отмена ордеров и считывание ваших сделок.
avatar
Kot_Begemot, DLL для работы с заявками-сделками называется Trans2Quik.dll.
Скачать ее и мануал можно с сайта ARQA.
Хотя, с заявками сделками можно и непосредственно через Луа работать.
avatar

теги блога Kot_Begemot

....все тэги



UPDONW
Новый дизайн