This commit is contained in:
blueloveTH 2024-08-11 23:25:58 +08:00
parent d3fd75d0be
commit 96a760dfd8
12 changed files with 58 additions and 856 deletions

View File

@ -1,6 +1,6 @@
---
icon: log
title: 'Upgrade to v2.0.0'
title: 'Upgrade to v2.0'
order: 25
---

View File

@ -1,19 +0,0 @@
---
title: Call
icon: dot
order: 6
---
### `bool pkpy_vectorcall(pkpy_vm*, int argc)`
Wraps `vm->vectorcall(argc)`. This function is used to call a function with a fixed number of arguments. The arguments are popped from the stack. The return value is pushed onto the stack.
1. First push the function to call.
2. Push `self` argument if it is a method call. Otherwise, call `pkpy_push_null`.
3. Push arguments from left to right.
!!!
Unlike lua, a python function always returns a value.
If the function returns `void`, it will push `None` onto the stack.
You can call `pkpy_pop_top` to discard the return value.
!!!

View File

@ -1,23 +0,0 @@
---
title: Error Handling
icon: dot
order: 5
---
#### `bool pkpy_clear_error(pkpy_vm*, char** message)`
+ If a method returns false, call the `pkpy_clear_error` method to check the error and clear it
+ If `pkpy_clear_error` returns false, it means that no error was set, and it takes no action
+ If `pkpy_clear_error` returns true, it means there was an error and it was cleared. It will provide a string summary of the error in the message parameter if it is not `NULL`.
!!!
You are responsible for freeing `message`.
!!!
#### `bool pkpy_check_error(pkpy_vm*)`
Return true if the vm is currently in an error state.
#### `bool pkpy_error(pkpy_vm*, const char* name, pkpy_CString message)`
Set the error state of the vm. It is almost equivalent to `raise` in python.

View File

@ -1,3 +1,3 @@
label: C-API
icon: code
order: 1
order: 15

View File

@ -1,21 +0,0 @@
---
title: Specials
icon: dot
order: 6
---
+ `void pkpy_free(void* p)`
Wraps `free(p)` in C++.
+ `pkpy_CString pkpy_string(const char*)`
Construct a `pkpy_CString` from a null-terminated C string.
+ `pkpy_CName pkpy_name(const char*)`
Construct a `pkpy_CName` from a null-terminated C string. You should cache the result of this function if you are going to use it multiple times.
+ `pkpy_CString pkpy_name_to_string(pkpy_CName)`
Convert a `pkpy_CName` to a `pkpy_CString`.

View File

