Skip to main content

Automatically creates the C++ and Python-side wrappers for ctypes bindings.

Project description

Project generated with PyScaffold PyPI-Server Unit tests

Generate ctypes wrappers

Overview

This script automatically creates the C++ and Python-side wrappers for ctypes bindings. Specifically, we fill restype and argtypes based on the C++ function signature and we create wrappers to handle C++ exceptions. We were inspired by the Rcpp::compile() function, which does the same for C++ code in R packages. The aim is to avoid errors from manual binding when developing ctypes-based Python packages.

Install

cpptypes is published to PyPI:

pip install cpptypes

Quick start

To use, add an // [[export]] tag above the C++ function to be exported to Python.

// [[export]]
int multiply(int a, double b) {
    return a * b;
}

We assume that all C++ code is located within a single directory src. We then run the cpptypes cli provided by this package:

cpptypes src/ --py bindings.py --cpp bindings.cpp

Developers should add bindings.cpp to the Extension sources in their setup.py. The exported function itself can then be used in Python code with:

from .bindings import * as cxx

cxx.multiply(1, 2)

Handling pointers

Pointers to base types (or void) are supported and will be bound with the appropriate ctypes pointer type.

//[[export]]
void* create_complex_object(int* my_int, double* my_dbl) {
    return reinterpret_cast<void*>(new Something(my_int, my_dbl));
}

And then, in Python (assuming we called our Python bindings file bindings.py):

import ctypes 
x = ctypes.c_int(100)
y = ctypes.c_double(200)

from .bindings import * as cxx
ptr = cxx.create_complex_object(ctypes.byref(x), ctypes.pointer(y))

Void pointers are represented as a (usually 64-bit) integer in Python that can be passed back to C++. Remember to cast void* back to the appropriate type before doing stuff with it! (For simplicity, we do not support arbitrary pointer types as otherwise we would need to include the header definitions in the bindings.cpp file and that would be tedious to track.)

If you want the ctypes bindings to treat pointers to base types as void*, you can tag the argument with void_p. This means that you can directly pass integer addresses to my_int and my_dbl in Python rather than casting them to a ctypes.POINTER type.

//[[export]]
void* create_complex_object2(int* my_int /** void_p */, double* my_dbl /** void_p */) {
    return reinterpret_cast<void*>(new Something(my_int, my_dbl));
}

Note on tags: Arguments can be tagged with a /** xxx yyy zzz */ comment, consisting of space-separated tags that determine how the type should be handled in the wrappers. (The double ** is important to distinguish from non-tag comments.) The tag-containing comment can be inserted anywhere in or next to the argument, e.g., before the type, between the type and the name, after the name but before the comma/parenthesis. The result type for the function can also be tagged in the same manner.

Handling NumPy arrays

If we know a certain pointer is derived from a NumPy array, we can add the numpy tag to automate type checking and address extraction.

//[[export]]
void* create_complex_object3(int32_t* my_int /** numpy */, double* my_dbl /** numpy */) {
    return reinterpret_cast<void*>(new Something(my_int, my_dbl));
}

Then, in Python, we can just pass the arrays directly to the bound function:

import numpy
x = numpy.random.rand(1000).astype(numpy.int32)
y = numpy.random.rand(1000).astype(numpy.double)
cxx.create_complex_object3(x, y)

This will check that the NumPy arrays correspond to the specified type before calling the C++ function. It is best to use fixed-width integers rather than relying on machine-dependent aliases like int, short, etc.

The wrapper functions will also check that the arrays are contiguous in memory. If you want to support non-contiguous arrays, add the non_contig tag to the relevant arguments.

The numpy tag is only relevant to function arguments and not return values. Use the numpy.ctypeslib.as_array() function to convert a ctypes pointer to a numpy array of the relevant type.

Known limitations

  • Not all ctypes types are actually supported, mostly out of laziness.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cpptypes-0.1.0.tar.gz (24.5 kB view details)

Uploaded Source

Built Distribution

cpptypes-0.1.0-py3-none-any.whl (10.9 kB view details)

Uploaded Python 3

File details

Details for the file cpptypes-0.1.0.tar.gz.

File metadata

  • Download URL: cpptypes-0.1.0.tar.gz
  • Upload date:
  • Size: 24.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.18

File hashes

Hashes for cpptypes-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c7f9f84e3fed926af500ceea5d78c6f4e8f90199efbc2b218a26da75167e2f45
MD5 fdeede83b70312f5a7cd27a201452454
BLAKE2b-256 637147dce62223172ad528673392e359f08c252deed78f97749b12cca6294e9d

See more details on using hashes here.

File details

Details for the file cpptypes-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: cpptypes-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.18

File hashes

Hashes for cpptypes-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ba00c0128654dbc985beae4161e43bed7b0a6fa262fefa20bf849c8a46c1c191
MD5 b1eb70d1bee48eb031dfec983b0514fd
BLAKE2b-256 b318a6cf433f21fe2ad24d33e72fbd8a9bc8ea1c816724ebbc05a691a3447c25

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page