1

Тема: Создание собственной DLL на C++ для LUA (в QUIK)

(Обновлено 09.09.2016 )

Написание собственных .dll-библиотек на С++ для подключения их к коду Lua (QLua) и вызов своих функций из этих библиотек

Здесь примеры будут рассматриваться на C++ (т.к. именно этот и только этот язык рассмотрен в документации LUA), но если у кого-то будет острое желание делать внешние модули на другом языке - пишите в комментариях. (Есть отдельная тема про Delphi / Lazarus.)

Скажу сразу: здесь не планируется и не будет исчерпывающего описания взаимодействия LUA и Си. Просто потому, что такого материала в интернете просто завались, ничего нового я не открою.
Однако хотелось бы привести описание шагов, позволяющих сделать «быстрый старт» в написании своей библиотеки, попутно указав на некоторые неочевидные нюансы настройки проекта.

Ссылки: официальная документация по сращиванию LUA и C, эта документация также доступна по-русски.

Начну в каком-то смысле «с конца».
Чтобы подключить внешнюю DLL библиотеку к LUA, в скрипте необходимо необходимо вписать строчку:

require("luacdll")

В результате выполнения этой строчки в LUA будет закружена библиотека с именем luacdll.dll (причем расширение в require не указывается), и из этой библиотеки будет прочитана информация об имеющихся в ней функциях, доступных из LUA (позже мы увидим каким именно образом).
Для простоты и надежности я очень советую положить скомпилированный dll-файл в тот же каталог, где расположен сам терминал QUIK, это проще и надежнее всего. Хотя можно настроить в LUA-скрипте содержимое переменной package.cpath до строки с require, если очень хочется.


Настройка C++ проекта

Итак, собственно переходим к C++.
Для его успешной сборки нам обязательно понадобятся следующие файлы из поставки LUA (полный собранный дистрибутив доступен на lua.org)

  • lauxlib.h

  • lua.h

  • luaconf.h

  • lua5.1.lib

Первые три файла содержат описание типов и прототипы интерфейсных функций LUA, четвертый - библиотека для статический линковки с внешней библиотекой интерпретатора lua5.1.dll.

Открываем MS Visual Studio, создаем новый проект DLL.
В свойствах проекта для всех конфигураций, какие мы будем собирать (обычно это Release и Debug), необходимо добавить библиотеку lua5.1.lib в дополнительные библиотеки. В приложенном примере она лежит в подпапке contrib:

Настройка свойств C++ проекта для сборки библиотеки, доступной из LUA

Открываем cpp-файл нашего проекта. Вначале добавляем в него заголовочные файлы LUA, причем перед их включением (это важно!) добавляем определение двух переменных препроцессора: они необходимы для случая сборки DLL, доступной из LUA. Если бы мы собирали наоборот LUA-интерпретатор, запускающий из себя LUA-скрипты, то необходимо было бы сделать другие определения.

#define LUA_LIB
#define LUA_BUILD_AS_DLL

extern "C" {
#include "../contrib/lauxlib.h"
#include "../contrib/lua.h"
}

Т.к. у нас C++ файл, то подключение заголовочных файлов LUA необходимо сделать под extern "C".

Вместо определения LUA_LIB и LUA_BUILD_AS_DLL через директиву #define, можно добавить их в строку Preprocessor Definitions в свойствах проекта (удобно, если проект содержит множество файлов):
Настройка свойств C++ проекта для сборки библиотеки, доступной из LUA

Еще одно соображение.
Чтобы у вас не возникло проблем с переносом и использованием вашей библиотеки на других компьютерах (если такая надобность есть), то есть смысл скомпилировать её со статическим run-time, т.е. весь код собственно библиотек C++ будет полностью внутри вашей DLL. Это несколько увеличит её размер, но кого сейчас волнует размер файла? Отвечу: никого. Зато вам не придётся дополнительно к вашей библиотеке таскать несколько системных DLL, или заставлять пользователей вашей устанавливать C++ Redistributable Package, причем определённой версии. Вы ведь не хотите создавать себе и другим лишние сложности? и правильно, поэтому статический run-time - это наш путь.
Чтобы его включить, необходимо сделать следующие настройки в свойствах проекта. Причем в зависимости от конфигурации следует выбрать верный тип run-time'а, иначе и debug будет не debug, и release не пойми что.

Для release-конфигураций выбираем:
https://quik2dde.ru/static-img/cpp_lua-2-03.png

Для debug-конфигураций выбираем:
https://quik2dde.ru/static-img/cpp_lua-2-04.png