@ -6,294 +6,13 @@ order: 18
In order to use a C/C++ library in python, you need to write bindings for it.
pkpy uses an universal signature to wrap a function pointer as a python function or method that can be called in python code, i.e `NativeFuncC`.
pkpy uses an universal signature to wrap a C function pointer as a python function or method, i.e `py_CFunction`.
```cpp
typedef PyVar (*NativeFuncC)(VM*, ArgsView);
```c
typedef bool (*py_CFunction)(int argc, py_Ref argv);
```
+ The first argument is the pointer of `VM` instance.
+ The second argument is an array-like object indicates the arguments list. You can use `[]` operator to get the element and call `size()` to get the length of the array.
+ The return value is a `PyVar`, which should not be `nullptr`. If there is no return value, return `vm->None`.
+ `argc` is the number of arguments passed to the function.
+ `argv` is the pointer to the first argument.
## Bind a function or method
Use `vm->bind` to bind a function or method.
+ `PyVar bind(PyVar, const char* sig, NativeFuncC)`
+ `PyVar bind(PyVar, const char* sig, const char* docstring, NativeFuncC)`
```cpp
vm->bind(obj, "add(a: int, b: int) -> int", [](VM* vm, ArgsView args){
int a = py_cast<int>(vm, args[0]);
int b = py_cast<int>(vm, args[1]);
return py_var(vm, a + b);
});
// or you can provide a docstring
vm->bind(obj,
"add(a: int, b: int) -> int",
"add two integers", [](VM* vm, ArgsView args){
int a = py_cast<int>(vm, args[0]);
int b = py_cast<int>(vm, args[1]);
return py_var(vm, a + b);
});
```
### How to capture something
By default, the lambda being bound is a C function pointer,
you cannot capture anything! The following example does not compile.
```cpp
int x = 1;
vm->bind(obj, "f() -> int", [x](VM* vm, ArgsView args){
// error: cannot capture 'x'
return py_var(vm, x);
});
```
I do not encourage you to capture something in a lambda being bound
because:
1. Captured lambda runs slower and causes "code-bloat".
2. Captured values are unsafe, especially for `PyVar` as they could leak by accident.
However, there are 3 ways to capture something when you really need to.
The most safe and elegant way is to subclass `VM` and add a member variable.
```cpp
class YourVM : public VM{
public:
int x;
YourVM() : VM() {}
};
int main(){
YourVM* vm = new YourVM();
vm->x = 1;
vm->bind(obj, "f() -> int", [](VM* _vm, ArgsView args){
// do a static_cast and you can get any extra members of YourVM
YourVM* vm = static_cast<YourVM*>(_vm);
return py_var(vm, vm->x);
});
return 0;
}
```
The 2nd way is to use `vm->bind`'s last parameter `userdata`, you can store an `pkpy::any` object.
And use `lambda_get_userdata<T>(args.begin())` to get it inside the lambda body.
```cpp
int x = 1;
vm->bind(obj, "f() -> int", [](VM* vm, ArgsView args){
// get the userdata
int x = lambda_get_userdata<int>(args.begin());
return py_var(vm, x);
}, x); // capture x
```
## Bind a class or struct
Assume you have a struct `Point` declared as follows.
```cpp
struct Point{
int x;
int y;
}
```
You can create a `test` module and use `vm->register_user_class<>` to bind the class to the test module.
```cpp
int main(){
VM* vm = new VM();
PyObject* mod = vm->new_module("test");
vm->register_user_class<Point>(mod, "Point",
[](VM* vm, PyVar mod, PyVar type){
// wrap field x
vm->bind_field(type, "x", &Point::x);
// wrap field y
vm->bind_field(type, "y", &Point::y);
// __init__ method
vm->bind(type, "__init__(self, x, y)", [](VM* vm, ArgsView args){
Point& self = _py_cast<Point&>(vm, args[0]);
self.x = py_cast<int>(vm, args[1]);
self.y = py_cast<int>(vm, args[2]);
return vm->None;
});
});
// use the Point class
vm->exec("import test");
vm->exec("a = test.Point(1, 2)");
vm->exec("print(a.x)"); // 1
vm->exec("print(a.y)"); // 2
delete vm;
return 0;
}
```
### Handle gc for container types
If your custom type stores `PyVar` in its fields, you need to handle gc for them.
```cpp
struct Container{
PyVar a;
std::vector<PyVar> b;
// ...
}
```
Add a magic method `_gc_mark(VM*) const` to your custom type.
```cpp
struct Container{
PyVar a;
std::vector<PyVar> b;
// ...
void _gc_mark(VM* vm) const{
// mark a
vm->obj_gc_mark(a);
// mark elements in b
for(PyVar obj : b){
vm->obj_gc_mark(obj);
}
}
}
```
For global objects, use the callback in `vm->heap`.
```cpp
void (*_gc_marker_ex)(VM*) = nullptr;
```
It will be invoked before a GC starts. So you can mark objects inside the callback to keep them alive.
## Others
For some magic methods, we provide specialized binding function.
They do not take universal function pointer as argument.
You need to provide the detailed `Type` object and the corresponding function pointer.
```cpp
PyVar f_add(VM* vm, PyVar lhs, PyVar rhs){
int a = py_cast<int>(vm, lhs);
int b = py_cast<int>(vm, rhs);
return py_var(vm, a + b);
}
vm->bind__add__(vm->tp_int, f_add);
```
This specialized binding function has optimizations and result in better performance when calling from python code.
For example, `vm->bind__add__` is preferred over `vm->bind_func(type, "__add__", 2, f_add)`.
## Further reading
See [random.cpp](https://github.com/pocketpy/pocketpy/blob/main/src/random.cpp) for an example used by `random` module.
See [collections.cpp](https://github.com/pocketpy/pocketpy/blob/main/src/collections.cpp) for a modern implementation of `collections.deque`.
## Reuse Lua bindings
pkpy provides a lua bridge to reuse lua bindings.
It allows you to run lua code and call lua functions in python
by embedding a lua virtual machine.
Add `lua_bridge.hpp` and `lua_bridge.cpp` in [3rd/lua_bridge](https://github.com/pocketpy/pocketpy/tree/main/3rd/lua_bridge) to your project.
Make sure `lua.h`, `lualib.h` and `lauxlib.h` are in your include path
because `lua_bridge.hpp` needs them.
The lua bridge is based on lua 5.1.5 for maximum compatibility.
lua 5.2 or higher should also work.
### Setup
Use `initialize_lua_bridge(VM*, lua_State*)` to initialize the lua bridge.
This creates a new module `lua` in your python virtual machine.
You can use `lua.dostring` to execute lua code and get the result.
And use `lua.Table()` to create a lua table.
A `lua.Table` instance in python is a dict-like object which provides a bunch of
magic methods to access the underlying lua table.
```python
class Table:
def keys(self) -> list:
"""Return a list of keys in the table."""
def values(self) -> list:
"""Return a list of values in the table."""
def items(self) -> list[tuple]:
"""Return a list of (key, value) pairs in the table."""
def __len__(self) -> int:
"""Return the length of the table."""
def __contains__(self, key) -> bool:
"""Return True if the table contains the key."""
def __getitem__(self, key): ...
def __setitem__(self, key, value): ...
def __delitem__(self, key): ...
def __getattr__(self, key): ...
def __setattr__(self, key, value): ...
def __delattr__(self, key): ...
```
Only basic types can be passed between python and lua.
The following table shows the type mapping.
If you pass an unsupported type, an exception will be raised.
| Python type | Lua type | Allow create in Python? | Reference? |
| ----------- | -------- | ---------------------- | --------- |
| `None` | `nil` | YES | NO |
| `bool` | `boolean` | YES | NO |
| `int` | `number` | YES | NO |
| `float` | `number` | YES | NO |
| `str` | `string` | YES | NO |
| `tuple` | `table` | YES | NO |
| `list` | `table` | YES | NO |
| `dict` | `table` | YES | NO |
| `lua.Table` | `table` | YES | YES |
| `lua.Function`| `function`| NO | YES |
### Example
```cpp
#include "lua_bridge.hpp"
using namespace pkpy;
int main(){
VM* vm = new VM();
// create lua state
lua_State* L = luaL_newstate();
luaL_openlibs(L);
// initialize lua bridge
initialize_lua_bridge(vm, L);
// dostring to get _G
vm->exec("import lua");
vm->exec("g = lua.dostring('return _G')");
// create a table
vm->exec("t = lua.Table()");
vm->exec("t.a = 1");
vm->exec("t.b = 2");
// call lua function
vm->exec("g.print(t.a + t.b)"); // 3
return 0;
}
```
If successful, the function should return `true` and set the return value in `py_retval()`. In case there is no return value, you should use `py_newnone(py_retval())`.
If an error occurs, the function should raise an exception and return `false`.

View File

@ -1,333 +0,0 @@
---
icon: log
title: 'Cheatsheet'
order: 22
---
## Basics
Setup pocketpy
```cpp
#include "pocketpy.h"
using namespace pkpy;
```
Create a python virtual machine
```cpp
VM* vm = new VM();
```
Dispose a python virtual machine
```cpp
delete vm;
```
Execute a source string
```cpp
vm->exec("print('Hello!')");
```
Evaluate a source string
```cpp
PyVar obj = vm->eval("123");
std::cout << py_cast<int>(vm, obj); // 123
```
Compile a source string into a code object
```cpp
CodeObject_ co = vm->compile("print('Hello!')", "main.py", EXEC_MODE);
```
Execute a compiled code object
```cpp
try{
vm->_exec(co); // may throw
}catch(TopLevelException e){
std::cerr << e.summary() << std::endl;
}
```
## Interop with native types
Create primitive objects
```cpp
PyVar obj;
obj = py_var(vm, 1); // create a int
obj = py_var(vm, 1.0); // create a float
obj = py_var(vm, "123"); // create a string
obj = py_var(vm, true); // create a bool
```
Create a tuple object
```cpp
// obj = (1, 1.0, '123')
Tuple t(3);
t[0] = py_var(vm, 1);
t[1] = py_var(vm, 1.0);
t[2] = py_var(vm, "123");
PyVar obj = py_var(vm, std::move(t));
```
Create a list object
```cpp
// obj = [1, 1.0, '123']
List t;
t.push_back(py_var(vm, 1));
t.push_back(py_var(vm, 1.0));
t.push_back(py_var(vm, "123"));
PyVar obj = py_var(vm, std::move(t));
```
Create a dict object
```cpp
// obj = {'x': 1, 'y': '123'}
Dict d(vm);
d.set(py_var(vm, "x"), py_var(vm, 1));
d.set(py_var(vm, "y"), py_var(vm, "123"));
PyVar obj = py_var(vm, std::move(d));
```
Get native types from python objects
```cpp
PyVar obj;
i64 a = py_cast<i64>(vm, obj);
f64 b = py_cast<f64>(vm, obj);
Str& c = py_cast<Str&>(vm, obj); // reference cast
bool d = py_cast<bool>(vm, obj);
Tuple& e = py_cast<Tuple&>(vm, obj); // reference cast
List& f = py_cast<List&>(vm, obj); // reference cast
Dict& g = py_cast<Dict&>(vm, obj); // reference cast
```
Get native types without type checking
```cpp
// unsafe version 1 (for int object you must use `_py_cast`)
i64 a = _py_cast<i64>(vm, obj);
f64 b = _py_cast<f64>(vm, obj);
Tuple& c = _py_cast<Tuple&>(vm, obj);
// unsafe version 2 (for others, you can also use `PK_OBJ_GET` macro)
Str& a_ = PK_OBJ_GET(Str, obj);
List& b_ = PK_OBJ_GET(List, obj);
Tuple& c_ = PK_OBJ_GET(Tuple, obj);
```
## Access python types
Access built-in python types
```cpp
PyVar int_t = vm->_t(VM::tp_int);
PyVar float_t = vm->_t(VM::tp_float);
PyVar object_t = vm->_t(VM::tp_object);
PyVar tuple_t = vm->_t(VM::tp_tuple);
PyVar list_t = vm->_t(VM::tp_list);
```
Access user registered types
```cpp
Type voidp_t = vm->_tp_user<VoidP>();
```
Check if an object is a python type
```cpp
PyVar obj;
bool ok = is_type(obj, VM::tp_int); // check if obj is an int
```
Get the type of a python object
```cpp
PyVar obj = py_var(vm, 1);
PyVar t = vm->_t(obj); // <class 'int'>
Type type = vm->_tp(obj); // VM::tp_int
```
Convert a type object into a type index
```cpp
PyVar int_t = vm->_t(VM::tp_int);
Type t = PK_OBJ_GET(Type, int_t);
// t == VM::tp_int
```
## Access attributes
Check an object supports attribute access
```cpp
PyVar obj;
bool ok = !is_tagged(obj) && obj->is_attr_valid();
```
```python
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
def sum(self):
return self.x + self.y
```
Get and set attributes
```cpp
PyVar obj = vm->exec("MyClass(1, 2)");
PyVar x = vm->getattr(obj, "x"); // obj.x
vm->setattr(obj, "x", py_var(vm, 3)); // obj.x = 3
```
## Call python functions
```python
def add(a, b):
return a + b
```
Call a function
```cpp
PyVar f_add = vm->eval("add");
PyVar ret = vm->call(f_add, py_var(vm, 1), py_var(vm, 2));
std::cout << py_cast<int>(vm, ret); // 3
```
Call a method
```cpp
PyVar obj = vm->exec("MyClass(1, 2)");
PyVar ret = vm->call_method(obj, "sum");
std::cout << CAST(i64, ret); // 3
```
Cache the name of a function or method to avoid string-based lookup
```cpp
// cache the name "add" to avoid string-based lookup
const static StrName m_sum("sum");
PyVar ret = vm->call_method(obj, m_sum);
```
## Special operations
Compare two python objects
```cpp
PyVar obj1 = py_var(vm, 1);
PyVar obj2 = py_var(vm, 2);
bool ok = vm->py_eq(obj1, obj2);
```
Convert a python object to string
```cpp
PyVar obj = py_var(vm, 123);
std::cout << vm->py_str(obj); // 123
```
Get the string representation of a python object
```cpp
PyVar obj = py_var(vm, "123");
std::cout << vm->py_repr(obj); // "'123'"
```
Get the JSON representation of a python object
```cpp
PyVar obj = py_var(vm, 123);
std::cout << vm->py_json(obj); // "123"
```
Get the hash value of a python object
```cpp
PyVar obj = py_var(vm, 1);
i64 h = vm->py_hash(obj); // 1
```
Get the iterator of a python object
```cpp
PyVar obj = vm->eval("range(3)");
PyVar iter = vm->py_iter(obj);
```
Get the next item of an iterator
```cpp
PyVar obj = vm->py_next(iter);
if(obj == vm->StopIteration){
// end of iteration
}else{
// process obj
}
```
Convert a python iterable to a list
```cpp
PyVar obj = vm->eval("range(3)");
List list = vm->py_list(obj);
```
## Bindings
Bind a native function
```cpp
vm->bind(obj, "add(a: int, b: int) -> int", [](VM* vm, ArgsView args){
int a = py_cast<int>(vm, args[0]);
int b = py_cast<int>(vm, args[1]);
return py_var(vm, a + b);
});
```
Bind a property
```cpp
// getter and setter of property `x`
vm->bind_property(type, "x: int",
[](VM* vm, ArgsView args){
Point& self = PK_OBJ_GET(Point, args[0]);
return VAR(self.x);
},
[](VM* vm, ArgsView args){
Point& self = PK_OBJ_GET(Point, args[0]);
self.x = py_cast<int>(vm, args[1]);
return vm->None;
});
```
## Modules
Create a source module
```cpp
vm->_lazy_modules["test"] = "pi = 3.14";
// import test
// print(test.pi) # 3.14
```
Create a native module
```cpp
PyObject* mod = vm->new_module("test");
vm->setattr(mod, "pi", py_var(vm, 3.14));
```

View File

@ -1,106 +0,0 @@
---
icon: book
order: -5
label: Coding Style Guide
---
# Coding Style Guide
## For Python
Use [PEP-8](https://www.python.org/dev/peps/pep-0008/) as the coding style guide.
## For C++
### Naming rules
For class names, always use **PascalCase**
```cpp
// Correct
class FooBar {};
// Wrong
class fooBar {};
class foo_bar {};
```
For function and methods, use **snake_case**
```cpp
// Correct
int test_func(int x) { return x+1; }
// Wrong
int TestFunc(int x) { return x+1; }
int testFunc(int x) { return x+1; }
```
For special python objects, use the same name as in python.
```cpp
auto x = vm->None;
vm->SyntaxError(...);
vm->TypeError(...);
vm->call(obj, __repr__);
```
For global constants, use **k** prefix with **PascalCase**
```cpp
const int kMaxCount = 10;
const float kMinValue = 1.0;
```
For macros, use **SNAKE_CASE**
```cpp
#define FOO_BAR 1
#define TEST(x) x+1
```
### Access control
Please use python style access control.
We do not recommend to use C++ keywords such as `private` or `public` to achieve access control. Also do not write any trivial setter/getter.
Use a single `_` as prefix to indicate a function or variable is for internal use.
```cpp
class FooBar {
public:
int _count;
int inc() { _count+=1; }
void _clear() { _count=0; }
}
```
`_` prefix is just a warning to remind you to use such members carefully.
It does not forbid users to access internal members.
### Use compact style
Try to make the code compact if it does not affect readability.
```cpp
// Correct
if(x == 1) break;
// Wrong
if(x == 1){
break;
}
```
### For `std::shared_ptr<T>`
Use a `_` suffix to indicate a type is a shared pointer.
```cpp
using CodeObject_ = std::shared_ptr<CodeObject>;
CodeObject_ co = std::make_shared<CodeObject>();
```

View File

@ -1,27 +0,0 @@
---
icon: dot
title: Arbitrary Sized Integers
---
Unlike cpython, pkpy's `int` is of limited precision (64-bit).
For arbitrary sized integers, we provide a builtin `long` type, just like python2's `long`.
`long` is implemented via pure python in [_long.py](https://github.com/pocketpy/pocketpy/blob/main/python/_long.py).
### Create a long object
You can use `L` suffix to create a `long` literal from a decimal literal.
Also, you can use `long()` function to create a `long` object from a `int` object or a `str` object.
```python
a = 1000L
b = long(1000)
c = long('1000')
assert a == b == c
```
```python
a = 2L # use `L` suffix to create a `long` object
print(a ** 1000)
# 10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376L
```

View File

@ -1,2 +1,2 @@
order: 19
order: 0
label: "GSoC"

View File

@ -5,12 +5,11 @@ label: Welcome
# Welcome to pocketpy
pkpy is a lightweight(~15K LOC) Python interpreter for game scripting, built on C++17 with STL.
pkpy is a lightweight(~15K LOC) Python 3.x interpreter for game scripting, written in C11.
It aims to be an alternative to lua for game scripting, with elegant syntax, powerful features and competitive performance.
pkpy is extremely easy to embed via a single header file `pocketpy.h`, without external dependencies.
> **Caution**: pocketpy should not be your first C++ project. Please learn C++ programming, compiling, linking, and debugging before working with pocketpy. There are many resources for this on the net.
## What it looks like

View File

@ -19,11 +19,6 @@ In your CMakelists.txt, add the following lines:
```cmake
add_subdirectory(pocketpy)
target_link_libraries(<your_target> pocketpy)
if(EMSCRIPTEN)
# exceptions must be enabled for emscripten
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fexceptions")
endif()
```
See [CMakeLists.txt](https://github.com/pocketpy/pocketpy/blob/main/CMakeLists.txt) for details.
@ -34,14 +29,9 @@ It is safe to use `main` branch in production if CI badge is green.
To compile it with your project, these flags must be set:
+ `--std=c++17` flag must be set
+ RTTI must be enabled
+ Exception must be enabled
+ `--std=c11` flag must be set
+ For MSVC, `/utf-8` flag must be set
For emscripten, you must enable exceptions to make pocketpy work properly.
See https://emscripten.org/docs/porting/exceptions.html.
### Get prebuilt binaries
We have prebuilt binaries,
@ -77,41 +67,64 @@ You can download an artifact there which contains the following files.
### Example
```cpp
```c
#include "pocketpy.h"
#include <stdio.h>
using namespace pkpy;
static bool int_add(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(0, tp_int);
PY_CHECK_ARG_TYPE(1, tp_int);
py_i64 a = py_toint(py_arg(0));
py_i64 b = py_toint(py_arg(1));
py_newint(py_retval(), a + b);
return true;
}
int main() {
// Create a virtual machine
VM* vm = new VM();
// Initialize pocketpy
py_initialize();
// Hello world!
vm->exec("print('Hello world!')");
bool ok = py_exec("print('Hello world!')", "<string>", EXEC_MODE, NULL);
if(!ok) goto __ERROR;
// Create a list
vm->exec("a = [1, 2, 3]");
// Create a list: [1, 2, 3]
py_Ref r0 = py_getreg(0);
py_newlistn(r0, 3);
py_newint(py_list_getitem(r0, 0), 1);
py_newint(py_list_getitem(r0, 1), 2);
py_newint(py_list_getitem(r0, 2), 3);
// Eval the sum of the list
PyVar result = vm->eval("sum(a)");
std::cout << "Sum of the list: "<< py_cast<int>(vm, result) << std::endl; // 6
py_Ref f_sum = py_getbuiltin(py_name("sum"));
py_push(f_sum);
py_pushnil();
py_push(r0);
ok = py_vectorcall(1, 0);
if(!ok) goto __ERROR;
// Bindings
vm->bind(vm->_main, "add(a: int, b: int)",
[](VM* vm, ArgsView args){
int a = py_cast<int>(vm, args[0]);
int b = py_cast<int>(vm, args[1]);
return py_var(vm, a + b);
});
printf("Sum of the list: %d\n", (int)py_toint(py_retval())); // 6
// Call the function
PyVar f_add = vm->_main->attr("add");
result = vm->call(f_add, py_var(vm, 3), py_var(vm, 7));
std::cout << "Sum of 2 variables: "<< py_cast<int>(vm, result) << std::endl; // 10
// Bind native `int_add` as a global variable
py_newnativefunc(r0, int_add);
py_setglobal(py_name("add"), r0);
// Dispose the virtual machine
delete vm;
// Call `add` in python
ok = py_exec("add(3, 7)", "<string>", EVAL_MODE, NULL);
if(!ok) goto __ERROR;
py_i64 res = py_toint(py_retval());
printf("Sum of 2 variables: %d\n", (int)res); // 10
py_finalize();
return 0;
__ERROR:
py_printexc();
py_finalize();
return 1;
}
```
### Overview