ObjectScript — новый объектно-ориентированный язык программирования с открытым исходным кодом. ObjectScript расширяет возможности таких языков, как JavaScript, Lua и PHP.
Давайте создадим свой модуль с фунциями, которые будут доступны в коде на OS. Назовем модуль
Для начала нум нужно написать функции нашего будущего модуля на C++, первая
Небольшие пояснения на счет:
Тут мы получаем первый параметр в виде строки.
API на C++ предоставляет целый ряд функций для получения разных простых типов из стека:
Иногда в программе на C++ происходит большая работа со стеком. В этом случае, чтобы не держать в уме относительные смещения (т.к. они будут меняться, когда что-либо добавляется или вынимается из стека), удобно работать с абсолютным указателем на значение в стеке, его можно получить функцией
Это было лирическое отступление, теперь вторая фукнция нашего модуля —
Небольшое пояснение на счет:
Это количество возвращаемых функцией значений. Функции готовы, теперь надо сообщить OS о том, что у нас есть новый модуль с такими функциями:
Теперь давайте проверим, как это работает, напишем программу на OS (main.os), которая для каждого значения тестового массива вызовет наши функции на C++:
Программа выведет следующий результат:
Полный текст исходников на C++:
Вы можете скачать исходники ObjectScript и пример из данной статьи по этой ссылке, открыть proj.win32\examples.sln, проект add_user_module.
Другие релевантные статьи об ObjectScript:
P.S. Небольшой опус об
OS::String — это объект со строкой ObjectScript, который может быть сохранен в пользовательском коде на C++. Такая строка сохраняет валидное значение все время своего существования. Чтобы получить указать на null-terminated строку, нужно воспользоваться функцией
Q: Что будет, если выполнить
A: OS::String захватывает экземпляр OS, давая ему понять, что он используется во внешнем коде. Поэтому
Часть 3: подключение модуля с функциями на C++
Давайте создадим свой модуль с фунциями, которые будут доступны в коде на OS. Назовем модуль
my, он будет содержать две функции:isdigitбудет проверять, состоит ли строка, переданная в параметре, только из чиселhashбудет преобразовывать параметр в строку хеша по нашему алгоритму
Для начала нум нужно написать функции нашего будущего модуля на C++, первая
my_isdigit:static int my_isdigit(OS * os, int params, int, int, void*) { OS::String str = os->toString(-params); int len = str.getLen(); for(int i = 0; i < len; i++){ if(!isdigit(str[i])){ os->pushBool(false); return 1; } } os->pushBool(len > 0); return 1; }
Небольшие пояснения на счет:
OS::String str = os->toString(-params);
Тут мы получаем первый параметр в виде строки.
params — это количество параметров, которые были переданы в функцию из OS. -params — это относительный указатель в стеке на первый параметр. Если бы нам нужно было обратится ко второму параметру, то это выглядело бы так: os->toString(-params+1), к третьему — os->toString(-params+2) и т.д.API на C++ предоставляет целый ряд функций для получения разных простых типов из стека:
toFloat, toDouble, toInt, toString, toUserdata, popFloat и т.д.Иногда в программе на C++ происходит большая работа со стеком. В этом случае, чтобы не держать в уме относительные смещения (т.к. они будут меняться, когда что-либо добавляется или вынимается из стека), удобно работать с абсолютным указателем на значение в стеке, его можно получить функцией
getAbsoluteOffs(int offs) и работать тогда вот так:int params_offs = os->getAbsoluteOffs(-params); OS::String str = os->toString(params_offs); // перпый параметр OS::String str = os->toString(params_offs+1); // второй
Это было лирическое отступление, теперь вторая фукнция нашего модуля —
my_hash:static int my_hash(OS * os, int params, int, int, void*) { OS::String str = os->toString(-params); int i, len = str.getLen(), hash = 5381; for(i = 0; i < len; i++){ hash = ((hash << 5) + hash) + str[i]; } hash &= 0x7fffffff; char buf[16]; for(i = 0; hash > 0; hash >>= 4){ buf[i++] = "0123456789abcdef"[hash & 0xf]; } buf[i] = 0; os->pushString(buf); return 1; }
Небольшое пояснение на счет:
return 1;
Это количество возвращаемых функцией значений. Функции готовы, теперь надо сообщить OS о том, что у нас есть новый модуль с такими функциями:
void initMyModule(OS * os) { OS::FuncDef funcs[] = { {"isdigit", my_isdigit}, {"hash", my_hash}, {} }; os->getModule("my"); os->setFuncs(funcs); os->pop(); }
Теперь давайте проверим, как это работает, напишем программу на OS (main.os), которая для каждого значения тестового массива вызовет наши функции на C++:
for(var i, s in ["123", "12w", 1234, " df", " "]){ print("my.isdigit("..s..") = "my.isdigit(s)" my.hash("..s..") = "my.hash(s)) }
Программа выведет следующий результат:
my.isdigit(123) = true my.hash(123) = bf9878b my.isdigit(12w) = false my.hash(12w) = f3a878b my.isdigit(1234) = true my.hash(1234) = f89c87c7 my.isdigit( df) = false my.hash( df) = f48478b my.isdigit( ) = false my.hash( ) = 5082f6c7
Полный текст исходников на C++:
#include "objectscript.h" #include <ctype.h> using namespace ObjectScript; static int my_isdigit(OS * os, int params, int, int, void*) { OS::String str = os->toString(-params); int len = str.getLen(); for(int i = 0; i < len; i++){ if(!isdigit(str[i])){ os->pushBool(false); return 1; } } os->pushBool(len > 0); return 1; } static int my_hash(OS * os, int params, int, int, void*) { OS::String str = os->toString(-params); int i, len = str.getLen(), hash = 5381; for(i = 0; i < len; i++){ hash = ((hash << 5) + hash) + str[i]; } char buf[16]; hash &= 0x7fffffff; for(i = 0; hash > 0; hash >>= 4){ buf[i++] = "0123456789abcdef"[hash & 0xf]; } buf[i] = 0; os->pushString(buf); return 1; } void initMyModule(OS * os) { OS::FuncDef funcs[] = { {"isdigit", my_isdigit}, {"hash", my_hash}, {} }; os->getModule("my"); os->setFuncs(funcs); os->pop(); } void main() { OS * os = OS::create(); initMyModule(os); os->require("main.os"); os->release(); }
Вы можете скачать исходники ObjectScript и пример из данной статьи по этой ссылке, открыть proj.win32\examples.sln, проект add_user_module.
Другие релевантные статьи об ObjectScript:
- ObjectScript API, интеграция с C++. Часть 2: выполнение скрипта на OS из C++
- ObjectScript API, интеграция с C++. Часть 1: работа со стеком, вызов функций OS из C++
- ObjectScript — новый язык программирования
P.S. Небольшой опус об OS::String
OS::String — это объект со строкой ObjectScript, который может быть сохранен в пользовательском коде на C++. Такая строка сохраняет валидное значение все время своего существования. Чтобы получить указать на null-terminated строку, нужно воспользоваться функцией
toChar(). ObjectScript хранит в памяти все разные строки в единственном экземпляре, поэтому OS::String — это константная строка, ее нельзя изменять ни при каких условиях. Но можно получить новую строку. OS::String реализует целый ряд конструкторов и оператор конкатенации, так что OS::String вполне можно создать из пользовательского кода и работать с ней (при необходимости).Q: Что будет, если выполнить
os->release() раньше, чем разрушаться строки OS::String, сохраненные в пользовательском коде?A: OS::String захватывает экземпляр OS, давая ему понять, что он используется во внешнем коде. Поэтому
os->release() не разрушит экземпляр OS, но он будет разрушен, когда последняя строка OS::String прекратит своё существование.