Names and types translation

Names and types translation#

There are numerous names and types translation options in the options file.

Below is a relevant extract from the options:

    ################################################################################
    #    <names translation from C++ to python>
    ################################################################################
    # Convert variables, functions and namespaces names to snake_case (class, structs, and enums names are always preserved)
    python_convert_to_snake_case: bool = True
    # List of code replacements when going from C++ to Python
    # Notes:
    # - by default, type_replacements is prefilled with standard_type_replacements()
    #   type_replacements will be applied to all types (including class and enum names)
    # - by default, value_replacements is prefilled with standard_value_replacements()
    # - by default, comments_replacements is prefilled with standard_comments_replacements()
    # - by default, the others are empty
    # - type_replacements, var_names_replacements and function_names_replacements enable you
    #   to modify the outputted python code
    type_replacements: RegexReplacementList  # = cpp_to_python.standard_type_replacements() by default
    var_names_replacements: RegexReplacementList  # = RegexReplacementList() by default (i.e. empty)
    namespace_names_replacements: RegexReplacementList  # = RegexReplacementList() by default (i.e. empty)
    function_names_replacements: RegexReplacementList  # = RegexReplacementList() by default (i.e. empty)
    value_replacements: RegexReplacementList  # = cpp_to_python.standard_value_replacements() by default
    comments_replacements: RegexReplacementList  # = cpp_to_python.standard_comment_replacements() by default
    macro_name_replacements: RegexReplacementList  # = RegexReplacementList() by default (i.e. empty)

Types replacements#

options.type_replacements enables to change the way some types are exported.

Let’s take an example with some C++ code.

In the example below, MyPair is a template class that should behave like a std::pair, and should be presented as a python Tuple[int, int].

cpp_code = """
    MyPair<int, int> GetMinMax(std::vector<int>& values);
"""

If we convert it, we see that std::vector<int> is correctly interpreted as a List[int], however MyPair<int, int> is not.

import litgen
from litgen.demo import litgen_demo

options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code)
    MyPair<int, int> GetMinMax(std::vector<int>& values);
def get_min_max(values: List[int]) -> MyPair[int, int]:
    pass


m.def("get_min_max",
    GetMinMax, py::arg("values"));
m.def("get_min_max",
    GetMinMax, nb::arg("values"));

In order to account for MyPair<int, int>, we need to add replacements to options.type_replacements:

options = litgen.LitgenOptions()
options.type_replacements.add_last_replacement(
    r"MyPair<(.*),\s*(.*)>", # this is a regex, with 2 captures
    r"Tuple[\1, \2]"         # and this is the replacement
)
litgen_demo.demo(options, cpp_code)
    MyPair<int, int> GetMinMax(std::vector<int>& values);
def get_min_max(values: List[int]) -> Tuple[int, int]:
    pass


m.def("get_min_max",
    GetMinMax, py::arg("values"));
m.def("get_min_max",
    GetMinMax, nb::arg("values"));

Note: by default options.type_replacements already contains standard replacements regexes. See standard_type_replacements() inside packages/litgen/internal/cpp_to_python.py.

Convert to snake case#

The code below:

cpp_code = """
namespace MyNamespace
{
    struct MyClass
    {
        int AddNumber(int a, int b);

        int MultiplierRatio = 4;
    };
}
"""

By default, the bindings will convert CamelCase to snake_case for functions, variables and namespaces:

import litgen

options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code)
namespace MyNamespace
{
    struct MyClass
    {
        int AddNumber(int a, int b);

        int MultiplierRatio = 4;
    };
}
# <submodule my_namespace>
class my_namespace:  # Proxy class that introduces typings for the *submodule* my_namespace
    pass  # (This corresponds to a C++ namespace. All method are static!)
    class MyClass:
        def add_number(self, a: int, b: int) -> int:
            pass

        multiplier_ratio: int = 4
        def __init__(self, multiplier_ratio: int = 4) -> None:
            """Auto-generated default constructor with named params"""
            pass

# </submodule my_namespace>