(Немного подробнее про run-time библиотеки вот в этом сообщении.)


Теперь собственно код библиотеки на C++

Т.к. библиотеку мы назвали luacdll и имено это имя указываем в require, то при загрузке нашей библиотеки LUA-интерпретатор будет искать экспортируемую из нее функцию с определенным именем, в данном случае luaopen_luacdll(). Здесь  luaopen_ это предопределенный префикс (см. документацию), а luacdll собственно имя нашей библиотеки. Разумеется, тип и аргументы этой функции тоже предопределены.

 extern "C" LUALIB_API int luaopen_luacdll(lua_State *L) {
    luaL_openlib(L, "luacdll", ls_lib, 0);
    return 0;
}

Собственно все, что мы здесь делаем - это регистрируем в LUA-интерпретаторе (путем вызова luaL_openlib) те функции, которые мы предоставляем из нашей библиотеки, что делает их доступными для вызова из LUA-скриптов. Вторым параметром функции передается namespace, в котором будут доступны функции нашей библиотеки при вызове; чтобы не запутаться, namespace совпадает с именем библиотеки.

В нашей простейшей библиотеке будут реализованы 3 функции, доступные из LUA:

  • GetCurrentThreadId - получить ID текущего потока

  • MultTwoNumbers - перемножает 2 числа, заданных в качестве аргументов

  • MultAllNumbers - перемножает все числа, встретившиеся в аргументах

Сам список функций (имя и указатель на соответствующую Си-функцию) описан в константном массиве:

 static struct luaL_reg ls_lib[] = {
    {"GetCurrentThreadId", forLua_GetCurrentThreadId},
    {"MultTwoNumbers", forLua_MultTwoNumbers},
    {"MultAllNumbers", forLua_MultAllNumbers},
    {NULL, NULL}
}; 

Собственно реализация, например, функции, возвращающей ID текущего потока:

 static int forLua_GetCurrentThreadId(lua_State *L) {
    // возвращаем одно значение, полученное от Win API функции
    lua_pushinteger(L, GetCurrentThreadId());
    return(1);
}

Ее прототип предопределен и един для всех интерфейсных функций: принимает единственный параметр L - указатель на стек LUA (см. документацию).
В данном случае функция не подразумевает никаких аргументов и возвращает единственное целочисленное значение.

Исходный код остальных функций можно посмотреть в приложенном архиве, на мой взгляд, в особых комментариях он не нуждается. В реализации MultAllNumbers можно посмотреть как производится обработка вызова функции из LUA с произвольным числом и типом параметров.

После компиляции получившийся DLL-файл (как было сказано) копируем в тот же каталог, где расположен терминал QUIK (обязательно перепишите туда же файл lua5.1.dll!), и запускаем в нем следующий LUA-скрипт, предварительно сохраним его в виде файла:

require("luacdll")

message(tostring(luacdll.GetCurrentThreadId()), 1)

r = luacdll.MultTwoNumbers(5.6, 2.17)
message (tostring(r), 1)

r = luacdll.MultAllNumbers(6, 3, "23423", {2.17}, 1.1)
message (tostring(r), 1)

function main()
end

В результате его выполнения будет выведено 3 сообщения: с ID основного потока терминала, число 12.152 и число 1.1.

Полностью сходные тексты и скомпилированную dll можно скачать в виде архива.

Есть вопросы? что-то не понятно? Вэлком в комментарии.

2

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Куда поместить файл с DLL-библиотекой

Я настоятельно рекомендую созданную библиотеку DLL поместить в каталог с QUIK, т.е. в ту же папку, где лежит файл info.exe.
Да, в принципе можно играться с указанием путей в скрипте и это даже работает при должном навыке. Вот только зачем? есть ли хоть один здравый аргумент? Эстетика - это, быть может, и хорошо, но надёжность - она, на мой взгляд, перевешивает любую эстетику на 3 порядка минимум.
Так что без стеснений размещайте свои DLL-библиотеки в каталог с QUIK - и всё у вас будет работать просто и надёжно.

3

Re: Создание собственной DLL на C++ для LUA (в QUIK)

< reserved >

4

Re: Создание собственной DLL на C++ для LUA (в QUIK)

< reserved >

5

Re: Создание собственной DLL на C++ для LUA (в QUIK)

подскажите как сверстать  dll на delphi. А то у меня по опционам по БШ много написано

6

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Достаточно взять заголовочные pas-файлы для Lua библиотеки из любого дельфового проекта интерграции с Lua (коих много) - а лальше API все ровно такое же )

