C++ has a far richer set of builtin types than Python. Most Python code can remain relatively agnostic to that, and
cppyy
provides automatic conversions as appropriate. On the other hand, Python builtin types such as lists and maps are far richer than any builtin types in C++. These are mapped to their Standard Template Library equivalents instead.
The C++ code used for the examples below can be found
here
, and it is assumed that that code is loaded before running any of the example code snippets. Download it, save it under the name
features.h
, and simply include it:
>>> import cppyy >>> cppyy.include('features.h') >>>
The selection of builtin data types varies greatly between Python and C++. Where possible, builtin data types map onto the expected equivalent Python types, with the caveats that there may be size differences, different precision or rounding, etc. For example, a C++
float
is returned as a Python
float
, which is in fact a C++
double
. If sizes allow, conversions are automatic. For example, a C++
unsigned int
becomes a Python2
long
or Python3
int
, but unsigned-ness is still honored:
>>> cppyy.gbl.gUint 0L >>> type(cppyy.gbl.gUint) <type 'long'> >>> cppyy.gbl.gUint = -1 Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: cannot convert negative integer to unsigned >>>
On some platforms, 8-bit integer types such as
int8_t
and
uint8_t
are represented as
char
types. For consistency, these are mapped onto Python
int
.
Some types are builtin in Python, but (STL) classes in C++. Examples are
str
vs.
std::string
(see also the
字符串
section) and
complex
vs.
std::complex
. These classes have been pythonized to behave the same wherever possible. For example, string comparison work directly, and
std::complex
has
real
and
imag
properties:
>>> c = cppyy.gbl.std.complex['double'](1, 2) >>> c (1+2j) >>> c.real, c.imag (1.0, 2.0) >>> s = cppyy.gbl.std.string("aap") >>> type(s) <class cppyy.gbl.std.string at 0x7fa75edbf8a0> >>> s == "aap" True >>>
To pass an argument through a C++
char
(signed or unsigned) use a Python string of size 1. In many cases, the explicit C types from module
ctypes
can also be used, but that module does not have a public API (for type conversion or otherwise), so support is somewhat limited.
There are automatic conversions between C++’s
std::vector
and Python’s
list
and
tuple
, where possible, as they are often used in a similar manner. These datatypes have completely different memory layouts, however, and the
std::vector
requires that all elements are of the same type and laid out consecutively in memory. Conversion thus requires type checks, memory allocation, and copies. This can be rather expensive. See the section on
STL
.
Builtin arrays are supported through arrays from module
array
(or any other builtin-type array that implements the Python buffer interface, such as numpy arrays) and a low-level view type from
cppyy
for returns and variable access (that implements the buffer interface as well). Out-of-bounds checking is limited to those cases where the size is known at compile time. Example:
>>> from cppyy.gbl import Concrete >>> from array import array >>> c = Concrete() >>> c.array_method(array('d', [1., 2., 3., 4.]), 4) 1 2 3 4 >>> c.m_data[4] # static size is 4, so out of bounds Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: buffer index out of range >>>
Arrays of arrays are supported through the C++ low-level view objects. This only works well if sizes are known at compile time or can be inferred. If sizes are not known, the size is set to a large integer (depending on the array element size) to allow access. It is then up to the developer not to access the array out-of-bounds. There is limited support for arrays of instances, but those should be avoided in C++ anyway:
>>> cppyy.cppdef('std::string str_array[3][2] = {{"aa", "bb"}, {"cc", "dd"}, {"ee", "ff"}};') True >>> type(cppyy.gbl.str_array[0][1]) <class cppyy.gbl.std.string at 0x7fd650ccb650> >>> cppyy.gbl.str_array[0][1] 'bb' >>> cppyy.gbl.str_array[4][0] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: tuple index out of range >>>
When the C++ code takes a pointer or reference type to a specific builtin type (such as an
unsigned int
for example), then types need to match exactly.
cppyy
supports the types provided by the standard modules
ctypes
and
array
for those cases. Example of using a reference to builtin:
>>> from ctypes import c_uint >>> u = c_uint(0) >>> c.uint_ref_assign(u, 42) >>> u.value 42 >>>
For objects, an object, a pointer to an object, and a smart pointer to an object are represented the same way, with the necessary (de)referencing applied automatically. Pointer variables are also bound by reference, so that updates on either the C++ or Python side are reflected on the other side as well.
Named, anonymous, and class enums are supported. The Python-underlying type of an enum is implementation dependent and may even be different for different enums on the same compiler. Typically, however, the types are
int
or
unsigned int
, which translates to Python’s
int
or
long
on Python2 or class
int
on Python3. Separate from the underlying, all enums have their own Python type to allow them to be used in template instantiations:
>>> from cppyy.gbl import kBanana # classic enum, globally available >>> print(kBanana) 29 >>> cppyy.gbl.EFruit <class '__main__.EFruit'> >>> print(cppyy.gbl.EFruit.kApple) 78 >>> cppyy.gbl.E1 # C++11 class enum, scoped Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: <namespace cppyy.gbl at 0x7ff2766a4af0> has no attribute 'E1'. >>> cppyy.gbl.NamedClassEnum.E1 42 >>>