ObjectScript API, integration with C ++. Part 4: connecting custom classes and functions in C ++
- Tutorial
ObjectScript is a new, open source, object-oriented programming language. ObjectScript extends the capabilities of languages such as JavaScript, Lua, and PHP.
Based on the results of previous articles, there were many questions about how to connect your C ++ classes and functions to ObjectScript. The connection method available in the first OS builds hardly met the needs, and I decided to make a more powerful and convenient binding, which now comes with the default OS.
What is the actual advantage of the new binding: now you can connect any function, with any parameters, any return value without additional wrappers. Immediately connect the function that you have and everything is ready. And be sure that when you call a C ++ function from a script on OS, it will receive the correct parameters, and the value returned from C ++ will be correctly converted to an analog on OS.
In part 3 , a low-level connection method was described; it was preserved. The new method implements the magic of connecting functions with any parameters, any return value. So let's go!
Suppose we have the following function in C ++:
To connect it to the global namespace on OS, you need to run the code:
Call the function from ObjectScript:
Conclusion:
Suppose we have the following functions in C ++ (note that the functions accept and return completely different data types):
Of course, user-defined functions can take many parameters. We connect functions to OS as the my module:
Done, now they can be used on the OS:
Conclusion:
Here the fun begins. Suppose we have the following C ++ test class that we want to use in OS code:
Connect to OS:
Done, check on OS:
Conclusion:
Works! In the source code for this article, you will also find how to clone a custom class and overload mathematical operators.
Well, for starters, suppose we have a data structure in C ++ and we want it to look on the OS as a container with values.
Let's teach the OS to work with our structure (pass it as a parameter and return the result):
We register functions in C ++ for working with TestStruct in the global OS namespace:
Check in OS:
Conclusion:
Great, everything works! All simple data types (float, int, etc.) are already described on the OS via CtypeValue in the same way. Use CtypeValue if you need to describe a specific data type conversion OS -> C ++ and vice versa.
You can download the ObjectScript source code and an example from this article at this link , open proj.win32 \ examples.sln , the osbind project .
Other relevant articles about ObjectScript:
Based on the results of previous articles, there were many questions about how to connect your C ++ classes and functions to ObjectScript. The connection method available in the first OS builds hardly met the needs, and I decided to make a more powerful and convenient binding, which now comes with the default OS.
What is the actual advantage of the new binding: now you can connect any function, with any parameters, any return value without additional wrappers. Immediately connect the function that you have and everything is ready. And be sure that when you call a C ++ function from a script on OS, it will receive the correct parameters, and the value returned from C ++ will be correctly converted to an analog on OS.
Part 4: binding of custom classes and functions in C ++
In part 3 , a low-level connection method was described; it was preserved. The new method implements the magic of connecting functions with any parameters, any return value. So let's go!
Connecting a global function
Suppose we have the following function in C ++:
std::string getcwdString()
{
const int PATH_MAX = 1024;
char buf[PATH_MAX];
getcwd(buf, PATH_MAX);
return buf;
}
To connect it to the global namespace on OS, you need to run the code:
os->setGlobal(def("getcwd", getcwdString));
Call the function from ObjectScript:
print "getcwd: "..getcwd()
Conclusion:
getcwd: C:\Sources\OS\proj.win32\osbind
Connecting a module with functions
Suppose we have the following functions in C ++ (note that the functions accept and return completely different data types):
bool my_isdigit(const OS::String& str)
{
int len = str.getLen();
for(int i = 0; i < len; i++){
if(!isdigit(str[i])){
return false;
}
}
return len > 0;
}
std::string my_hash(const char * str)
{
int i, len = strlen(str), 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;
return buf;
}
void my_print_num(int i)
{
printf("my_print_num: %d\n", i);
}
void my_print_void(void)
{
printf("my_print_void\n");
}
long double my_fabs(long double a)
{
return a >= 0 ? a : -a;
}
Of course, user-defined functions can take many parameters. We connect functions to OS as the my module:
OS::FuncDef funcs[] = {
def("isdigit", my_isdigit),
def("hash", my_hash),
def("print_num", my_print_num),
def("print_void", my_print_void),
def("abs", my_fabs),
{}
};
os->getModule("my");
os->setFuncs(funcs);
os->pop();
Done, now they can be used on the OS:
print "isdigit(123): "..my.isdigit("123")
print "isdigit(123q): "..my.isdigit("123q")
print "my.hash(123): "..my.hash(123)
print "call my.print_num(123.5)"
my.print_num(123.5)
print "call my.print_void()"
my.print_void()
print "my.abs(-12): "..my.abs(-12)
print "my.fabs(-123.5): "..my.fabs(-123.5)
Conclusion:
isdigit(123): true
isdigit(123q): false
my.hash(123): bf9878b
call my.print_num(123.5)
my_print_num: 123
call my.print_void()
my_print_void
my.abs(-12): 12
my.fabs(-123.5): 123.5
C ++ class connection
Here the fun begins. Suppose we have the following C ++ test class that we want to use in OS code:
class TestClass
{
public:
int i;
float j;
TestClass(int _i, float _j){ i = _i; j = _j; }
int getI() const { return i; }
void setI(int _i){ i = _i; }
float getJ() const { return j; }
void setJ(float _j){ j = _j; }
double doSomething(int a, float b, double c, TestClass * pb)
{
return i + j + a + b + c + pb->i + pb->j;
}
void print()
{
printf("test class: %d, %f\n", i, j);
}
};
Connect to OS:
// 1. нужно объявить класс в пространстве имен ObjectScript
// OS_DECL_USER_CLASS - это макрос, в котором объявляются несколько
// служебных функций для правильной типизации класса на C++
namespace ObjectScript { OS_DECL_USER_CLASS(TestClass); }
// 2. нужно сделать функцию, которая будет создавать экземпляр класса
TestClass * __constructTestClass(int i, float j){ return new TestClass(i, j); }
// 3. описать протопит класса и зарегистрировать его в OS
OS::FuncDef funcs[] = {
def("__construct", __constructTestClass),
def("__get@i", &TestClass::getI),
def("__set@i", &TestClass::setI),
def("__get@j", &TestClass::getJ),
def("__set@j", &TestClass::setJ),
def("doSomething", &TestClass::doSomething),
def("print", &TestClass::print),
{}
};
registerUserClass(os, funcs);
Done, check on OS:
var t = TestClass(1, 0.25)
print "t.i: "..t.i
print "t.j: "..t.j
var t2 = TestClass(2, 0.5)
t2.i = t2.i + t.j
print "t2"
t2.print()
print "t.doSomething(10, 100.001, 1000.1, t2): "..t.doSomething(10, 100.001, 1000.1, t2)
Conclusion:
t.i: 1
t.j: 0.25
t2
test class: 2, 0.500000
t.doSomething(10, 100.001, 1000.1, t2): 1113.8509994506835
Works! In the source code for this article, you will also find how to clone a custom class and overload mathematical operators.
Connecting a custom data type in C ++
Well, for starters, suppose we have a data structure in C ++ and we want it to look on the OS as a container with values.
struct TestStruct
{
float a, b;
TestStruct(){ a = b = 0; }
TestStruct(float _a, float _b){ a = _a; b = _b; }
};
void printTestStruct(const TestStruct& p)
{
printf("TestStruct: %f %f\n", p.a, p.b);
}
TestStruct changeTestStruct(const TestStruct& p)
{
return TestStruct(p.a*10, p.b*100);
}
Let's teach the OS to work with our structure (pass it as a parameter and return the result):
namespace ObjectScript {
OS_DECL_USER_CLASS(TestStruct);
template <>
struct CtypeValue
{
// type используется внутри OS
typedef TestStruct type;
// возвращает true, если функция на C++ может работать с полученным значением
static bool isValid(const TestStruct&){ return true; }
// если параметр не был передан из OS, то возвращается def
static TestStruct def(ObjectScript::OS * os){ return TestStruct(0, 0); }
// считывание параметра из стека OS
static TestStruct getArg(ObjectScript::OS * os, int offs)
{
if(os->isObject(offs)){
os->getProperty(offs, "a"); // required
float a = os->popFloat();
os->getProperty(offs, "b"); // required
float b = os->popFloat();
return TestStruct(a, b);
}
os->triggerError(OS_E_ERROR, "TestStruct expected");
return TestStruct(0, 0);
}
// учим OS пушить в стек значения типа TestStruct
static void push(ObjectScript::OS * os, const TestStruct& p)
{
os->newObject();
os->pushStackValue();
os->pushNumber(p.a);
os->setProperty("a");
os->pushStackValue();
os->pushNumber(p.b);
os->setProperty("b");
}
};
} // namespace ObjectScript
We register functions in C ++ for working with TestStruct in the global OS namespace:
os->setGlobal(def("printTestStruct", printTestStruct));
os->setGlobal(def("changeTestStruct", changeTestStruct));
Check in OS:
var data = {a=10 b=20}
printTestStruct(data)
data = changeTestStruct(data)
printTestStruct(data)
print data
Conclusion:
TestStruct: 10.000000 20.000000
TestStruct: 100.000000 2000.000000
{"a":100,"b":2000}
Great, everything works! All simple data types (float, int, etc.) are already described on the OS via CtypeValue in the same way. Use CtypeValue if you need to describe a specific data type conversion OS -> C ++ and vice versa.
You can download the ObjectScript source code and an example from this article at this link , open proj.win32 \ examples.sln , the osbind project .
Other relevant articles about ObjectScript:
- ObjectScript API, integration with C ++. Part 3: connecting a module with functions in C ++
- ObjectScript API, integration with C ++. Part 2: running a script on OS from C ++
- ObjectScript API, integration with C ++. Part 1: working with the stack, calling OS functions from C ++
- ObjectScript - a new programming language, faster than PHP and JS
- ObjectScript - a new programming language