Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,12 @@ The python wrapper supports keyword arguments for functions/methods. Hence, the

- Global variables
- Similar to global functions, the wrapper supports global variables as well.
- Currently we only support primitive types, such as `double`, `int`, `string`, etc.
- Primitive types such as `double`, `int`, and `string` become Python module attributes.
- Variables whose C++ type is `std::function`, including type aliases, become
named Python functions with signatures that `pybind11-stubgen` can discover.
Declare the alias in an included C++ header and use it as the variable type
in the interface file. Empty `std::function` values become `None`.
MATLAB continues to ignore global variables.
- E.g.
```cpp
const double kGravity = -9.81;
Expand Down
35 changes: 34 additions & 1 deletion gtwrap/pybind_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,35 @@ class PybindWrapper:
return PyArgPolicy<typename std::decay<T>::type>::make(name);
}

} // namespace internal
} // namespace gtwrap
"""

VARIABLE_BINDING_SUPPORT = """
#include <pybind11/functional.h>

namespace gtwrap {
namespace internal {

// Let C++ resolve aliases rather than guessing callable types in the parser.
template <typename T>
void bind_variable(pybind11::module_& module, const char* name, const T& value) {
module.attr(name) = value;
}

template <typename Return, typename... Args>
void bind_variable(pybind11::module_& module, const char* name,
const std::function<Return(Args...)>& value) {
if (!value) {
module.attr(name) = pybind11::none();
} else if (auto target = value.template target<Return (*)(Args...)>()) {
// Preserve pybind11's direct C++ callback path for function pointers.
module.def(name, *target);
} else {
module.def(name, value);
}
}

} // namespace internal
} // namespace gtwrap
"""
Expand Down Expand Up @@ -443,7 +472,9 @@ def wrap_variable(self,
else:
variable_value = variable.default

return '{prefix}{module_var}.attr("{variable_name}") = {namespace}{variable_value};'.format(
return ('{prefix}gtwrap::internal::bind_variable('
'{module_var}, "{variable_name}", '
'{namespace}{variable_value});').format(
prefix=prefix,
module_var=module_var,
variable_name=variable.name,
Expand Down Expand Up @@ -936,6 +967,8 @@ def wrap_file(self,
])

includes += self.ARG_POLICY_SUPPORT
if 'gtwrap::internal::bind_variable(' in wrapped_bindings:
includes += self.VARIABLE_BINDING_SUPPORT

return self.module_template.format(
module_def=module_def,
Expand Down
31 changes: 29 additions & 2 deletions tests/expected/python/namespaces_pybind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,33 @@ pybind11::arg py_arg(const char* name) {
} // namespace internal
} // namespace gtwrap

#include <pybind11/functional.h>

namespace gtwrap {
namespace internal {

// Let C++ resolve aliases rather than guessing callable types in the parser.
template <typename T>
void bind_variable(pybind11::module_& module, const char* name, const T& value) {
module.attr(name) = value;
}

template <typename Return, typename... Args>
void bind_variable(pybind11::module_& module, const char* name,
const std::function<Return(Args...)>& value) {
if (!value) {
module.attr(name) = pybind11::none();
} else if (auto target = value.template target<Return (*)(Args...)>()) {
// Preserve pybind11's direct C++ callback path for function pointers.
module.def(name, *target);
} else {
module.def(name, value);
}
}

} // namespace internal
} // namespace gtwrap




Expand Down Expand Up @@ -98,15 +125,15 @@ void gtwrap_bind_namespaces_py(py::module_ &m_) {
gtwrap_class_m_ns2_ClassC
.def(py::init<>());

m_ns2.attr("aNs2Var") = ns2::aNs2Var;
gtwrap::internal::bind_variable(m_ns2, "aNs2Var", ns2::aNs2Var);
m_ns2.def("aGlobalFunction",static_cast<gtsam::Vector (*)()>(&ns2::aGlobalFunction));
m_ns2.def("overloadedGlobalFunction",static_cast<ns1::ClassA (*)(const ns1::ClassA&)>(&ns2::overloadedGlobalFunction), gtwrap::internal::py_arg<const ns1::ClassA&>("a"));
m_ns2.def("overloadedGlobalFunction",static_cast<ns1::ClassA (*)(const ns1::ClassA&, double)>(&ns2::overloadedGlobalFunction), gtwrap::internal::py_arg<const ns1::ClassA&>("a"), gtwrap::internal::py_arg<double>("b"));
auto gtwrap_class_m__ClassD = py::reinterpret_borrow<py::class_<ClassD, std::shared_ptr<ClassD>>>(m_.attr("ClassD"));
gtwrap_class_m__ClassD
.def(py::init<>());

m_.attr("aGlobalVar") = aGlobalVar;
gtwrap::internal::bind_variable(m_, "aGlobalVar", aGlobalVar);
pybind11::module m_gtsam = py::reinterpret_borrow<pybind11::module>(m_.attr("gtsam"));

auto gtwrap_class_m_gtsam_Values = py::reinterpret_borrow<py::class_<gtsam::Values, std::shared_ptr<gtsam::Values>>>(m_gtsam.attr("Values"));
Expand Down
Loading