{ // <namespace MyNamespace>
    py::module_ pyNsMyNamespace = m.def_submodule("my_namespace", "");
    auto pyNsMyNamespace_ClassMyClass =
        py::class_<MyNamespace::MyClass>
            (pyNsMyNamespace, "MyClass", "")
        .def(py::init<>([](
        int MultiplierRatio = 4)
        {
            auto r = std::make_unique<MyNamespace::MyClass>();
            r->MultiplierRatio = MultiplierRatio;
            return r;
        })
        , py::arg("multiplier_ratio") = 4
        )
        .def("add_number",
            &MyNamespace::MyClass::AddNumber, py::arg("a"), py::arg("b"))
        .def_readwrite("multiplier_ratio", &MyNamespace::MyClass::MultiplierRatio, "")
        ;
} // </namespace MyNamespace>
{ // <namespace MyNamespace>
    nb::module_ pyNsMyNamespace = m.def_submodule("my_namespace", "");
    auto pyNsMyNamespace_ClassMyClass =
        nb::class_<MyNamespace::MyClass>
            (pyNsMyNamespace, "MyClass", "")
        .def("__init__", [](MyNamespace::MyClass * self, int MultiplierRatio = 4)
        {
            new (self) MyNamespace::MyClass();  // placement new
            auto r = self;
            r->MultiplierRatio = MultiplierRatio;
        },
        nb::arg("multiplier_ratio") = 4
        )
        .def("add_number",
            &MyNamespace::MyClass::AddNumber, nb::arg("a"), nb::arg("b"))
        .def_rw("multiplier_ratio", &MyNamespace::MyClass::MultiplierRatio, "")
        ;
} // </namespace MyNamespace>

However, you can set options.python_convert_to_snake_case = False to disable this.

options.python_convert_to_snake_case = False
litgen_demo.demo(options, cpp_code)
namespace MyNamespace
{
    struct MyClass
    {
        int AddNumber(int a, int b);

        int MultiplierRatio = 4;
    };
}
# <submodule MyNamespace>
class MyNamespace:  # Proxy class that introduces typings for the *submodule* MyNamespace
    pass  # (This corresponds to a C++ namespace. All method are static!)
    class MyClass:
        def AddNumber(self, a: int, b: int) -> int:
            pass

        MultiplierRatio: int = 4
        def __init__(self, MultiplierRatio: int = 4) -> None:
            """Auto-generated default constructor with named params"""
            pass

# </submodule MyNamespace>


{ // <namespace MyNamespace>
    py::module_ pyNsMyNamespace = m.def_submodule("MyNamespace", "");
    auto pyNsMyNamespace_ClassMyClass =
        py::class_<MyNamespace::MyClass>
            (pyNsMyNamespace, "MyClass", "")
        .def(py::init<>([](
        int MultiplierRatio = 4)
        {
            auto r = std::make_unique<MyNamespace::MyClass>();
            r->MultiplierRatio = MultiplierRatio;
            return r;
        })
        , py::arg("MultiplierRatio") = 4
        )
        .def("AddNumber",
            &MyNamespace::MyClass::AddNumber, py::arg("a"), py::arg("b"))
        .def_readwrite("MultiplierRatio", &MyNamespace::MyClass::MultiplierRatio, "")
        ;
} // </namespace MyNamespace>
{ // <namespace MyNamespace>
    nb::module_ pyNsMyNamespace = m.def_submodule("MyNamespace", "");
    auto pyNsMyNamespace_ClassMyClass =
        nb::class_<MyNamespace::MyClass>
            (pyNsMyNamespace, "MyClass", "")
        .def("__init__", [](MyNamespace::MyClass * self, int MultiplierRatio = 4)
        {
            new (self) MyNamespace::MyClass();  // placement new
            auto r = self;
            r->MultiplierRatio = MultiplierRatio;
        },
        nb::arg("MultiplierRatio") = 4
        )
        .def("AddNumber",
            &MyNamespace::MyClass::AddNumber, nb::arg("a"), nb::arg("b"))
        .def_rw("MultiplierRatio", &MyNamespace::MyClass::MultiplierRatio, "")
        ;
} // </namespace MyNamespace>