Template classes and functions#
litgen provides advanced support for template classes and functions. Refer to the examples below.
Template Functions#
Relevant portion of the pybind11 manual and of the nanobind manual
litgen can instantiate template functions for a customizable range of types.
Export template functions with an @overload decorator#
Consider the example below. If we try to generate code from it, litgen will complain that this template function is unhandled:
cpp_code = """
template<typename T> T MaxValue(const std::vector<T>& values);
"""
import litgen
from litgen.demo import litgen_demo
options = litgen.LitgenOptions()
generated_code = litgen.generate_code(options, cpp_code)
Warning: (LitgenTemplateFunctionIgnore) Ignoring template function MaxValue. You might need to set LitgenOptions.fn_template_options
While parsing a "function_decl", corresponding to this C++ code:
Position:2:5
template<typename T> T MaxValue(const std::vector<T>& values);
^
Warning: (LitgenTemplateFunctionIgnore) Ignoring template function MaxValue. You might need to set LitgenOptions.fn_template_options
While parsing a "function_decl", corresponding to this C++ code:
Position:2:5
template<typename T> T MaxValue(const std::vector<T>& values);
^
If we add some information about how we want to specialize the function, then litgen will correctly output the bindings, and it will add an @overload
decorator to the python functions.
options.fn_template_options.add_specialization("^MaxValue$", ["int", "float"], add_suffix_to_function_name=False)
litgen_demo.demo(options, cpp_code)
template<typename T> T MaxValue(const std::vector<T>& values);
# ------------------------------------------------------------------------
# <template specializations for function MaxValue>
@overload
def max_value(values: List[int]) -> int:
pass
@overload
def max_value(values: List[float]) -> float:
pass
# </template specializations for function MaxValue>
# ------------------------------------------------------------------------
m.def("max_value",
py::overload_cast<const std::vector<int> &>(MaxValue<int>), py::arg("values"));
m.def("max_value",
py::overload_cast<const std::vector<float> &>(MaxValue<float>), py::arg("values"));
m.def("max_value",
nb::overload_cast<const std::vector<int> &>(MaxValue<int>), nb::arg("values"));
m.def("max_value",
nb::overload_cast<const std::vector<float> &>(MaxValue<float>), nb::arg("values"));
Export template functions with a suffix#
Instead of using @overload
, we can give different names to the python functions:
cpp_code = """
template<typename T> voi LogValue(const std::string& label, const T& value);
"""
options = litgen.LitgenOptions()
options.fn_template_options.add_specialization("^LogValue$", ["int", "float"], add_suffix_to_function_name=True)
litgen_demo.demo(options, cpp_code)
template<typename T> voi LogValue(const std::string& label, const T& value);
# ------------------------------------------------------------------------
# <template specializations for function LogValue>
def log_value_int(label: str, value: int) -> voi:
pass
def log_value_float(label: str, value: float) -> voi:
pass
# </template specializations for function LogValue>
# ------------------------------------------------------------------------
m.def("log_value_int",
LogValue<int>, py::arg("label"), py::arg("value"));
m.def("log_value_float",
LogValue<float>, py::arg("label"), py::arg("value"));
m.def("log_value_int",
LogValue<int>, nb::arg("label"), nb::arg("value"));
m.def("log_value_float",
LogValue<float>, nb::arg("label"), nb::arg("value"));
Template classes#
Introduction#
Relevant portion of the pybind11 manual and of the nanobind manual
litgen handles template classes instantiation in a sophisticated way.
In the example below, we set the following options:
type replacements
We set an option for type name replacements, so that ImGuiConfig
will be exposed as Config
in python:
options.type_replacements.add_last_replacement(r"ImGui([A-Z][a-zA-Z0-9]*)", r"\1")
class specialization
We tell litgen to instantiate
ImVector
forImGuiConfig
,float *
, andint32_t
.We also tell it to emit a synonym (
ImVector_Int32 = ImVector_int
) in the python stub.
options.class_template_options.add_specialization(
"ImVector", # which class do we want to specialize
["ImGuiConfig", "float *", "int32_t"], # for which types
["Int32=uint32_t"] # With which synonyms
)
Notes
The member
Foo::Configs
will be exposed with the correct python type (ImVector_Config
)The member
Foo::IntValues
will not be published, sinceImVector<int>
is not publishedlitgen will emit a warning about the missing specialization for
int
Example of template class instantiation#
cpp_code = """
struct ImGuiConfig { /* implementation not shown here */ };
template<typename T>
struct ImVector
{
// Implementation not shown here
private:
T* data;
};
struct Foo
{
ImVector<ImGuiConfig> Configs; // This member will be added to the bindings
ImVector<int> IntValues; // This member will be excluded from the bindings, since ImVector<int> is not published!
};
"""
options = litgen.LitgenOptions()
options.type_replacements.add_last_replacement(r"ImGui([A-Z][a-zA-Z0-9]*)", r"\1") # Remove prefix ImGui from exposed type
options.class_template_options.add_specialization(
"ImVector", # which class do we want to specialize
["ImGuiConfig", "float *", "int32_t"], # for which types
["Int32=uint32_t"], # With which synonyms
)
litgen_demo.demo(options, cpp_code)
# Note: the warnings below are normal, since we did not specialize ImVector<int> (they can be filtered out, see below)
Warning: (Undefined) Excluding template type ImVector<int> because its specialization for `int` is not handled
While parsing a "type", corresponding to this C++ code:
Position:16:9
Warning: (Undefined) Excluding template type ImVector<int> because its specialization for `int` is not handled
While parsing a "type", corresponding to this C++ code:
Position:16:9
Warning: (Undefined) Excluding template type ImVector<int> because its specialization for `int` is not handled
While parsing a "type", corresponding to this C++ code:
Position:16:9
Warning: (Undefined) Excluding template type ImVector<int> because its specialization for `int` is not handled
While parsing a "type", corresponding to this C++ code:
Position:16:9
Warning: (Undefined) Excluding template type ImVector<int> because its specialization for `int` is not handled
While parsing a "type", corresponding to this C++ code:
Position:16:9
Warning: (Undefined) Excluding template type ImVector<int> because its specialization for `int` is not handled
While parsing a "type", corresponding to this C++ code:
Position:16:9
struct ImGuiConfig { /* implementation not shown here */ };
template<typename T>
struct ImVector
{
// Implementation not shown here
private:
T* data;
};
struct Foo
{
ImVector<ImGuiConfig> Configs; // This member will be added to the bindings
ImVector<int> IntValues; // This member will be excluded from the bindings, since ImVector<int> is not published!
};
class Config:
# implementation not shown here
def __init__(self) -> None:
"""Auto-generated default constructor"""
pass
# ------------------------------------------------------------------------
# <template specializations for class ImVector>
class ImVector_Config: # Python specialization for ImVector<ImGuiConfig>
# Implementation not shown here
def __init__(self) -> None:
"""Auto-generated default constructor"""
pass
class ImVector_float_ptr: # Python specialization for ImVector<float *>
# Implementation not shown here
def __init__(self) -> None:
"""Auto-generated default constructor"""
pass
class ImVector_int32_t: # Python specialization for ImVector<int32_t>
# Implementation not shown here
def __init__(self) -> None:
"""Auto-generated default constructor"""
pass
ImVector_Int32 = ImVector_int
# </template specializations for class ImVector>
# ------------------------------------------------------------------------
class Foo:
configs: ImVector_Config # This member will be added to the bindings
def __init__(self, configs: ImVector_Config = ImVector_Config()) -> None:
"""Auto-generated default constructor with named params"""
pass
auto pyClassImGuiConfig =
py::class_<ImGuiConfig>
(m, "Config", "")
.def(py::init<>()) // implicit default constructor
;
auto pyClassImVector_ImGuiConfig =
py::class_<ImVector<ImGuiConfig>>
(m, "ImVector_Config", "")
.def(py::init<>()) // implicit default constructor
;
auto pyClassImVector_float_ptr =
py::class_<ImVector<float *>>
(m, "ImVector_float_ptr", "")
.def(py::init<>()) // implicit default constructor
;
auto pyClassImVector_int32_t =
py::class_<ImVector<int32_t>>
(m, "ImVector_int32_t", "")
.def(py::init<>()) // implicit default constructor
;
auto pyClassFoo =
py::class_<Foo>
(m, "Foo", "")
.def(py::init<>([](
ImVector<ImGuiConfig> Configs = ImVector<ImGuiConfig>())
{
auto r = std::make_unique<Foo>();
r->Configs = Configs;
return r;
})
, py::arg("configs") = ImVector<ImGuiConfig>()
)
.def_readwrite("configs", &Foo::Configs, "This member will be added to the bindings")
;
auto pyClassImGuiConfig =
nb::class_<ImGuiConfig>
(m, "Config", "")
.def(nb::init<>()) // implicit default constructor
;
auto pyClassImVector_ImGuiConfig =
nb::class_<ImVector<ImGuiConfig>>
(m, "ImVector_Config", "")
.def(nb::init<>()) // implicit default constructor
;
auto pyClassImVector_float_ptr =
nb::class_<ImVector<float *>>
(m, "ImVector_float_ptr", "")
.def(nb::init<>()) // implicit default constructor
;
auto pyClassImVector_int32_t =
nb::class_<ImVector<int32_t>>
(m, "ImVector_int32_t", "")
.def(nb::init<>()) // implicit default constructor
;
auto pyClassFoo =
nb::class_<Foo>
(m, "Foo", "")
.def("__init__", [](Foo * self, ImVector<ImGuiConfig> Configs = ImVector<ImGuiConfig>())
{
new (self) Foo(); // placement new
auto r = self;
r->Configs = Configs;
},
nb::arg("configs") = ImVector<ImGuiConfig>()
)
.def_rw("configs", &Foo::Configs, "This member will be added to the bindings")
;
Suppress template class warnings#
You can ask litgen to ignore the warnings concerning the missing specialization:
# tell litgen to ignore warnings that contain "Excluding template type ImVector<int>"
options.srcmlcpp_options.ignored_warning_parts.append("Excluding template type ImVector<int>")
# the following line emits a warning that is ignored
generated_code = litgen.generate_code(options, cpp_code)