Created
February 26, 2021 18:28
-
-
Save danieljfarrell/bc455548ae7ff9133104766ff55bb168 to your computer and use it in GitHub Desktop.
Idioms for handling C struct in Cython
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Idiom - C struct container simple values | |
# | |
# Idiom - Use ctypedef so we can can pass this | |
# easily in and out of functions. | |
# | |
cdef struct book: | |
int uid | |
ctypedef book book_t | |
# Idiom - C struct of arrays | |
# | |
# Struct owns a pointer to the data type | |
# and we include `count` so we know how | |
# many items are in the array. | |
# | |
cdef struct shelf: | |
book_t * books | |
int count | |
ctypedef shelf shelf_t | |
# Idiom - Struct create function | |
# | |
# This will return a pointer to a | |
# dynamically created struct. | |
cdef book_t * create_book(int) | |
# Idiom - Struct free function | |
# | |
# This takes the pointer created by the | |
# create function and frees the memory. | |
cdef void free_book(book_t*) | |
# Idiom - A container C struct can be created by copying the | |
# buffer of items. Use this if you want to maintain your | |
# own copy of the data. | |
cdef shelf_t * create_shelf_by_copying(book_t * books, int count) | |
# Idiom - A container C struct can be created by referencing | |
# the items in the input buffer. Use this if you want to | |
# share the items. | |
cdef shelf_t * create_shelf_by_referencing(book_t * books, int count) | |
cdef void free_shelf(shelf_t*) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from libc.stdlib cimport malloc, free | |
from libc.string cimport memcpy | |
cdef book_t * create_book(int uid): | |
# Idiom - Create function allocate memory | |
# Create function dynamically allocate memory to for | |
# one C struct. Later this "create" call should be | |
# paired with a "free" call to avoid a memory leak. | |
cdef book_t * mybook = <book_t*>malloc(sizeof(book_t)) | |
# Idiom - Cython allows direct access to the attributes | |
# of the C struct eventhrough it is a pointer to the | |
# C struct | |
mybook.uid = uid | |
return mybook | |
cdef void free_book(book_t* mybook): | |
# Idiom - Structs need to be freed. All "create" calls | |
# must be paired with a "free" call a the end of the | |
# struct's life. | |
free(mybook) | |
cdef shelf_t * create_shelf_by_copying(book_t * books, int count): | |
# Idiom - Container structs can be created by copying the | |
# contents of another buffer into dynamically allocated memory | |
# Dynamically allocate the shelf | |
cdef shelf_t * shelfptr = <shelf_t *> malloc(sizeof(shelf_t)) | |
# Dynamically allocate space on the shelf | |
shelfptr.books = <book_t *> malloc(sizeof(book_t) * count) | |
# Idiom - Note that eventhrough shelfptr is (shelf_t *) Cython allow | |
# us to access attribute directly using the dot notation. If coming | |
# from C background this is confusing because you would need to use | |
# `->` notiation. | |
# Copy the books buffer into out dynamically created buffer | |
memcpy(<book_t*> shelfptr.books, <book_t*>books, sizeof(book_t) * count) | |
return shelfptr | |
cdef shelf_t * create_shelf_by_referencing(book_t * books, int count): | |
# Idiom - Container structs can be created by reference the | |
# contents of another buffer! Useful to save space but only | |
# use if you know other threads are not writing to the buffer. | |
cdef shelf_t * shelfptr = <shelf_t *> malloc(sizeof(shelf_t)) | |
shelfptr.books = books | |
return shelfptr | |
cdef void free_shelf(shelf_t* shelf): | |
free(shelf) | |
cdef _c_test_shelf_create(): | |
print("Running Demo") | |
# Idiom - Dynamic allocation of array of C struct | |
cdef book_t * books = <book_t*> malloc(3 * sizeof(book_t)) | |
# Idiom - Use `[0]` to get the value of the pointer | |
# | |
# `create_book` returns a `book_t *`. To assign the value | |
# being pointed at we need to use [0] because Cython does | |
# not have the `*` operator. | |
# | |
# Moreover, in C in you can do: | |
# | |
# book_t * bookptr = create_book(42) | |
# book[0] = *bookptr | |
# book[0] = bookptr[0] | |
# | |
# Cython only supports the latter. | |
books[0] = create_book(42)[0] | |
books[1] = create_book(43)[0] | |
books[2] = create_book(44)[0] | |
print("Dynamically created 3 books!") | |
print("First book in books array has UID: {}".format(books[0].uid)) | |
print("Second book in books array has UID: {}".format(books[1].uid)) | |
print("Third book in books array has UID: {}".format(books[2].uid)) | |
print("") | |
print("Create shelf by COPYING contents of books array") | |
cdef shelf_t * copied_shelf = create_shelf_by_copying(books, 3) | |
print("First book in copied shelf has UID: {}".format(copied_shelf.books[0].uid)) | |
print("Second book in copied shelf has UID: {}".format(copied_shelf.books[1].uid)) | |
print("Third book in copied shelf has UID: {}".format(copied_shelf.books[2].uid)) | |
print("") | |
print("Mutating first book in copied shelf to have UID 99") | |
copied_shelf.books[0].uid = 99 | |
print("First book in copied shelf has UID: {}".format(copied_shelf.books[0].uid)) | |
print("First book in books array _still_ has UID: {}".format(books[0].uid)) | |
print("") | |
print("Create shelf by REFERENCE contents of books array") | |
cdef shelf_t * ref_shelf = create_shelf_by_referencing(books, 3) | |
print("First book in reference shelf has UID: {}".format(copied_shelf.books[0].uid)) | |
print("Second book in reference shelf has UID: {}".format(copied_shelf.books[1].uid)) | |
print("Third book in reference shelf has UID: {}".format(copied_shelf.books[2].uid)) | |
print("") | |
print("Mutating first book in reference shelf to have UID 99") | |
ref_shelf.books[0].uid = 99 | |
print("First book in reference shelf has UID: {}".format(ref_shelf.books[0].uid)) | |
print("First book in books array _now_ has UID: {}".format(books[0].uid)) | |
print("") | |
def _test_shelf_create(): | |
_c_test_shelf_create() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Output