Exclude members and classes¶
Sometimes, you may want to exclude classes and/or members from the bindings. Look at the example below for instructions:
import litgen
from litgen.demo import litgen_demo
cpp_code = """
class FooDetails // A class that we want to exclude from the bindings
{
// ...
};
struct Foo
{
int X = 0, Y = 1;
FooDetails mDetails = {}; // A member that we would want to exclude from the bindings
};
"""
options = litgen.LitgenOptions()
options.class_exclude_by_name__regex = r"Details$"
options.member_exclude_by_name__regex = r"Details$"
litgen_demo.demo(options, cpp_code)Default constructor with named params¶
litgen will automatically generate a default constructor with named params for structs. For classes, you can use options.class_create_default_named_ctor__regex.
This constructor is generated only if the class/struct does not provide a default constructor.
See example below:
cpp_code = """
enum class Options { A, B };
// A constructor with named params will be created for FooClass,
// since options.class_create_default_named_ctor__regex was filled
class FooClass {
public:
Options options = Options::A;
int a = 1;
};
// A constructor with named params will be created for FooStruct
struct FooStruct { int X = 0, Y = 1; };
// FooStruct has a default constructor, so no constructor with named params will be generated
struct FooStruct2 {
FooStruct2();
int X = 0, Y = 1;
};
"""
options = litgen.LitgenOptions()
# options.struct_create_default_named_ctor__regex = r".*"
options.class_create_default_named_ctor__regex = r".*"
litgen_demo.demo(options, cpp_code)Expose protected member functions¶
Relevant portion of the pybind11 manual and of the nanobind manual
Exposing protected member functions requires the creation of a “Publicist” helper class. litgen enables to automate this:
cpp_code = """
class A {
protected:
int foo() const { return 42; }
};
"""
options = litgen.LitgenOptions()
options.class_expose_protected_methods__regex = "^A$"
litgen_demo.demo(options, cpp_code, show_pydef=True)Overriding virtual methods in Python¶
Relevant portion of the pybind11 manual and of the nanobind manual
In order to override virtual methods in Python, you need to create of a trampoline class, which can be a bit cumbersome.
litgen can automate this: look at the pybind11 binding code, and at the glue code below.
cpp_code = """
class Animal {
public:
virtual ~Animal() { }
virtual std::string go(int n_times) = 0;
};
"""
options = litgen.LitgenOptions()
options.class_override_virtual_methods_in_python__regex = "^Animal$"
litgen_demo.demo(options, cpp_code, show_pydef=True)Note: in the case of nanobind, the glue code will differ a bit. It is shown below:
options.bind_library = litgen.BindLibraryType.nanobind
generated_code = litgen.generate_code(options, cpp_code)
litgen_demo.show_cpp_code(generated_code.glue_code, "Glue code with nanobind")Combining virtual functions and inheritance¶
Relevant portion of the pybind11 manual and of the nanobind manual
cpp_code = """
class Animal {
public:
virtual std::string go(int n_times) = 0;
virtual std::string name() { return "unknown"; }
};
class Dog : public Animal {
public:
std::string go(int n_times) override {
std::string result;
for (int i=0; i<n_times; ++i)
result += bark() + " ";
return result;
}
virtual std::string bark() { return "woof!"; }
};
"""
options = litgen.LitgenOptions()
options.class_override_virtual_methods_in_python__regex = "^Animal$|^Dog$"
litgen_demo.demo(options, cpp_code, show_pydef=True)Note: in the case of nanobind, the glue code will differ a bit. It is shown below:
options.bind_library = litgen.BindLibraryType.nanobind
generated_code = litgen.generate_code(options, cpp_code)
litgen_demo.show_cpp_code(generated_code.glue_code, "Glue code with nanobind")Operator overloading¶
litgen is able to automatically transform C++ numerical operators into their corresponding dunder function in Python.
Overloading addition, substraction, etc.¶
See example below:
cpp_code = """
struct IntWrapper
{
int value;
IntWrapper(int v) : value(v) {}
// arithmetic operators
IntWrapper operator+(IntWrapper b) { return IntWrapper{ value + b.value}; }
IntWrapper operator-(IntWrapper b) { return IntWrapper{ value - b.value }; }
// Unary minus operator
IntWrapper operator-() { return IntWrapper{ -value }; }
// Comparison operator
bool operator<(IntWrapper b) { return value < b.value; }
// Two overload of the += operator
IntWrapper operator+=(IntWrapper b) { value += b.value; return *this; }
IntWrapper operator+=(int b) { value += b; return *this; }
// Two overload of the call operator, with different results
int operator()(IntWrapper b) { return value * b.value + 2; }
int operator()(int b) { return value * b + 3; }
};
"""
options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code, height=60)Overloading comparisons with the spaceship operator¶
cpp_code = """
struct Point
{
int x;
int y;
auto operator<=>(const Point&) const = default;
};
"""
options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code)Dynamic attributes¶
Relevant portion of the pybind11 manual and of the nanobind manual
The python class Pet below will be able to pick up new attributes dynamically:
cpp_code = """
struct Pet {
Pet(const std::string &name) : name(name) { }
std::string name;
};
"""
options = litgen.LitgenOptions()
options.class_dynamic_attributes__regex = "^Pet$"
litgen_demo.demo(options, cpp_code, show_pydef=True)Copy and Deep copy¶
Relevant portion of the pybind11 manual. The principle is the same for nanobind.
See below for instructions on how to add support for copy and deepcopy.
cpp_code = """
struct Foo { std::vector<int> values; };
struct Foo2 {
Foo foo1 = Foo();
Foo foo2 = Foo();
};
"""
options = litgen.LitgenOptions()
options.class_copy__regex = "^Foo$|^Foo2$"
options.class_deep_copy__regex = "^Foo2$"
options.class_copy_add_info_in_stub = True
litgen_demo.demo(options, cpp_code)Iterable classes¶
It is possible to make custom container classes iterable in python. See example below:
cpp_code = """
class MyContainer
{
public:
float operator[](int idx);
// We need to have defined begin(), end(), and size() to make the class iterable
iterator begin(); // This function is excluded from the bindings (see options.fn_exclude_by_name__regex)
iterator end(); // This function is excluded from the bindings (see options.fn_exclude_by_name__regex)
size_t size(); // This function is also published as __len__
private:
std::vector<float> values;
};
"""
options = litgen.LitgenOptions()
options.class_iterables_infos.add_iterable_class("^MyContainer$", "float")
options.fn_exclude_by_name__regex = "^begin$|^end$"
litgen_demo.demo(options, cpp_code, show_pydef=True)Numeric C array members¶
If a struct/class stores a numeric C array member, it will be exposed as a modifiable numpy array.
cpp_code = """
struct Foo { int values[4]; };
"""
options = litgen.LitgenOptions()
# options.member_numeric_c_array_replace__regex = r".*" # this is the default
litgen_demo.demo(options, cpp_code, show_pydef=True)Inner class or enum¶
litgen handles inner classes or enum and correctly nest them in their host class:
cpp_code = """
struct Foo
{
enum class Choice { A, B };
int HandleChoice(Choice value = Choice::A) { return 0; }
};
"""
options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code)