# C++

For C++ developers, you can download the pocketpy.h on our GitHub release page.

https://github.com/blueloveTH/pocketpy/releases/latest

# Example

#include "pocketpy.h"

using namespace pkpy;

int main(){
    // Create a virtual machine
    VM* vm = new VM(true);
    
    // Hello world!
    vm->exec("print('Hello world!')", "main.py", EXEC_MODE);

    // Create a list
    vm->exec("a = [1, 2, 3]", "main.py", EXEC_MODE);

    // Eval the sum of the list
    PyVar result = vm->exec("sum(a)", "<eval>", EVAL_MODE);
    std::cout << py_cast<i64>(vm, result);   // 6
    return 0;
}

# Interop with PyVar

In PocketPy, any python object is represented by a PyVar.

  • VAR(...), create a PyVar from a C type
  • CAST(T, ...), cast a PyVar to a C type
  • _CAST(T, ...), cast a PyVar to a C type, without type check
PyVar x = VAR(12);		// cast a C int to PyVar
int y = CAST(int, x);		// cast a PyVar to C int

PyVar i = VAR("abc");
std::cout << CAST(Str, i);	// abc

# Types

PyVar type C type note
int i64 62 bits integer
float f64 62 bits floating point
str pkpy::Str
bool bool
list pkpy::List
tuple pkpy::Tuple
function pkpy::Function
... ... ...

# Bind a Native Function

In VM class, there are 4 methods to bind native function.

  • VM::bind_func<ARGC>
  • VM::bind_builtin_func<ARGC>
  • VM::bind_method<ARGC>
  • VM::bind_static_method<ARGC>

They are all template methods, the template argument is a int number, indicating the argument count. For variadic arguments, use -1. For methods, ARGC do not include self.

PkPy uses a universal function type for native functions:

std::function<PyVar(VM*, Args&)>

The first argument is the pointer of VM instance.

The second argument is a special array, a restricted and optimized std::vector<PyVar>. You can use [] operator to get the element. If you have specified ARGC other than -1, the interpreter will ensure args.size() == ARGC. No need to do size check.

The return value is a PyVar, which should not be nullptr. If there is no return value, return vm->None.

This is an example of binding the input() function to the builtins module.

VM* vm = pkpy_new_vm(true);
vm->bind_builtin_func<0>("input", [](VM* vm, Args& args){
    static std::string line;
    std::getline(std::cin, line);
    return VAR(line);
});

# Bind a Native Function with Proxy

For simple functions, you can use ProxyFunc to simplify the binding process. A ProxyFunc is a wrapper of a native function. It can be constructed from a function pointer, a lambda function, or a std::function.

It will automatically call VAR(...) and CAST(T, ...) when necessary.

double add(int a, double b){
    return a + b;
}

vm->bind_builtin_func<2>("add", NativeProxyFunc(&add));

# Call a Python Function

Use these to call a python function.

// call a python function to get its result
vm::call(const PyVar& obj, const Str& func, Args args);

// call a python class to create an instance
vm::call(const PyVar& obj);

For example, to create a dict object,

const PyVar& tp = vm->builtins->attr("dict");
PyVar obj = vm->call(tp);	// this is a `dict`

And set a key-value pair,

call(obj, "__setitem__", two_args(VAR("a"), VAR(5)));
PyVar ret = call(obj, "__getitem__", one_arg(VAR("a")));
std::cout << CAST(int, ret) << std::endl; // 5

# Wrapping a struct as PyObject