Добрый день, Хабрахабр!
Решил написать этот топик на тему скриптов

Если вы разрабатывали большие проекты (к примеру, масштабные игры), замечали, что с каждой новой сотней строк кода компиляция идет медленней?
В игре создается больше оружия, больше диалогов, больше меню, больше etc.
Одна из самых главных проблем, возникающих в связи с нововведениями — поддерживать бессчетное множество оружия и бейджиков довольно сложное занятие.
В ситуации, когда просьба друга/босса/напарника изменить диалог или добавить новый вид оружия занимает слишком много времени, приходится прибегать к каким-то мерам — например, записи всей этой фигни в отдельные текстовые файлы.
Почти каждый геймдевелопер когда-нибудь делал карту уровней или диалоги в отдельном текстовом файле и потом их считывал. Взять хотя бы простейший вариант — олимпиадные задачи по информатике с файлом ввода
Но есть способ, на голову выше — использование скриптов.
«Окей, для таких дел хватает обычного файла с описанием характеристиков игрока. Но что делать, если в бурно развивающемся проекте почти каждый день приходится немножко изменять логику главного игрока, и, следовательно, много раз компилировать проект?»
Хороший вопрос. В этом случае нам на помощь приходят скрипты, держащие именно логику игрока со всеми характеристиками либо какой-либо другой части игры.
Естественно, удобнее всего держать, логику игрока в виде кода какого-нибудь языка программирования.
Первая мысль — написать свой интерпретатор своего скриптового языка, выкидывается из мозга через несколько секунд. Логика игрока определенно не стоит таких жутких затрат.
К счастью, есть специальные библиотеки скриптовых языков для С++, которые принимают на вход текстовый файл и выполняют его.
Об одном таком скриптовом языке Lua пойдет речь.
Прежде чем начать, важно понимать, как работает скриптовый язык. Дело в том, что в скриптовых языках есть очень мало функций, при наличии конструкций for, while, if, прочих.
В основном это функции вывода текста в консоль, математические функции и функции для работы с файлами.
Как же тогда можно управлять игроком через скрипты?
Мы в С++-программе делаем какие-либо функции, «регистрируем» их под каким-нибудь именем в скрипте и вызываем в скрипте. То есть если мы зарегистрировали функцию SetPos(x,y) для определения позиции игрока в С++-программе, то, встретив эту функцию в скрипте, «интерпретатор» из библиотеки скриптового языка вызывает эту функцию в С++-программе, естественно, с передачей всех методов.
Удивительно, да? :)
UPD: Внимание! Один юзер обратился мне с мейлом, что, когда я заливал код, я не полностью устранил все ошибки — habrahabr.ru/post/196272/#comment_6850016
В коде с позволения хабра проникли жучки
Замените участки кода вроде
На
И еще вместо lua_CFunction проскакивает lua_cfunction
Спасибо!
Когда вы поняли преимущества скриптовых языков программирования, самое время начать работать!
Скачайте из репозитория на гитхабе (низ топика) lib'у и includ'ы Lua, либо возмите их на официальном сайте.
Создаем консольный проект либо Win32 (это неважно) в Visual Studio (у меня стоит версия 2012)
Заходим в Проект->Свойства->Свойства конфигурации->Каталоги VC++ и в «каталоги включения» и «каталоги библиотек» добавьте папку Include и Lib из репозитория соответственно.
Теперь создаем файл main.cpp, пишем в нем:
Как вы догадались, у меня консольное приложение.
Теперь переходим к кодингу
Обещаю, что буду тщательно объяснять каждый момент
У нас за скрипты будет отвечать класс Script. Я буду объявлять и одновременно реализовывать функции в Script.h/.cpp
Создаем Script.cpp и пишем в нем
Создаем Script.h и пишем в нем
После 2 строчки и перед #endif мы определяем класс скриптов
Этот код пишется для предотвращения взаимного включения файлов. Допустим, что файл Game.h подключает Script.h, а Script.h подключает Game.h — непорядок! А с таким кодом включение выполняется только 1 раз
Теперь пишем внутри этого кода вот это
Первая строчка подключает сам lua.lib из архива.
Для чего нужен extern «C»? Дело в том, что lua написан на С и поэтому такой код необходим для подключения библиотек.
Дальше идет подключение хорошо известных многим файлов для работы с консолью
Теперь приступим к определению класса
Самый главный объект библиотеки Lua для C++ — lua_State, он необходим для выполнения скриптов
Дальше идут публичные функции
Эта функция инициализирует lua_State
Эта функция освобождает ресурсы Lua
А эта функция выполняет файл. На вход она принимает название файла, например, «C:\\script.lua».
Почему она возвращает int? Просто некоторые скрипты могут содержать return, прерывая работу скрипта и возвращая какое-нибудь значение.
Теперь мы сделаем функцию, регистрирующую константы (числа, строки, функции)
Следующая функция создает таблицу для Луа. Если непонятно, что это значит, то тамошная таблица все равно что массив
Следующая функция регистрирует элемент в таблице.
Наконец, заполненную таблицу нужно зарегистрировать
Следующие функции предназначены в основном только для функций типа int foo(lua_State*), которые нужны для регистрации в Луа.
Первая из них — получает количество аргументов
Следующая функция получает аргумент, переданный функции в скрипте
Наконец, последняя функция, которая возвращает значение в скрипт
Пора что-нибудь сделать!
Изменяем main.cpp
Компилируем. Теперь можно приступить к тестированию нашего класса
Помните, я обещал сделать функцию Write? :)
Видоизменяем main.cpp
А в папке с проектом создаем файл script.lua