Описание в таких же пределах, как здесь (т.е. самые азы), будет достаточно?

7

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Можно пример.

8

Re: Создание собственной DLL на C++ для LUA (в QUIK)

admin, если можно, исходники проекта dll приложите.
С С++ много лет не работал. Основательно подзабыл.

9

Re: Создание собственной DLL на C++ для LUA (в QUIK)

ubase пишет:

admin, если можно, исходники проекта dll приложите.

Все, кто дочитывает, в конце второго поста видят ссылку на скачивание архива с исходниками )

10

Re: Создание собственной DLL на C++ для LUA (в QUIK)

admin пишет:

Все, кто дочитывает, в конце второго поста видят ссылку на скачивание архива с исходниками )

Нашел, спасибо. Я просто 2-й раз читаю, уже по диагонали. smile Первый раз меня исходники не интересовали.

11

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Подскажите как сделать все-таки dll на Delphi?

12

Re: Создание собственной DLL на C++ для LUA (в QUIK)

d@ncer пишет:

Подскажите как сделать все-таки dll на Delphi?

Пожалуйста, наслаждайтесь wink

Как сделать свою библиотеку для Lua на Delphi

13 (2013-02-18 02:02:29 отредактировано ubase)

Re: Создание собственной DLL на C++ для LUA (в QUIK)

В инете видел вызов С-функции (библиотеки) require("kernel32"). Удивило. Это как это, напрямую, без танцев с бубном? (подробно, не разбирался, просто отметил)
Честно, не понял. Сайт потерял, к сожалению.
Вообще, авторы Луа могли бы это все эти танцы непосредственно в ДЛЛ внести, чтобы просто, не думая вызывать С-функции. Впрочем, это их дело. smile

14

Re: Создание собственной DLL на C++ для LUA (в QUIK)

День добрый.
Очень бы хотелось вас попросить сделать пример создания C# dll под луа.

15

Re: Создание собственной DLL на C++ для LUA (в QUIK)

akuzn, кому хочется - пусть разбирается с Luanet )
Я посмотрел - в чистом виде это делать довольно занудно, мне не очень хотелось бы связываться

16 (2013-04-17 10:27:24 отредактировано akuzn)

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Luanet не dll, тем более, как и многое из предполагаемого, не работает под КВИКом.
Ну если бы в начале не было разбега - обращайтесь, то уж не стал бы писать.
Если настроение будет, посмотрите. Тем более прикладное применение есть.
Я в том смысле что по сравнению с "роботом по волнам ..иота" и прочей шелухой от данного решения можно добиться гораздо больше пользы.

17

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Хорошо бы вы озвучили это " прикладное применение".

18

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Формулирую. За КЛУА остается работка с заявками и событиями терминала, логика и интерфейс уходит в Си#.
Плюсы Си# - качественные интерфейсы, удобно оттестироваться в WealthLab.

19

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Т.е. фактически для решения задачи вполне подойдет отдельное приложение на C#, которое будет взаимодействовать со скриптом Lua,верно? Как таковая dll на C# получается не нужна.

А что касается "разбега",
Друзья! Бесплатно я делаю то, что мне интересно. Поимейте уже совесть.

20

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Все просто. Взялся за гуж не говори что не дюж.
Могу выполнить на таких-то условиях - это нормальный ответ.
Тебя бесплатно никто не просит. То не хочется, то не можется.

21

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Вы забыли ответить на вопрос.

22 (2013-04-18 20:43:21 отредактировано akuzn)

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Да, любая форма взаимодействия с C#.
Конечно, может быть, в новой версии терминала что-то улучшилось - уж столько товарищам было высказано. Но из документации пока ничего не ясно. Ровно как они ничего не сказали о перечне изменений и дополнений.
В любом раскладе, считаю C# с логикой и интерфейсом, КЛУА - терминал - это супер решение.

23

Re: Создание собственной DLL на C++ для LUA (в QUIK)

В качестве итога. Решение сводится к сборке, сочетающей управляемый и неуправляемый код. Все решается в рамках студии.

24

Re: Создание собственной DLL на C++ для LUA (в QUIK)

akuzn,
итог - это пример законченного кода и настроек студии, иллюстрирующие описанные концепции. Спасибо.

25 (2013-05-05 14:34:30 отредактировано akuzn)

Re: Создание собственной DLL на C++ для LUA (в QUIK)

Я вам написал исключительно по-товарищески. Такой пример мною оплачен и очень дорого.
Единственное что можно сказать - количество сил не соответствует. Вы, наверное, правы, в итоге лучше без квика и луи.