mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 19:40:18 +00:00
add examples for c binding
This commit is contained in:
parent
3f5c460761
commit
04fd576f84
163
docs/bindings.md
163
docs/bindings.md
@ -19,6 +19,7 @@ If an error occurs, the function should raise an exception and return `false`.
|
|||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
|
### Bind a simple function
|
||||||
Say you have a function `add` that takes two integers and returns their sum.
|
Say you have a function `add` that takes two integers and returns their sum.
|
||||||
```c
|
```c
|
||||||
int add(int a, int b) {
|
int add(int a, int b) {
|
||||||
@ -59,6 +60,168 @@ py_GlobalRef mod = py_getmodule("__main__");
|
|||||||
py_bind(mod, "add(a, b=1)", py_add);
|
py_bind(mod, "add(a, b=1)", py_add);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Bind a struct
|
||||||
|
If you have a struct like this:
|
||||||
|
```c
|
||||||
|
typedef struct MyStruct{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
}MyStruct;
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's how you can create a `MyStruct`:
|
||||||
|
```c
|
||||||
|
// 1. Define the "new" function to create a MyStruct.
|
||||||
|
MyStruct* py_new_MyStruct(py_OutRef out, int x, int y) {
|
||||||
|
// 2. Create a new object.
|
||||||
|
MyStruct* res = py_newobject(out, tp_object, 2, sizeof(MyStruct));
|
||||||
|
// 3. Put x and y into the object.
|
||||||
|
res->x = x;
|
||||||
|
res->y = y;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// 4. Define a wrapper function with the signature `py_CFunction`.
|
||||||
|
bool MyStruct__new__(int argc, py_Ref argv) {
|
||||||
|
// 5. Check the number of arguments.
|
||||||
|
PY_CHECK_ARGC(3);
|
||||||
|
// 6. Check the type of arguments.
|
||||||
|
PY_CHECK_ARG_TYPE(0, tp_type);
|
||||||
|
PY_CHECK_ARG_TYPE(1, tp_int);
|
||||||
|
PY_CHECK_ARG_TYPE(2, tp_int);
|
||||||
|
// 7. Convert the arguments into C types.
|
||||||
|
int x = py_toint(py_arg(1));
|
||||||
|
int y = py_toint(py_arg(2));
|
||||||
|
// 8. Call the previous function to create MyStruct.
|
||||||
|
MyStruct* res = py_new_MyStruct(py_pushtmp(), x, y);
|
||||||
|
// 9. Set the created MyStruct into the return value register.
|
||||||
|
py_assign(py_retval(), py_peek(-1));
|
||||||
|
// 10. The result is already in the return value register, pop MyStruct is safe now.
|
||||||
|
py_pop();
|
||||||
|
// 11. Don't forget to return `true`.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We also would like to have a function to get x from `MyStruct`:
|
||||||
|
```c
|
||||||
|
bool MyStruct_x(int argc, py_Ref argv) {
|
||||||
|
// 1. Check the number of arguments.
|
||||||
|
PY_CHECK_ARGC(1);
|
||||||
|
// 2. Convert the arguments into C types.
|
||||||
|
MyStruct* self = py_touserdata(argv);
|
||||||
|
// 3. Set the x value.
|
||||||
|
py_newint(py_retval(), self->x);
|
||||||
|
// 4. Return `true`.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Similar to the simple function binding, you can bind your struct to a python module:
|
||||||
|
```c
|
||||||
|
// 1. Put MyStruct into a new module `mystruct`
|
||||||
|
py_GlobalRef mod = py_newmodule("mystruct");
|
||||||
|
// 2. MyStruct is named as `custom_struct`
|
||||||
|
py_Type mystruct = py_newtype("custom_struct", tp_object, mod, NULL);
|
||||||
|
// 3. Bind functions.
|
||||||
|
py_bind(py_tpobject(mystruct), "__new__(cls, x: int, y: int)", MyStruct__new__);
|
||||||
|
py_bind(mod, "get_x(cls: mystruct)", MyStruct_x);
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can use MyStruct like this:
|
||||||
|
```python
|
||||||
|
import mystruct
|
||||||
|
test = mystruct.custom_struct(10, 100)
|
||||||
|
print(mystruct.get_x(test))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bind a function with arbitrary argument lists
|
||||||
|
Sometimes you want a function that takes arbitrary input arguments. For example, sum several numbers in the table,
|
||||||
|
or make a simple `print` function.
|
||||||
|
|
||||||
|
#### Sum several numbers
|
||||||
|
Say you have 2,3,4,5,6 and put them into the `sum` function. Here's an implementation:
|
||||||
|
```c
|
||||||
|
bool py_sum(int argc, py_Ref argv) {
|
||||||
|
// 1. These numbers are packed as a tuple
|
||||||
|
PY_CHECK_ARG_TYPE(0, tp_tuple);
|
||||||
|
// 2. Get the length of the tuple
|
||||||
|
int len = py_tuple_len(py_arg(0));
|
||||||
|
int res = 0;
|
||||||
|
// 3. Sum the numbers up.
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
int _0 = py_toint(py_tuple_getitem(py_arg(0), i));
|
||||||
|
res += _0;
|
||||||
|
}
|
||||||
|
// 4. Set the result.
|
||||||
|
py_newint(py_retval(), res);
|
||||||
|
// 5. Return `true`.
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And then bind it:
|
||||||
|
```c
|
||||||
|
py_GlobalRef mod = py_newmodule("sumary");
|
||||||
|
py_bind(mod, "sum(*values: tuple[int])", py_sum);
|
||||||
|
```
|
||||||
|
|
||||||
|
It can be used like this:
|
||||||
|
```python
|
||||||
|
import sumary
|
||||||
|
print(sumary.sum(2,3,4,5,6))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Make a simple print function
|
||||||
|
Let's make a simple print function now. It takes arbitrary argument `*values`, and `end`/`sep`
|
||||||
|
is not necessary. It's so simple that only string argument is acceptable.
|
||||||
|
|
||||||
|
Here's an implementation:
|
||||||
|
```c
|
||||||
|
bool py_print(int argc, py_Ref argv) {
|
||||||
|
// 1. *values is always a tuple.
|
||||||
|
PY_CHECK_ARG_TYPE(0, tp_tuple);
|
||||||
|
// 2. Get the length of tuple.
|
||||||
|
int len = py_tuple_len(py_arg(0));
|
||||||
|
const char* end = "\n";
|
||||||
|
const char* sep = " ";
|
||||||
|
// 3. First arg is sep, but it could be None.
|
||||||
|
if (!py_isnone(py_arg(1))) {
|
||||||
|
PY_CHECK_ARG_TYPE(1, tp_str);
|
||||||
|
sep = py_tostr(py_arg(1));
|
||||||
|
}
|
||||||
|
// 4. Second arg is end, it also can be None.
|
||||||
|
if (!py_isnone(py_arg(2))) {
|
||||||
|
PY_CHECK_ARG_TYPE(2, tp_str);
|
||||||
|
end = py_tostr(py_arg(2));
|
||||||
|
}
|
||||||
|
// 5. Print.
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
printf("%s", sep);
|
||||||
|
}
|
||||||
|
// 6. It can print iterable like `list` if you modify this line.
|
||||||
|
printf("%s", py_tostr(py_tuple_getitem(py_arg(0), i)));
|
||||||
|
}
|
||||||
|
printf("%s", end);
|
||||||
|
// 7. All the functions should return a value, here None is returned.
|
||||||
|
py_newnone(py_retval());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And then bind:
|
||||||
|
```c
|
||||||
|
py_GlobalRef mod = py_newmodule("myprint");
|
||||||
|
py_bind(mod, "my_print(*values: object, sep: str | None = None, end: str | None = None)", py_print);
|
||||||
|
```
|
||||||
|
|
||||||
|
It can print names like this:
|
||||||
|
```python
|
||||||
|
import myprint
|
||||||
|
myprint.my_print('Bob','Mary', end = 'Cake', sep = '|')
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
See also:
|
See also:
|
||||||
+ [`py_bind`](/c-api/functions/#py_bind)
|
+ [`py_bind`](/c-api/functions/#py_bind)
|
||||||
+ [`py_bindmethod`](/c-api/functions/#py_bindmethod)
|
+ [`py_bindmethod`](/c-api/functions/#py_bindmethod)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user