Компилируем и запускаем проект.

Теперь изменяем script.lua
Теперь программа будет выводить по 2 строки ("\n" — создание новой строки), ждать нажатия Enter и снова выводить строки.

Экспериментируйте со скриптами!
Вот пример main.cpp с функциями и пример script.lua
Полный листинг Script.h и Script.cpp
Репозиторий с lib'ой и includ'ами: https://github.com/Izaron/LuaForHabr
Все вопросы посылайте мне в ЛС, либо в этот топик, либо, если вам не повезло быть зарегистрированным на хабре — на мейл izarizar@mail.ru
Решил написать этот топик на тему скриптов

Что нужно знать?
- С++ на приличном уровне (в уроке будут шаблоны — template)
- Lua, очень легкий скриптовый язык. Советую этот урок.
Почему писать диалоги игры в .cpp файле было большой ошибкой
Если вы разрабатывали большие проекты (к примеру, масштабные игры), замечали, что с каждой новой сотней строк кода компиляция идет медленней?
В игре создается больше оружия, больше диалогов, больше меню, больше etc.
Одна из самых главных проблем, возникающих в связи с нововведениями — поддерживать бессчетное множество оружия и бейджиков довольно сложное занятие.
В ситуации, когда просьба друга/босса/напарника изменить диалог или добавить новый вид оружия занимает слишком много времени, приходится прибегать к каким-то мерам — например, записи всей этой фигни в отдельные текстовые файлы.
Почти каждый геймдевелопер когда-нибудь делал карту уровней или диалоги в отдельном текстовом файле и потом их считывал. Взять хотя бы простейший вариант — олимпиадные задачи по информатике с файлом ввода
Но есть способ, на голову выше — использование скриптов.
Решение проблемы
«Окей, для таких дел хватает обычного файла с описанием характеристиков игрока. Но что делать, если в бурно развивающемся проекте почти каждый день приходится немножко изменять логику главного игрока, и, следовательно, много раз компилировать проект?»
Хороший вопрос. В этом случае нам на помощь приходят скрипты, держащие именно логику игрока со всеми характеристиками либо какой-либо другой части игры.
Естественно, удобнее всего держать, логику игрока в виде кода какого-нибудь языка программирования.
Первая мысль — написать свой интерпретатор своего скриптового языка, выкидывается из мозга через несколько секунд. Логика игрока определенно не стоит таких жутких затрат.
К счастью, есть специальные библиотеки скриптовых языков для С++, которые принимают на вход текстовый файл и выполняют его.
Об одном таком скриптовом языке Lua пойдет речь.
Как это работает?
Прежде чем начать, важно понимать, как работает скриптовый язык. Дело в том, что в скриптовых языках есть очень мало функций, при наличии конструкций for, while, if, прочих.
В основном это функции вывода текста в консоль, математические функции и функции для работы с файлами.
Как же тогда можно управлять игроком через скрипты?
Мы в С++-программе делаем какие-либо функции, «регистрируем» их под каким-нибудь именем в скрипте и вызываем в скрипте. То есть если мы зарегистрировали функцию SetPos(x,y) для определения позиции игрока в С++-программе, то, встретив эту функцию в скрипте, «интерпретатор» из библиотеки скриптового языка вызывает эту функцию в С++-программе, естественно, с передачей всех методов.
Удивительно, да? :)
UPD: Внимание! Один юзер обратился мне с мейлом, что, когда я заливал код, я не полностью устранил все ошибки — habrahabr.ru/post/196272/#comment_6850016
В коде с позволения хабра проникли жучки
Замените участки кода вроде
template<class t>
T MethodName();
На
template<class T>
T MethodName();
И еще вместо lua_CFunction проскакивает lua_cfunction
Спасибо!
Я готов!
Когда вы поняли преимущества скриптовых языков программирования, самое время начать работать!
Скачайте из репозитория на гитхабе (низ топика) lib'у и includ'ы Lua, либо возмите их на официальном сайте.
Создаем консольный проект либо Win32 (это неважно) в Visual Studio (у меня стоит версия 2012)
Заходим в Проект->Свойства->Свойства конфигурации->Каталоги VC++ и в «каталоги включения» и «каталоги библиотек» добавьте папку Include и Lib из репозитория соответственно.
Теперь создаем файл main.cpp, пишем в нем:
int main()
{
return 0;
}
Как вы догадались, у меня консольное приложение.
Теперь переходим к кодингу
Обещаю, что буду тщательно объяснять каждый момент
У нас за скрипты будет отвечать класс Script. Я буду объявлять и одновременно реализовывать функции в Script.h/.cpp
Создаем Script.cpp и пишем в нем
#include "Script.h"
Создаем Script.h и пишем в нем
#ifndef _SCRIPT_H_
#define _SCRIPT_H_
#endif
После 2 строчки и перед #endif мы определяем класс скриптов
Этот код пишется для предотвращения взаимного включения файлов. Допустим, что файл Game.h подключает Script.h, а Script.h подключает Game.h — непорядок! А с таким кодом включение выполняется только 1 раз
Теперь пишем внутри этого кода вот это
#pragma comment(lib,"lua.lib")
extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
Первая строчка подключает сам lua.lib из архива.
Для чего нужен extern «C»? Дело в том, что lua написан на С и поэтому такой код необходим для подключения библиотек.
Дальше идет подключение хорошо известных многим файлов для работы с консолью
#include <stdio.h>
#include <iostream>
#include <sstream>
using namespace std;
Теперь приступим к определению класса
class Script
{
Самый главный объект библиотеки Lua для C++ — lua_State, он необходим для выполнения скриптов
private:
lua_State *lua_state;
Дальше идут публичные функции
public:
void Create();
Эта функция инициализирует lua_State
Create()
Его определение в Script.cpp
Первой строчкой мы инициализируем наш lua_State.
Потом мы объявляем список «подключенных библиотек». Дело в том, что в «чистом» виде в луа есть только функция print(). Для математических и прочих функций требуется подключать специальные библиотеки и потом вызывать их как math.foo, base.foo, io.foo. Для подключения других библиотек добавьте в lualibs, например, {«math», luaopen_math}. Все названия библиотек начинаются с luaopen_..., в конце lialibs должен стоять {NULL,NULL}
void Script::Create()
{
lua_state = luaL_newstate();
static const luaL_Reg lualibs[] =
{
{"base", luaopen_base},
{"io", luaopen_io},
{NULL, NULL}
};
for(const luaL_Reg *lib = lualibs; lib->func != NULL; lib++)
{
luaL_requiref(lua_state, lib->name, lib->func, 1);
lua_settop(lua_state, 0);
}
}
Первой строчкой мы инициализируем наш lua_State.
Потом мы объявляем список «подключенных библиотек». Дело в том, что в «чистом» виде в луа есть только функция print(). Для математических и прочих функций требуется подключать специальные библиотеки и потом вызывать их как math.foo, base.foo, io.foo. Для подключения других библиотек добавьте в lualibs, например, {«math», luaopen_math}. Все названия библиотек начинаются с luaopen_..., в конце lialibs должен стоять {NULL,NULL}
void Close();
Эта функция освобождает ресурсы Lua
Close()
Ее определение
Просто используем lua_close()
void Script::Close()
{
lua_close(lua_state);
}
Просто используем lua_close()
int DoFile(char* ScriptFileName);
А эта функция выполняет файл. На вход она принимает название файла, например, «C:\\script.lua».
Почему она возвращает int? Просто некоторые скрипты могут содержать return, прерывая работу скрипта и возвращая какое-нибудь значение.
DoFile()
Ее определение
Как вы видите, я выполняю скрипт и возвращаю int. Но возращать функция может не только int, но еще и bool и char*, просто я всегда возвращаю числа (lua_toboolean, lua_tostring)
int Script::DoFile(char* ScriptFileName)
{
luaL_dofile(lua_state,ScriptFileName);
return lua_tointeger(lua_state, lua_gettop(lua_state));
}
Как вы видите, я выполняю скрипт и возвращаю int. Но возращать функция может не только int, но еще и bool и char*, просто я всегда возвращаю числа (lua_toboolean, lua_tostring)
Теперь мы сделаем функцию, регистрирующую константы (числа, строки, функции)
template<class t>
void RegisterConstant(T value, char* constantname);
RegisterConstant()
Мы действуем через шаблоны. Пример вызова функции:
Ее определение
Для каждого возможного значения class T мы определяем свои действия.
*Капитан* последнее определение — регистрация функции
Функции, годные для регистрации, выглядят так:
Где n — количество возвращаемых значений. Если n = 2, то в Луа можно сделать так:
Читайте мануалы по Луа, если были удивлены тем, что одна функция возвращает несколько значений :)
RegisterConstant<int>(13,"goodvalue");
Ее определение
template<>
void Script::RegisterConstant<int>(int value, char* constantname)
{
lua_pushinteger(lua_state, value);
lua_setglobal(lua_state,constantname);
}
template<>
void Script::RegisterConstant<double>(double value, char* constantname)
{
lua_pushnumber(lua_state, value);
lua_setglobal(lua_state,constantname);
}
template<>
void Script::RegisterConstant<char>(char* value, char* constantname)
{
lua_pushstring(lua_state, value);
lua_setglobal(lua_state,constantname);
}
template<>
void Script::RegisterConstant<bool>(bool value, char* constantname)
{
lua_pushboolean(lua_state, value);
lua_setglobal(lua_state,constantname);
}
template<>
void Script::RegisterConstant<lua_cfunction>(lua_CFunction value, char* constantname)
{
lua_pushcfunction(lua_state, value);
lua_setglobal(lua_state,constantname);
}
Для каждого возможного значения class T мы определяем свои действия.
*Капитан* последнее определение — регистрация функции
Функции, годные для регистрации, выглядят так:
int Foo(lua_State*)
{
// ...
return n;
}
Где n — количество возвращаемых значений. Если n = 2, то в Луа можно сделать так:
a, b = Foo()
Читайте мануалы по Луа, если были удивлены тем, что одна функция возвращает несколько значений :)
Следующая функция создает таблицу для Луа. Если непонятно, что это значит, то тамошная таблица все равно что массив
void Array();
Array()
Ее описание
void Script::Array()
{
lua_createtable(lua_state, 2, 0);
}
Следующая функция регистрирует элемент в таблице.
template<class t>
void RegisterConstantArray(T value, int index);
RegisterConstantArray()
Ее описание
Если вы не знаете Lua, вы, наверное, удивлены тем, что в один массив помещается столько типов? :)
На самом деле в элементе таблицы может содержаться еще и таблица, я так никогда не делаю.
template
void Script::RegisterConstantArray<int>(int value, int index)
{
lua_pushnumber(lua_state, index);
lua_pushinteger(lua_state, value);
lua_settable(lua_state, -3);
}
template
void Script::RegisterConstantArray<double>(double value, int index)
{
lua_pushnumber(lua_state, index);
lua_pushnumber(lua_state, value);
lua_settable(lua_state, -3);
}
template
void Script::RegisterConstantArray<char>(char* value, int index)
{
lua_pushnumber(lua_state, index);
lua_pushstring(lua_state, value);
lua_settable(lua_state, -3);
}
template
void Script::RegisterConstantArray<bool>(bool value, int index)
{
lua_pushnumber(lua_state, index);
lua_pushboolean(lua_state, value);
lua_settable(lua_state, -3);
}
template
void Script::RegisterConstantArray<lua_cfunction>(lua_CFunction value, int index)
{
lua_pushnumber(lua_state, index);
lua_pushcfunction(lua_state, value);
lua_settable(lua_state, -3);
}
Если вы не знаете Lua, вы, наверное, удивлены тем, что в один массив помещается столько типов? :)
На самом деле в элементе таблицы может содержаться еще и таблица, я так никогда не делаю.
Наконец, заполненную таблицу нужно зарегистрировать
void RegisterArray(char* arrayname);
RegisterArray()
Ее описание
Ничего особенного нет
void Script::RegisterArray(char* arrayname)
{
lua_setglobal(lua_state, arrayname);
}
Ничего особенного нет
Следующие функции предназначены в основном только для функций типа int foo(lua_State*), которые нужны для регистрации в Луа.
Первая из них — получает количество аргументов
int GetArgumentCount();
Create()
Ее описание
Эта функция нужна, например, для функции Write(), куда можно запихать сколь угодно аргументов, а можно и ни одного
Подобную функцию мы реализуем позже
int Script::GetArgumentCount()
{
return lua_gettop(lua_state);
}
Эта функция нужна, например, для функции Write(), куда можно запихать сколь угодно аргументов, а можно и ни одного
Подобную функцию мы реализуем позже
Следующая функция получает аргумент, переданный функции в скрипте
template<class t>
T GetArgument(int index);
GetArgument()
Ее описание
Можно получить все типы, описывавшиеся ранее, кроме таблиц и функций
index — это номер аргумента. И первый аргумент начинается с 1.
template
int Script::GetArgument<int>(int index)
{
return lua_tointeger(lua_state,index);
}
template
double Script::GetArgument<double>(int index)
{
return lua_tonumber(lua_state,index);
}
template
char* Script::GetArgument<char>(int index)
{
return (char*)lua_tostring(lua_state,index);
}
template
bool Script::GetArgument<bool>(int index)
{
return lua_toboolean(lua_state,index);
}
Можно получить все типы, описывавшиеся ранее, кроме таблиц и функций
index — это номер аргумента. И первый аргумент начинается с 1.
Наконец, последняя функция, которая возвращает значение в скрипт
template<class t>
void Return(T value);
Return()
Ее описание
template<>
void Script::Return<int>(int value)
{
lua_pushinteger(lua_state,value);
}
template<>
void Script::Return<double>(double value)
{
lua_pushnumber(lua_state,value);
}
template<>
void Script::Return<char>(char* value)
{
lua_pushstring(lua_state,value);
}
template<>
void Script::Return<bool>(bool value)
{
lua_pushboolean(lua_state,value);
}
Боевой код
Пора что-нибудь сделать!
Изменяем main.cpp
#include "Script.h"
int main()
{
return 0;
}
Компилируем. Теперь можно приступить к тестированию нашего класса
Помните, я обещал сделать функцию Write? :)
Видоизменяем main.cpp
#include "Script.h"
// Нужен для _getch()
#include <conio.h>
// Объект скрипта
Script script;
// Функция Write для текста
int Write(lua_State*)
{
// Тут мы считываем количество аргументов и каждый аргумент выводим
for(int i = 1; i < script.GetArgumentCount()+1; i++)
cout << script.GetArgument<char*>(i);
// После вывода ставим консоль на паузу
_getch();
return 0;
}
int main()
{
script.Create();
// Имя у луашной функции такое же, как у сишной
script.RegisterConstant<lua_cfunction>(Write,"Write");
script.DoFile("script.lua");
script.Close();
}
А в папке с проектом создаем файл script.lua
Write(1,2,3,4)

