ObjectScript — новый объектно-ориентированный язык программирования с открытым исходным кодом. ObjectScript расширяет возможности таких языков, как JavaScript, Lua и PHP.
Часть 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.win32examples.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, который может быть сохранен в пользовательском коде. Такая строка сохраняет валидное значение все время своего существования. Чтобы получить указать на null-terminated строку, нужно воспользоваться функцией toChar()
. ObjectScript хранит в памяти все разные строки в единственном экземпляре, поэтому OS::String
— это константная строка, ее нельзя изменять ни при каких условиях. Но можно получить новую строку. OS::String
реализует целый ряд конструкторов и оператор конкатенации, так что OS::String
вполне можно создать из пользовательского кода и работать с ней (при необходимости).
Q: Что будет, если выполнить os->release()
раньше, чем разрушаться строки OS::String
, сохраненные в пользовательском коде?
A: OS::String захватывает экземпляр OS, давая ему понять, что он используется во внешнем коде. Поэтому os->release()
не разрушит экземпляр OS, но он будет разрушен, когда последняя строка OS::String
прекратит своё существование.
Автор: evgeniyup