Компилируем и запускаем проект.

Теперь изменяем script.lua
for i = 1, 4 do
Write(i, "\n", "Hier kommt die Sonne", "\n")
end
Теперь программа будет выводить по 2 строки ("\n" — создание новой строки), ждать нажатия Enter и снова выводить строки.

Экспериментируйте со скриптами!
Вот пример main.cpp с функциями и пример script.lua
#include "Script.h"
#include <conio.h>
#include <Windows.h>
#include <time.h>
Script script;
int Write(lua_State*)
{
// Тут мы считываем количество аргументов и каждый аргумент выводим
for(int i = 1; i < script.GetArgumentCount()+1; i++)
cout << script.GetArgument<char*>(i);
cout << "\n";
return 0;
}
int GetString(lua_State*)
{
// Считываем строку с помощью cin и возвращаем ее, используя методы Script
char* str = "";
cin >> str;
script.Return<char*>(str);
// Не забудьте! У нас возвращается 1 результат -> return 1
return 1;
}
int Message(lua_State*)
{
// Выводим обычное сообщение MessageBox из Windows.h
// Кстати, вам домашнее задание - сделайте возможность вывода сообщений с несколькими аргументами :)
char* msg = script.GetArgument<char*>(1);
MessageBox(0,msg,"Сообщение",MB_OK);
return 0;
}
int GetTwoRandomNumbers(lua_State*)
{
// Возвращаем два рандомных числа до 1000
srand(time(NULL));
for(int i = 0; i < 2; i++)
script.Return<int>(rand()%1000);
// Вовзращаем 2 значения
return 2;
}
int GetLotOfRandomNumbers(lua_State*)
{
// Возвращаем много рандомных чисел до 1000
srand(time(NULL));
for(int i = 0; i < script.GetArgument<int>(1); i++)
script.Return<int>(rand()%1000);
// Вовзращаем столько значений, сколько задано в аргументе
return script.GetArgument<int>(1);
}
int main()
{
script.Create();
script.RegisterConstant<lua_CFunction>(Write,"Write");
script.RegisterConstant<lua_CFunction>(GetString,"GetString");
script.RegisterConstant<lua_CFunction>(Message,"Message");
script.RegisterConstant<lua_CFunction>(GetTwoRandomNumbers,"Rand1");
script.RegisterConstant<lua_CFunction>(GetLotOfRandomNumbers,"Rand2");
script.Array();
script.RegisterConstantArray<int>(1,1);
script.RegisterConstantArray<int>(2,2);
script.RegisterConstantArray<int>(3,3);
script.RegisterConstantArray<int>(4,4);
script.RegisterArray("mass");
script.DoFile("script.lua");
script.Close();
// Пауза после скрипта
_getch();
}
for i = 1, 4 do
Write(i, "\n", "Hier kommt die Sonne", "\n")
end
Write(2*100-1)
Message("Привет!")
a, b = Rand1()
Write(a, "\n", b, "\n")
Write(Rand1(), "\n")
a, b, c, d = Rand2(4)
Write(a, "\n", b, "\n", c, "\n", d, "\n")
return 1
Полезные советы
- Для класса Script все равно, в каком расширении находится скрипт, хоть в .txt, хоть в .lua, хоть в .bmp, просто .lua открывается множеством редакторов именно ЯП Луа
- Используйте редакторы Lua кода, очень трудно писать код, можно забыть написать end, do, либо что-нибудь еще. Программа из-за ошибки в луа скрипте не вылетит, но просто не выполнит код
- Lua может оказаться намного гибче, чем вам могло показаться. К примеру, числа свободно преобразуются в строки, он нетипизирован. Если передать в функцию 100 параметров, а она в С++ считывает только первые 2, то программа не вылетит. Есть еще много подобных допущений.
Вопросы и ответы
- Вопрос: Почему мы не используем луа стейт, который есть в каждой подобной функции — int foo(lua_State* L)?
Ответ: За всю программу мы используем только один стейт в Script, где регистрируем функции, инициализируем его и делаем прочие штучки. К тому же просто невыгодно было бы, написав целый класс, опять обращаться начистоту к lua_State через lua_pushboolean и прочие функции.
Полный листинг Script.h и Script.cpp
Script.h
Я удалил инклуды для работы с консолью
#ifndef _SCRIPT_H_
#define _SCRIPT_H_
#pragma comment(lib,"lua.lib")
extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
class Script
{
private:
lua_State *lua_state;
public:
void Create();
void Close();
int DoFile(char* ScriptFileName);
template<class t>
void RegisterConstant(T value, char* constantname);
void Array();
template<class t>
void RegisterConstantArray(T value, int index);
void RegisterArray(char* arrayname);
int GetArgumentCount();
template<class t>
T GetArgument(int index);
template<class t>
void Return(T value);
};
#endif
Я удалил инклуды для работы с консолью
Script.cpp
#include "Script.h"
void Script::Create()
{
lua_state = luaL_newstate();
static const luaL_Reg lualibs[] =
{
{"base", luaopen_base},
{"io", luaopen_io},
{NULL, NULL}
};
for(const luaL_Reg *lib = lualibs; lib->func != NULL; lib++)
{
luaL_requiref(lua_state, lib->name, lib->func, 1);
lua_settop(lua_state, 0);
}
}
void Script::Close()
{
lua_close(lua_state);
}
int Script::DoFile(char* ScriptFileName)
{
luaL_dofile(lua_state,ScriptFileName);
return lua_tointeger(lua_state, lua_gettop(lua_state));
}
template<>
void Script::RegisterConstant<int>(int value, char* constantname)
{
lua_pushinteger(lua_state, value);
lua_setglobal(lua_state,constantname);
}
template<>
void Script::RegisterConstant<double>(double value, char* constantname)
{
lua_pushnumber(lua_state, value);
lua_setglobal(lua_state,constantname);
}
template<>
void Script::RegisterConstant<char>(char* value, char* constantname)
{
lua_pushstring(lua_state, value);
lua_setglobal(lua_state,constantname);
}
template<>
void Script::RegisterConstant<bool>(bool value, char* constantname)
{
lua_pushboolean(lua_state, value);
lua_setglobal(lua_state,constantname);
}
template<>
void Script::RegisterConstant<lua_cfunction>(int(*value)(lua_State*), char* constantname)
{
lua_pushcfunction(lua_state, value);
lua_setglobal(lua_state,constantname);
}
void Script::Array()
{
lua_createtable(lua_state, 2, 0);
}
template<>
void Script::RegisterConstantArray<int>(int value, int index)
{
lua_pushnumber(lua_state, index);
lua_pushinteger(lua_state, value);
lua_settable(lua_state, -3);
}
template<>
void Script::RegisterConstantArray<double>(double value, int index)
{
lua_pushnumber(lua_state, index);
lua_pushnumber(lua_state, value);
lua_settable(lua_state, -3);
}
template<>
void Script::RegisterConstantArray<char>(char* value, int index)
{
lua_pushnumber(lua_state, index);
lua_pushstring(lua_state, value);
lua_settable(lua_state, -3);
}
template<>
void Script::RegisterConstantArray<bool>(bool value, int index)
{
lua_pushnumber(lua_state, index);
lua_pushboolean(lua_state, value);
lua_settable(lua_state, -3);
}
template<>
void Script::RegisterConstantArray<lua_cfunction>(lua_CFunction value, int index)
{
lua_pushnumber(lua_state, index);
lua_pushcfunction(lua_state, value);
lua_settable(lua_state, -3);
}
void Script::RegisterArray(char* arrayname)
{
lua_setglobal(lua_state, arrayname);
}
int Script::GetArgumentCount()
{
return lua_gettop(lua_state);
}
template<>
int Script::GetArgument<int>(int index)
{
return lua_tointeger(lua_state,index);
}
template<>
double Script::GetArgument<double>(int index)
{
return lua_tonumber(lua_state,index);
}
template<>
char* Script::GetArgument<char>(int index)
{
return (char*)lua_tostring(lua_state,index);
}
template<>
bool Script::GetArgument<bool>(int index)
{
return lua_toboolean(lua_state,index);
}
template<>
void Script::Return<int>(int value)
{
lua_pushinteger(lua_state,value);
}
template<>
void Script::Return<double>(double value)
{
lua_pushnumber(lua_state,value);
}
template<>
void Script::Return<char>(char* value)
{
lua_pushstring(lua_state,value);
}
template<>
void Script::Return<bool>(bool value)
{
lua_pushboolean(lua_state,value);
}
Репозиторий с lib'ой и includ'ами: https://github.com/Izaron/LuaForHabr
Все вопросы посылайте мне в ЛС, либо в этот топик, либо, если вам не повезло быть зарегистрированным на хабре — на мейл izarizar@mail.ru