diff --git a/gtwrap/matlab_wrapper/mixins.py b/gtwrap/matlab_wrapper/mixins.py index b0943ce..c59ab31 100644 --- a/gtwrap/matlab_wrapper/mixins.py +++ b/gtwrap/matlab_wrapper/mixins.py @@ -20,6 +20,8 @@ class CheckMixin: ) # Ignore the namespace for these datatypes ignore_namespace: Tuple = ('Matrix', 'Vector', 'Point2', 'Point3') + # Matrix-like view types that can alias MATLAB double matrix storage. + matrix_view_types: Tuple = ('ConstMatrixView', ) # Methods that should be ignored ignore_methods: Tuple = ('pickle', ) # Methods that should not be wrapped directly @@ -42,6 +44,7 @@ def can_be_pointer(self, arg_type: parser.Type): """ return (arg_type.typename.name not in self.not_ptr_type and arg_type.typename.name not in self.ignore_namespace + and not self.is_matrix_view(arg_type) and arg_type.typename.name != 'string') def is_shared_ptr(self, arg_type: parser.Type): @@ -67,6 +70,10 @@ def is_ref(self, arg_type: parser.Type): arg_type.typename.name not in self.not_ptr_type and \ arg_type.is_ref + def is_matrix_view(self, arg_type: parser.Type): + """Check if `arg_type` should be unwrapped as a matrix view.""" + return arg_type.typename.name in self.matrix_view_types + def is_class_enum(self, arg_type: parser.Type, class_: parser.Class): """Check if arg_type is an enum in the class `class_`.""" if class_: diff --git a/gtwrap/matlab_wrapper/wrapper.py b/gtwrap/matlab_wrapper/wrapper.py index 6bc354c..6776320 100755 --- a/gtwrap/matlab_wrapper/wrapper.py +++ b/gtwrap/matlab_wrapper/wrapper.py @@ -51,6 +51,7 @@ def __init__(self, 'unsigned char': 'unsigned char', 'Vector': 'double', 'Matrix': 'double', + 'ConstMatrixView': 'double', 'int': 'numeric', 'size_t': 'numeric', 'Key': 'numeric', @@ -69,6 +70,7 @@ def __init__(self, 'Point3': 'double', 'Vector': 'double', 'Matrix': 'double', + 'ConstMatrixView': 'double', 'Key': 'numeric', 'bool': 'bool' } @@ -354,6 +356,11 @@ def _unwrap_argument(self, arg, arg_id=0, instantiated_class=None): arg_type = f"{enum_type}" unwrap = f'unwrap_enum<{enum_type}>(in[{arg_id}]);' + elif self.is_matrix_view(arg.ctype): + arg_type = self._format_type_name(arg.ctype.typename) + unwrap = 'unwrapMatrixView< {ctype} >(in[{id}]);'.format( + ctype=arg_type, id=arg_id) + elif self.is_ref(arg.ctype): # and not constructor: arg_type = "{ctype}&".format(ctype=ctype_sep) unwrap = '*unwrap_shared_ptr< {ctype} >(in[{id}], "ptr_{ctype_camel}");'.format( diff --git a/gtwrap/pybind_wrapper.py b/gtwrap/pybind_wrapper.py index 4fcbfbb..d58ba68 100755 --- a/gtwrap/pybind_wrapper.py +++ b/gtwrap/pybind_wrapper.py @@ -27,6 +27,26 @@ class PybindWrapper: Class to generate binding code for Pybind11 specifically. """ + ARG_POLICY_SUPPORT = """ +#include + +namespace gtwrap { +namespace internal { + +template +struct PyArgPolicy { + static pybind11::arg make(const char* name) { return pybind11::arg(name); } +}; + +template +pybind11::arg py_arg(const char* name) { + return PyArgPolicy::type>::make(name); +} + +} // namespace internal +} // namespace gtwrap +""" + def __init__(self, module_name, top_module_namespaces='', @@ -70,8 +90,11 @@ def _py_args_names(self, args): default = ' = {arg.default}'.format(arg=arg) else: default = '' - argument = 'py::arg("{name}"){default}'.format( - name=arg.name, default='{0}'.format(default)) + argument = ( + 'gtwrap::internal::py_arg<{ctype}>("{name}"){default}' + ).format(ctype=arg.ctype.to_cpp(), + name=arg.name, + default='{0}'.format(default)) py_args.append(argument) return ", " + ", ".join(py_args) else: @@ -251,12 +274,13 @@ def _wrap_method(self, method, (parser.StaticMethod, instantiator.InstantiatedStaticMethod)) return_void = method.return_type.is_void() - return_ref = getattr( - getattr(method.return_type, 'type1', None), 'is_ref', False) + return_type = getattr(method.return_type, 'type1', None) + return_ref = getattr(return_type, 'is_ref', False) + return_const = getattr(return_type, 'is_const', False) # For methods returning const T&, use reference_internal policy # to avoid unnecessary copies and keep the returned reference alive. - if return_ref and is_method: + if return_ref and return_const and is_method: lambda_ret = ' -> const auto&' ref_policy = ', py::return_value_policy::reference_internal' else: @@ -752,6 +776,8 @@ def wrap_file(self, content, module_name=None, submodules=None): module_def = "void {0}(py::module_ &m_)".format(module_name) submodules = [] + includes += self.ARG_POLICY_SUPPORT + return self.module_template.format( module_def=module_def, module_name=module_name, diff --git a/matlab.h b/matlab.h index 5c7cf7a..58b6dda 100644 --- a/matlab.h +++ b/matlab.h @@ -37,6 +37,7 @@ extern "C" { #include } +#include #include #include #include @@ -429,6 +430,29 @@ gtsam::Matrix unwrap< gtsam::Matrix >(const mxArray* array) { return A; } +// unwrap a MATLAB double matrix as a const Eigen matrix view without copying +template +MatrixView unwrapMatrixView(const mxArray* array) { + if (mxIsDouble(array)==false || mxIsComplex(array) || mxIsSparse(array)) + error("unwrapMatrixView: not a full real double matrix"); + const mwSize rows = mxGetM(array), cols = mxGetN(array); + if (rows > static_cast(std::numeric_limits::max()) || + cols > static_cast(std::numeric_limits::max())) { + error("unwrapMatrixView: matrix dimensions exceed Eigen::Index"); + } + const Eigen::Index m = static_cast(rows); + const Eigen::Index n = static_cast(cols); +#ifdef DEBUG_WRAP + mexPrintf("unwrapMatrixView called with %lldx%lld argument\n", + static_cast(m), static_cast(n)); +#endif + using Stride = Eigen::Stride; + using ConstMatrixMap = Eigen::Map; + const double* data = static_cast(mxGetData(array)); + ConstMatrixMap map(data, m, n, Stride(m, 1)); + return MatrixView(map); +} + /* [create_object] creates a MATLAB proxy class object with a mexhandle in the self property. Matlab does not allow the creation of matlab @@ -547,4 +571,3 @@ Class* unwrap_ptr(const mxArray* obj, const string& propertyName) { // static_assert(unwrap_shared_ptr_Matrix_attempted, "Matrix cannot be unwrapped as a shared pointer"); // return Matrix(); //} - diff --git a/tests/expected/python/class_pybind.cpp b/tests/expected/python/class_pybind.cpp index d5f6492..c1844f9 100644 --- a/tests/expected/python/class_pybind.cpp +++ b/tests/expected/python/class_pybind.cpp @@ -6,6 +6,24 @@ #include "folder/path/to/Test.h" +#include + +namespace gtwrap { +namespace internal { + +template +struct PyArgPolicy { + static pybind11::arg make(const char* name) { return pybind11::arg(name); } +}; + +template +pybind11::arg py_arg(const char* name) { + return PyArgPolicy::type>::make(name); +} + +} // namespace internal +} // namespace gtwrap + @@ -19,41 +37,41 @@ PYBIND11_MODULE(class_py, m_) { py::class_>(m_, "FunRange") .def(py::init<>()) - .def("range",[](FunRange* self, double d){return self->range(d);}, py::arg("d")) + .def("range",[](FunRange* self, double d){return self->range(d);}, gtwrap::internal::py_arg("d")) .def_static("create",[](){return FunRange::create();}); py::class_, std::shared_ptr>>(m_, "FunDouble") - .def("templatedMethodString",[](Fun* self, double d, string t){return self->templatedMethod(d, t);}, py::arg("d"), py::arg("t")) - .def("multiTemplatedMethodStringSize_t",[](Fun* self, double d, string t, size_t u){return self->multiTemplatedMethod(d, t, u);}, py::arg("d"), py::arg("t"), py::arg("u")) + .def("templatedMethodString",[](Fun* self, double d, string t){return self->templatedMethod(d, t);}, gtwrap::internal::py_arg("d"), gtwrap::internal::py_arg("t")) + .def("multiTemplatedMethodStringSize_t",[](Fun* self, double d, string t, size_t u){return self->multiTemplatedMethod(d, t, u);}, gtwrap::internal::py_arg("d"), gtwrap::internal::py_arg("t"), gtwrap::internal::py_arg("u")) .def("sets",[](Fun* self){return self->sets();}) .def_static("staticMethodWithThis",[](){return Fun::staticMethodWithThis();}) - .def_static("templatedStaticMethodInt",[](const int& m){return Fun::templatedStaticMethod(m);}, py::arg("m")); + .def_static("templatedStaticMethodInt",[](const int& m){return Fun::templatedStaticMethod(m);}, gtwrap::internal::py_arg("m")); py::class_>(m_, "Test") .def(py::init<>()) - .def(py::init(), py::arg("a"), py::arg("b")) - .def("return_pair",[](Test* self, const gtsam::Vector& v, const gtsam::Matrix& A){return self->return_pair(v, A);}, py::arg("v"), py::arg("A")) - .def("return_pair",[](Test* self, const gtsam::Vector& v){return self->return_pair(v);}, py::arg("v")) - .def("return_bool",[](Test* self, bool value){return self->return_bool(value);}, py::arg("value")) - .def("return_size_t",[](Test* self, size_t value){return self->return_size_t(value);}, py::arg("value")) - .def("return_int",[](Test* self, int value){return self->return_int(value);}, py::arg("value")) - .def("return_double",[](Test* self, double value){return self->return_double(value);}, py::arg("value")) - .def("return_string",[](Test* self, string value){return self->return_string(value);}, py::arg("value")) - .def("return_vector1",[](Test* self, const gtsam::Vector& value){return self->return_vector1(value);}, py::arg("value")) - .def("return_matrix1",[](Test* self, const gtsam::Matrix& value){return self->return_matrix1(value);}, py::arg("value")) - .def("return_vector2",[](Test* self, const gtsam::Vector& value){return self->return_vector2(value);}, py::arg("value")) - .def("return_matrix2",[](Test* self, const gtsam::Matrix& value){return self->return_matrix2(value);}, py::arg("value")) - .def("return_vector2",[](Test* self, const gtsam::Vector& value) -> const auto&{return self->return_vector2(value);}, py::return_value_policy::reference_internal, py::arg("value")) - .def("return_matrix2",[](Test* self, const gtsam::Matrix& value) -> const auto&{return self->return_matrix2(value);}, py::return_value_policy::reference_internal, py::arg("value")) - .def("arg_EigenConstRef",[](Test* self, const gtsam::Matrix& value){ self->arg_EigenConstRef(value);}, py::arg("value")) - .def("push_back",[](Test* self, gtsam::Key key){ self->push_back(key);}, py::arg("key")) - .def("return_field",[](Test* self, const Test& t){return self->return_field(t);}, py::arg("t")) - .def("return_TestPtr",[](Test* self, const std::shared_ptr value){return self->return_TestPtr(value);}, py::arg("value")) - .def("return_Test",[](Test* self, std::shared_ptr value){return self->return_Test(value);}, py::arg("value")) - .def("return_Point2Ptr",[](Test* self, bool value){return self->return_Point2Ptr(value);}, py::arg("value")) + .def(py::init(), gtwrap::internal::py_arg("a"), gtwrap::internal::py_arg("b")) + .def("return_pair",[](Test* self, const gtsam::Vector& v, const gtsam::Matrix& A){return self->return_pair(v, A);}, gtwrap::internal::py_arg("v"), gtwrap::internal::py_arg("A")) + .def("return_pair",[](Test* self, const gtsam::Vector& v){return self->return_pair(v);}, gtwrap::internal::py_arg("v")) + .def("return_bool",[](Test* self, bool value){return self->return_bool(value);}, gtwrap::internal::py_arg("value")) + .def("return_size_t",[](Test* self, size_t value){return self->return_size_t(value);}, gtwrap::internal::py_arg("value")) + .def("return_int",[](Test* self, int value){return self->return_int(value);}, gtwrap::internal::py_arg("value")) + .def("return_double",[](Test* self, double value){return self->return_double(value);}, gtwrap::internal::py_arg("value")) + .def("return_string",[](Test* self, string value){return self->return_string(value);}, gtwrap::internal::py_arg("value")) + .def("return_vector1",[](Test* self, const gtsam::Vector& value){return self->return_vector1(value);}, gtwrap::internal::py_arg("value")) + .def("return_matrix1",[](Test* self, const gtsam::Matrix& value){return self->return_matrix1(value);}, gtwrap::internal::py_arg("value")) + .def("return_vector2",[](Test* self, const gtsam::Vector& value){return self->return_vector2(value);}, gtwrap::internal::py_arg("value")) + .def("return_matrix2",[](Test* self, const gtsam::Matrix& value){return self->return_matrix2(value);}, gtwrap::internal::py_arg("value")) + .def("return_vector2",[](Test* self, const gtsam::Vector& value) -> const auto&{return self->return_vector2(value);}, py::return_value_policy::reference_internal, gtwrap::internal::py_arg("value")) + .def("return_matrix2",[](Test* self, const gtsam::Matrix& value) -> const auto&{return self->return_matrix2(value);}, py::return_value_policy::reference_internal, gtwrap::internal::py_arg("value")) + .def("arg_EigenConstRef",[](Test* self, const gtsam::Matrix& value){ self->arg_EigenConstRef(value);}, gtwrap::internal::py_arg("value")) + .def("push_back",[](Test* self, gtsam::Key key){ self->push_back(key);}, gtwrap::internal::py_arg("key")) + .def("return_field",[](Test* self, const Test& t){return self->return_field(t);}, gtwrap::internal::py_arg("t")) + .def("return_TestPtr",[](Test* self, const std::shared_ptr value){return self->return_TestPtr(value);}, gtwrap::internal::py_arg>("value")) + .def("return_Test",[](Test* self, std::shared_ptr value){return self->return_Test(value);}, gtwrap::internal::py_arg>("value")) + .def("return_Point2Ptr",[](Test* self, bool value){return self->return_Point2Ptr(value);}, gtwrap::internal::py_arg("value")) .def("create_ptrs",[](Test* self){return self->create_ptrs();}) .def("create_MixedPtrs",[](Test* self){return self->create_MixedPtrs();}) - .def("return_ptrs",[](Test* self, std::shared_ptr p1, std::shared_ptr p2){return self->return_ptrs(p1, p2);}, py::arg("p1"), py::arg("p2")) + .def("return_ptrs",[](Test* self, std::shared_ptr p1, std::shared_ptr p2){return self->return_ptrs(p1, p2);}, gtwrap::internal::py_arg>("p1"), gtwrap::internal::py_arg>("p2")) .def("print",[](Test* self){ py::scoped_ostream_redirect output; self->print();}) .def("__repr__", [](const Test& self){ @@ -62,18 +80,18 @@ PYBIND11_MODULE(class_py, m_) { return redirect.str(); }) .def("lambda_",[](Test* self){ self->lambda();}) - .def("set_container",[](Test* self, std::vector container){ self->set_container(container);}, py::arg("container")) - .def("set_container",[](Test* self, std::vector> container){ self->set_container(container);}, py::arg("container")) - .def("set_container",[](Test* self, std::vector container){ self->set_container(container);}, py::arg("container")) + .def("set_container",[](Test* self, std::vector container){ self->set_container(container);}, gtwrap::internal::py_arg>("container")) + .def("set_container",[](Test* self, std::vector> container){ self->set_container(container);}, gtwrap::internal::py_arg>>("container")) + .def("set_container",[](Test* self, std::vector container){ self->set_container(container);}, gtwrap::internal::py_arg>("container")) .def("get_container",[](Test* self){return self->get_container();}) - .def("_repr_markdown_",[](Test* self, const gtsam::KeyFormatter& keyFormatter){return self->markdown(keyFormatter);}, py::arg("keyFormatter") = gtsam::DefaultKeyFormatter) + .def("_repr_markdown_",[](Test* self, const gtsam::KeyFormatter& keyFormatter){return self->markdown(keyFormatter);}, gtwrap::internal::py_arg("keyFormatter") = gtsam::DefaultKeyFormatter) .def_readwrite("model_ptr", &Test::model_ptr) .def_readwrite("value", &Test::value) .def_readwrite("name", &Test::name); py::class_, std::shared_ptr>>(m_, "PrimitiveRefDouble") .def(py::init<>()) - .def_static("Brutal",[](const double& t){return PrimitiveRef::Brutal(t);}, py::arg("t")); + .def_static("Brutal",[](const double& t){return PrimitiveRef::Brutal(t);}, gtwrap::internal::py_arg("t")); py::class_, std::shared_ptr>>(m_, "MyVector3") .def(py::init<>()); @@ -86,35 +104,35 @@ PYBIND11_MODULE(class_py, m_) { py::class_, std::shared_ptr>>(m_, "MultipleTemplatesIntFloat"); py::class_>(m_, "ForwardKinematics") - .def(py::init(), py::arg("robot"), py::arg("start_link_name"), py::arg("end_link_name"), py::arg("joint_angles"), py::arg("l2Tp") = gtsam::Pose3()); + .def(py::init(), gtwrap::internal::py_arg("robot"), gtwrap::internal::py_arg("start_link_name"), gtwrap::internal::py_arg("end_link_name"), gtwrap::internal::py_arg("joint_angles"), gtwrap::internal::py_arg("l2Tp") = gtsam::Pose3()); py::class_>(m_, "TemplatedConstructor") .def(py::init<>()) - .def(py::init(), py::arg("arg")) - .def(py::init(), py::arg("arg")) - .def(py::init(), py::arg("arg")); + .def(py::init(), gtwrap::internal::py_arg("arg")) + .def(py::init(), gtwrap::internal::py_arg("arg")) + .def(py::init(), gtwrap::internal::py_arg("arg")); py::class_>(m_, "FastSet") .def(py::init<>()) .def("__len__",[](FastSet* self){return std::distance(self->begin(), self->end());}) - .def("__contains__",[](FastSet* self, size_t key){return std::find(self->begin(), self->end(), key) != self->end();}, py::arg("key")) + .def("__contains__",[](FastSet* self, size_t key){return std::find(self->begin(), self->end(), key) != self->end();}, gtwrap::internal::py_arg("key")) .def("__iter__",[](FastSet* self){return py::make_iterator(self->begin(), self->end());}); py::class_>(m_, "HessianFactor") - .def(py::init&, const std::vector&, double>(), py::arg("js"), py::arg("Gs"), py::arg("gs"), py::arg("f")); + .def(py::init&, const std::vector&, double>(), gtwrap::internal::py_arg("js"), gtwrap::internal::py_arg&>("Gs"), gtwrap::internal::py_arg&>("gs"), gtwrap::internal::py_arg("f")); py::class_>, gtsam::SmartProjectionFactor>, std::shared_ptr>>>(m_, "SmartProjectionRigFactorPinholeCameraCal3_S2") - .def("add",[](SmartProjectionRigFactor>* self, const gtsam::PinholeCamera::Measurement& measured, const gtsam::Key& poseKey, const size_t& cameraId){ self->add(measured, poseKey, cameraId);}, py::arg("measured"), py::arg("poseKey"), py::arg("cameraId") = 0); + .def("add",[](SmartProjectionRigFactor>* self, const gtsam::PinholeCamera::Measurement& measured, const gtsam::Key& poseKey, const size_t& cameraId){ self->add(measured, poseKey, cameraId);}, gtwrap::internal::py_arg::Measurement&>("measured"), gtwrap::internal::py_arg("poseKey"), gtwrap::internal::py_arg("cameraId") = 0); py::class_, std::shared_ptr>>(m_, "MyFactorPosePoint2") - .def(py::init>(), py::arg("key1"), py::arg("key2"), py::arg("measured"), py::arg("noiseModel")) - .def("print",[](MyFactor* self, const string& s, const gtsam::KeyFormatter& keyFormatter){ py::scoped_ostream_redirect output; self->print(s, keyFormatter);}, py::arg("s") = "factor: ", py::arg("keyFormatter") = gtsam::DefaultKeyFormatter) + .def(py::init>(), gtwrap::internal::py_arg("key1"), gtwrap::internal::py_arg("key2"), gtwrap::internal::py_arg("measured"), gtwrap::internal::py_arg>("noiseModel")) + .def("print",[](MyFactor* self, const string& s, const gtsam::KeyFormatter& keyFormatter){ py::scoped_ostream_redirect output; self->print(s, keyFormatter);}, gtwrap::internal::py_arg("s") = "factor: ", gtwrap::internal::py_arg("keyFormatter") = gtsam::DefaultKeyFormatter) .def("__repr__", [](const MyFactor& self, const string& s, const gtsam::KeyFormatter& keyFormatter){ gtsam::RedirectCout redirect; self.print(s, keyFormatter); return redirect.str(); - }, py::arg("s") = "factor: ", py::arg("keyFormatter") = gtsam::DefaultKeyFormatter); + }, gtwrap::internal::py_arg("s") = "factor: ", gtwrap::internal::py_arg("keyFormatter") = gtsam::DefaultKeyFormatter); py::class_, std::shared_ptr>>(m_, "SuperCoolFactorPose3"); diff --git a/tests/expected/python/enum_pybind.cpp b/tests/expected/python/enum_pybind.cpp index c67bf1d..398425d 100644 --- a/tests/expected/python/enum_pybind.cpp +++ b/tests/expected/python/enum_pybind.cpp @@ -5,6 +5,24 @@ #include "gtsam/nonlinear/utilities.h" // for RedirectCout. +#include + +namespace gtwrap { +namespace internal { + +template +struct PyArgPolicy { + static pybind11::arg make(const char* name) { return pybind11::arg(name); } +}; + +template +pybind11::arg py_arg(const char* name) { + return PyArgPolicy::type>::make(name); +} + +} // namespace internal +} // namespace gtwrap + @@ -23,8 +41,8 @@ PYBIND11_MODULE(enum_py, m_) { py::class_> pet(m_, "Pet"); pet - .def(py::init(), py::arg("name"), py::arg("type")) - .def("setColor",[](Pet* self, const Color& color){ self->setColor(color);}, py::arg("color")) + .def(py::init(), gtwrap::internal::py_arg("name"), gtwrap::internal::py_arg("type")) + .def("setColor",[](Pet* self, const Color& color){ self->setColor(color);}, gtwrap::internal::py_arg("color")) .def("getColor",[](Pet* self){return self->getColor();}) .def_readwrite("name", &Pet::name) .def_readwrite("type", &Pet::type); @@ -67,8 +85,8 @@ PYBIND11_MODULE(enum_py, m_) { py::class_, std::shared_ptr>> optimizergaussnewtonparams(m_gtsam, "OptimizerGaussNewtonParams"); optimizergaussnewtonparams - .def(py::init::Verbosity&>(), py::arg("verbosity")) - .def("setVerbosity",[](gtsam::Optimizer* self, const Optimizer::Verbosity value){ self->setVerbosity(value);}, py::arg("value")) + .def(py::init::Verbosity&>(), gtwrap::internal::py_arg::Verbosity&>("verbosity")) + .def("setVerbosity",[](gtsam::Optimizer* self, const Optimizer::Verbosity value){ self->setVerbosity(value);}, gtwrap::internal::py_arg::Verbosity>("value")) .def("getVerbosity",[](gtsam::Optimizer* self){return self->getVerbosity();}) .def("getVerbosity",[](gtsam::Optimizer* self){return self->getVerbosity();}); diff --git a/tests/expected/python/functions_pybind.cpp b/tests/expected/python/functions_pybind.cpp index 0b7772c..39ef57c 100644 --- a/tests/expected/python/functions_pybind.cpp +++ b/tests/expected/python/functions_pybind.cpp @@ -5,6 +5,24 @@ #include "gtsam/nonlinear/utilities.h" // for RedirectCout. +#include + +namespace gtwrap { +namespace internal { + +template +struct PyArgPolicy { + static pybind11::arg make(const char* name) { return pybind11::arg(name); } +}; + +template +pybind11::arg py_arg(const char* name) { + return PyArgPolicy::type>::make(name); +} + +} // namespace internal +} // namespace gtwrap + @@ -16,27 +34,27 @@ PYBIND11_MODULE(functions_py, m_) { m_.doc() = "pybind11 wrapper of functions_py"; - m_.def("load2D",[](string filename, std::shared_ptr model, int maxID, bool addNoise, bool smart){return ::load2D(filename, model, maxID, addNoise, smart);}, py::arg("filename"), py::arg("model"), py::arg("maxID"), py::arg("addNoise"), py::arg("smart")); - m_.def("load2D",[](string filename, const std::shared_ptr model, int maxID, bool addNoise, bool smart){return ::load2D(filename, model, maxID, addNoise, smart);}, py::arg("filename"), py::arg("model"), py::arg("maxID"), py::arg("addNoise"), py::arg("smart")); - m_.def("load2D",[](string filename, gtsam::noiseModel::Diagonal* model){return ::load2D(filename, model);}, py::arg("filename"), py::arg("model")); + m_.def("load2D",[](string filename, std::shared_ptr model, int maxID, bool addNoise, bool smart){return ::load2D(filename, model, maxID, addNoise, smart);}, gtwrap::internal::py_arg("filename"), gtwrap::internal::py_arg>("model"), gtwrap::internal::py_arg("maxID"), gtwrap::internal::py_arg("addNoise"), gtwrap::internal::py_arg("smart")); + m_.def("load2D",[](string filename, const std::shared_ptr model, int maxID, bool addNoise, bool smart){return ::load2D(filename, model, maxID, addNoise, smart);}, gtwrap::internal::py_arg("filename"), gtwrap::internal::py_arg>("model"), gtwrap::internal::py_arg("maxID"), gtwrap::internal::py_arg("addNoise"), gtwrap::internal::py_arg("smart")); + m_.def("load2D",[](string filename, gtsam::noiseModel::Diagonal* model){return ::load2D(filename, model);}, gtwrap::internal::py_arg("filename"), gtwrap::internal::py_arg("model")); m_.def("aGlobalFunction",[](){return ::aGlobalFunction();}); - m_.def("overloadedGlobalFunction",[](int a){return ::overloadedGlobalFunction(a);}, py::arg("a")); - m_.def("overloadedGlobalFunction",[](int a, double b){return ::overloadedGlobalFunction(a, b);}, py::arg("a"), py::arg("b")); - m_.def("MultiTemplatedFunctionStringSize_tDouble",[](const string& x, size_t y){return ::MultiTemplatedFunction(x, y);}, py::arg("x"), py::arg("y")); - m_.def("MultiTemplatedFunctionDoubleSize_tDouble",[](const double& x, size_t y){return ::MultiTemplatedFunction(x, y);}, py::arg("x"), py::arg("y")); - m_.def("DefaultFuncInt",[](int a, int b){ ::DefaultFuncInt(a, b);}, py::arg("a") = 123, py::arg("b") = 0); - m_.def("DefaultFuncString",[](const string& s, const string& name){ ::DefaultFuncString(s, name);}, py::arg("s") = "hello", py::arg("name") = ""); - m_.def("DefaultFuncObj",[](const gtsam::KeyFormatter& keyFormatter){ ::DefaultFuncObj(keyFormatter);}, py::arg("keyFormatter") = gtsam::DefaultKeyFormatter); - m_.def("DefaultFuncZero",[](int a, int b, double c, int d, bool e){ ::DefaultFuncZero(a, b, c, d, e);}, py::arg("a"), py::arg("b"), py::arg("c") = 0.0, py::arg("d") = 0, py::arg("e") = false); - m_.def("DefaultFuncVector",[](const std::vector& i, const std::vector& s){ ::DefaultFuncVector(i, s);}, py::arg("i") = {1, 2, 3}, py::arg("s") = {"borglab", "gtsam"}); - m_.def("setPose",[](const gtsam::Pose3& pose){ ::setPose(pose);}, py::arg("pose") = gtsam::Pose3()); - m_.def("EliminateDiscrete",[](const gtsam::DiscreteFactorGraph& factors, const gtsam::Ordering& frontalKeys){return ::EliminateDiscrete(factors, frontalKeys);}, py::arg("factors"), py::arg("frontalKeys")); - m_.def("triangulatePoint3Cal3_S2",[](const gtsam::Pose3Vector& poses, std::shared_ptr sharedCal, const gtsam::Point2Vector& measurements, double rank_tol, bool optimize, const gtsam::SharedNoiseModel& model){return ::triangulatePoint3(poses, sharedCal, measurements, rank_tol, optimize, model);}, py::arg("poses"), py::arg("sharedCal"), py::arg("measurements"), py::arg("rank_tol"), py::arg("optimize"), py::arg("model") = nullptr); - m_.def("FindKarcherMeanPoint3",[](const std::vector& elements){return ::FindKarcherMean(elements);}, py::arg("elements")); - m_.def("FindKarcherMeanSO3",[](const std::vector& elements){return ::FindKarcherMean(elements);}, py::arg("elements")); - m_.def("FindKarcherMeanSO4",[](const std::vector& elements){return ::FindKarcherMean(elements);}, py::arg("elements")); - m_.def("FindKarcherMeanPose3",[](const std::vector& elements){return ::FindKarcherMean(elements);}, py::arg("elements")); - m_.def("TemplatedFunctionRot3",[](const gtsam::Rot3& t){ ::TemplatedFunction(t);}, py::arg("t")); + m_.def("overloadedGlobalFunction",[](int a){return ::overloadedGlobalFunction(a);}, gtwrap::internal::py_arg("a")); + m_.def("overloadedGlobalFunction",[](int a, double b){return ::overloadedGlobalFunction(a, b);}, gtwrap::internal::py_arg("a"), gtwrap::internal::py_arg("b")); + m_.def("MultiTemplatedFunctionStringSize_tDouble",[](const string& x, size_t y){return ::MultiTemplatedFunction(x, y);}, gtwrap::internal::py_arg("x"), gtwrap::internal::py_arg("y")); + m_.def("MultiTemplatedFunctionDoubleSize_tDouble",[](const double& x, size_t y){return ::MultiTemplatedFunction(x, y);}, gtwrap::internal::py_arg("x"), gtwrap::internal::py_arg("y")); + m_.def("DefaultFuncInt",[](int a, int b){ ::DefaultFuncInt(a, b);}, gtwrap::internal::py_arg("a") = 123, gtwrap::internal::py_arg("b") = 0); + m_.def("DefaultFuncString",[](const string& s, const string& name){ ::DefaultFuncString(s, name);}, gtwrap::internal::py_arg("s") = "hello", gtwrap::internal::py_arg("name") = ""); + m_.def("DefaultFuncObj",[](const gtsam::KeyFormatter& keyFormatter){ ::DefaultFuncObj(keyFormatter);}, gtwrap::internal::py_arg("keyFormatter") = gtsam::DefaultKeyFormatter); + m_.def("DefaultFuncZero",[](int a, int b, double c, int d, bool e){ ::DefaultFuncZero(a, b, c, d, e);}, gtwrap::internal::py_arg("a"), gtwrap::internal::py_arg("b"), gtwrap::internal::py_arg("c") = 0.0, gtwrap::internal::py_arg("d") = 0, gtwrap::internal::py_arg("e") = false); + m_.def("DefaultFuncVector",[](const std::vector& i, const std::vector& s){ ::DefaultFuncVector(i, s);}, gtwrap::internal::py_arg&>("i") = {1, 2, 3}, gtwrap::internal::py_arg&>("s") = {"borglab", "gtsam"}); + m_.def("setPose",[](const gtsam::Pose3& pose){ ::setPose(pose);}, gtwrap::internal::py_arg("pose") = gtsam::Pose3()); + m_.def("EliminateDiscrete",[](const gtsam::DiscreteFactorGraph& factors, const gtsam::Ordering& frontalKeys){return ::EliminateDiscrete(factors, frontalKeys);}, gtwrap::internal::py_arg("factors"), gtwrap::internal::py_arg("frontalKeys")); + m_.def("triangulatePoint3Cal3_S2",[](const gtsam::Pose3Vector& poses, std::shared_ptr sharedCal, const gtsam::Point2Vector& measurements, double rank_tol, bool optimize, const gtsam::SharedNoiseModel& model){return ::triangulatePoint3(poses, sharedCal, measurements, rank_tol, optimize, model);}, gtwrap::internal::py_arg("poses"), gtwrap::internal::py_arg>("sharedCal"), gtwrap::internal::py_arg("measurements"), gtwrap::internal::py_arg("rank_tol"), gtwrap::internal::py_arg("optimize"), gtwrap::internal::py_arg("model") = nullptr); + m_.def("FindKarcherMeanPoint3",[](const std::vector& elements){return ::FindKarcherMean(elements);}, gtwrap::internal::py_arg&>("elements")); + m_.def("FindKarcherMeanSO3",[](const std::vector& elements){return ::FindKarcherMean(elements);}, gtwrap::internal::py_arg&>("elements")); + m_.def("FindKarcherMeanSO4",[](const std::vector& elements){return ::FindKarcherMean(elements);}, gtwrap::internal::py_arg&>("elements")); + m_.def("FindKarcherMeanPose3",[](const std::vector& elements){return ::FindKarcherMean(elements);}, gtwrap::internal::py_arg&>("elements")); + m_.def("TemplatedFunctionRot3",[](const gtsam::Rot3& t){ ::TemplatedFunction(t);}, gtwrap::internal::py_arg("t")); #include "python/specializations.h" diff --git a/tests/expected/python/geometry_pybind.cpp b/tests/expected/python/geometry_pybind.cpp index eb85c82..c9a43e1 100644 --- a/tests/expected/python/geometry_pybind.cpp +++ b/tests/expected/python/geometry_pybind.cpp @@ -7,6 +7,24 @@ #include "gtsam/geometry/Point2.h" #include "gtsam/geometry/Point3.h" #include +#include + +namespace gtwrap { +namespace internal { + +template +struct PyArgPolicy { + static pybind11::arg make(const char* name) { return pybind11::arg(name); } +}; + +template +pybind11::arg py_arg(const char* name) { + return PyArgPolicy::type>::make(name); +} + +} // namespace internal +} // namespace gtwrap + BOOST_CLASS_EXPORT(gtsam::Point2) BOOST_CLASS_EXPORT(gtsam::Point3) @@ -23,20 +41,20 @@ PYBIND11_MODULE(geometry_py, m_) { py::class_>(m_gtsam, "Point2") .def(py::init<>()) - .def(py::init(), py::arg("x"), py::arg("y")) + .def(py::init(), gtwrap::internal::py_arg("x"), gtwrap::internal::py_arg("y")) .def("x",[](gtsam::Point2* self){return self->x();}) .def("y",[](gtsam::Point2* self){return self->y();}) .def("dim",[](gtsam::Point2* self){return self->dim();}) .def("returnChar",[](gtsam::Point2* self){return self->returnChar();}) - .def("argChar",[](gtsam::Point2* self, char a){ self->argChar(a);}, py::arg("a")) - .def("argChar",[](gtsam::Point2* self, std::shared_ptr a){ self->argChar(a);}, py::arg("a")) - .def("argChar",[](gtsam::Point2* self, char& a){ self->argChar(a);}, py::arg("a")) - .def("argChar",[](gtsam::Point2* self, char* a){ self->argChar(a);}, py::arg("a")) - .def("argChar",[](gtsam::Point2* self, const std::shared_ptr a){ self->argChar(a);}, py::arg("a")) - .def("argChar",[](gtsam::Point2* self, const char& a){ self->argChar(a);}, py::arg("a")) - .def("argChar",[](gtsam::Point2* self, const char* a){ self->argChar(a);}, py::arg("a")) - .def("argUChar",[](gtsam::Point2* self, unsigned char a){ self->argUChar(a);}, py::arg("a")) - .def("eigenArguments",[](gtsam::Point2* self, const gtsam::Vector& v, const gtsam::Matrix& m){ self->eigenArguments(v, m);}, py::arg("v"), py::arg("m")) + .def("argChar",[](gtsam::Point2* self, char a){ self->argChar(a);}, gtwrap::internal::py_arg("a")) + .def("argChar",[](gtsam::Point2* self, std::shared_ptr a){ self->argChar(a);}, gtwrap::internal::py_arg>("a")) + .def("argChar",[](gtsam::Point2* self, char& a){ self->argChar(a);}, gtwrap::internal::py_arg("a")) + .def("argChar",[](gtsam::Point2* self, char* a){ self->argChar(a);}, gtwrap::internal::py_arg("a")) + .def("argChar",[](gtsam::Point2* self, const std::shared_ptr a){ self->argChar(a);}, gtwrap::internal::py_arg>("a")) + .def("argChar",[](gtsam::Point2* self, const char& a){ self->argChar(a);}, gtwrap::internal::py_arg("a")) + .def("argChar",[](gtsam::Point2* self, const char* a){ self->argChar(a);}, gtwrap::internal::py_arg("a")) + .def("argUChar",[](gtsam::Point2* self, unsigned char a){ self->argUChar(a);}, gtwrap::internal::py_arg("a")) + .def("eigenArguments",[](gtsam::Point2* self, const gtsam::Vector& v, const gtsam::Matrix& m){ self->eigenArguments(v, m);}, gtwrap::internal::py_arg("v"), gtwrap::internal::py_arg("m")) .def("vectorConfusion",[](gtsam::Point2* self){return self->vectorConfusion();}) .def("serialize", [](gtsam::Point2* self){ return gtsam::serialize(*self); }) .def("deserialize", [](gtsam::Point2* self, string serialized){ gtsam::deserialize(serialized, *self); }, py::arg("serialized")) @@ -45,7 +63,7 @@ PYBIND11_MODULE(geometry_py, m_) { [](py::tuple t){ /* __setstate__ */ gtsam::Point2 obj; gtsam::deserialize(t[0].cast(), obj); return obj; })); py::class_>(m_gtsam, "Point3") - .def(py::init(), py::arg("x"), py::arg("y"), py::arg("z")) + .def(py::init(), gtwrap::internal::py_arg("x"), gtwrap::internal::py_arg("y"), gtwrap::internal::py_arg("z")) .def("norm",[](gtsam::Point3* self){return self->norm();}) .def("serialize", [](gtsam::Point3* self){ return gtsam::serialize(*self); }) .def("deserialize", [](gtsam::Point3* self, string serialized){ gtsam::deserialize(serialized, *self); }, py::arg("serialized")) @@ -53,7 +71,7 @@ PYBIND11_MODULE(geometry_py, m_) { [](const gtsam::Point3 &a){ /* __getstate__: Returns a string that encodes the state of the object */ return py::make_tuple(gtsam::serialize(a)); }, [](py::tuple t){ /* __setstate__ */ gtsam::Point3 obj; gtsam::deserialize(t[0].cast(), obj); return obj; })) .def_static("staticFunction",[](){return gtsam::Point3::staticFunction();}) - .def_static("StaticFunctionRet",[](double z){return gtsam::Point3::StaticFunctionRet(z);}, py::arg("z")); + .def_static("StaticFunctionRet",[](double z){return gtsam::Point3::StaticFunctionRet(z);}, gtwrap::internal::py_arg("z")); #include "python/specializations.h" diff --git a/tests/expected/python/inheritance_pybind.cpp b/tests/expected/python/inheritance_pybind.cpp index 1dd3900..39729f2 100644 --- a/tests/expected/python/inheritance_pybind.cpp +++ b/tests/expected/python/inheritance_pybind.cpp @@ -5,6 +5,24 @@ #include "gtsam/nonlinear/utilities.h" // for RedirectCout. +#include + +namespace gtwrap { +namespace internal { + +template +struct PyArgPolicy { + static pybind11::arg make(const char* name) { return pybind11::arg(name); } +}; + +template +pybind11::arg py_arg(const char* name) { + return PyArgPolicy::type>::make(name); +} + +} // namespace internal +} // namespace gtwrap + @@ -20,48 +38,48 @@ PYBIND11_MODULE(inheritance_py, m_) { py::class_, MyBase, std::shared_ptr>>(m_, "MyTemplatePoint2") .def(py::init<>()) - .def("templatedMethodPoint2",[](MyTemplate* self, const gtsam::Point2& t){return self->templatedMethod(t);}, py::arg("t")) - .def("templatedMethodPoint3",[](MyTemplate* self, const gtsam::Point3& t){return self->templatedMethod(t);}, py::arg("t")) - .def("templatedMethodVector",[](MyTemplate* self, const gtsam::Vector& t){return self->templatedMethod(t);}, py::arg("t")) - .def("templatedMethodMatrix",[](MyTemplate* self, const gtsam::Matrix& t){return self->templatedMethod(t);}, py::arg("t")) - .def("accept_T",[](MyTemplate* self, const gtsam::Point2& value){ self->accept_T(value);}, py::arg("value")) - .def("accept_Tptr",[](MyTemplate* self, std::shared_ptr value){ self->accept_Tptr(value);}, py::arg("value")) - .def("return_Tptr",[](MyTemplate* self, std::shared_ptr value){return self->return_Tptr(value);}, py::arg("value")) - .def("return_T",[](MyTemplate* self, gtsam::Point2* value){return self->return_T(value);}, py::arg("value")) + .def("templatedMethodPoint2",[](MyTemplate* self, const gtsam::Point2& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("templatedMethodPoint3",[](MyTemplate* self, const gtsam::Point3& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("templatedMethodVector",[](MyTemplate* self, const gtsam::Vector& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("templatedMethodMatrix",[](MyTemplate* self, const gtsam::Matrix& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("accept_T",[](MyTemplate* self, const gtsam::Point2& value){ self->accept_T(value);}, gtwrap::internal::py_arg("value")) + .def("accept_Tptr",[](MyTemplate* self, std::shared_ptr value){ self->accept_Tptr(value);}, gtwrap::internal::py_arg>("value")) + .def("return_Tptr",[](MyTemplate* self, std::shared_ptr value){return self->return_Tptr(value);}, gtwrap::internal::py_arg>("value")) + .def("return_T",[](MyTemplate* self, gtsam::Point2* value){return self->return_T(value);}, gtwrap::internal::py_arg("value")) .def("create_ptrs",[](MyTemplate* self){return self->create_ptrs();}) .def("create_MixedPtrs",[](MyTemplate* self){return self->create_MixedPtrs();}) - .def("return_ptrs",[](MyTemplate* self, std::shared_ptr p1, std::shared_ptr p2){return self->return_ptrs(p1, p2);}, py::arg("p1"), py::arg("p2")) - .def_static("Level",[](const gtsam::Point2& K){return MyTemplate::Level(K);}, py::arg("K")); + .def("return_ptrs",[](MyTemplate* self, std::shared_ptr p1, std::shared_ptr p2){return self->return_ptrs(p1, p2);}, gtwrap::internal::py_arg>("p1"), gtwrap::internal::py_arg>("p2")) + .def_static("Level",[](const gtsam::Point2& K){return MyTemplate::Level(K);}, gtwrap::internal::py_arg("K")); py::class_, MyBase, std::shared_ptr>>(m_, "MyTemplateMatrix") .def(py::init<>()) - .def("templatedMethodPoint2",[](MyTemplate* self, const gtsam::Point2& t){return self->templatedMethod(t);}, py::arg("t")) - .def("templatedMethodPoint3",[](MyTemplate* self, const gtsam::Point3& t){return self->templatedMethod(t);}, py::arg("t")) - .def("templatedMethodVector",[](MyTemplate* self, const gtsam::Vector& t){return self->templatedMethod(t);}, py::arg("t")) - .def("templatedMethodMatrix",[](MyTemplate* self, const gtsam::Matrix& t){return self->templatedMethod(t);}, py::arg("t")) - .def("accept_T",[](MyTemplate* self, const gtsam::Matrix& value){ self->accept_T(value);}, py::arg("value")) - .def("accept_Tptr",[](MyTemplate* self, std::shared_ptr value){ self->accept_Tptr(value);}, py::arg("value")) - .def("return_Tptr",[](MyTemplate* self, std::shared_ptr value){return self->return_Tptr(value);}, py::arg("value")) - .def("return_T",[](MyTemplate* self, gtsam::Matrix* value){return self->return_T(value);}, py::arg("value")) + .def("templatedMethodPoint2",[](MyTemplate* self, const gtsam::Point2& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("templatedMethodPoint3",[](MyTemplate* self, const gtsam::Point3& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("templatedMethodVector",[](MyTemplate* self, const gtsam::Vector& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("templatedMethodMatrix",[](MyTemplate* self, const gtsam::Matrix& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("accept_T",[](MyTemplate* self, const gtsam::Matrix& value){ self->accept_T(value);}, gtwrap::internal::py_arg("value")) + .def("accept_Tptr",[](MyTemplate* self, std::shared_ptr value){ self->accept_Tptr(value);}, gtwrap::internal::py_arg>("value")) + .def("return_Tptr",[](MyTemplate* self, std::shared_ptr value){return self->return_Tptr(value);}, gtwrap::internal::py_arg>("value")) + .def("return_T",[](MyTemplate* self, gtsam::Matrix* value){return self->return_T(value);}, gtwrap::internal::py_arg("value")) .def("create_ptrs",[](MyTemplate* self){return self->create_ptrs();}) .def("create_MixedPtrs",[](MyTemplate* self){return self->create_MixedPtrs();}) - .def("return_ptrs",[](MyTemplate* self, std::shared_ptr p1, std::shared_ptr p2){return self->return_ptrs(p1, p2);}, py::arg("p1"), py::arg("p2")) - .def_static("Level",[](const gtsam::Matrix& K){return MyTemplate::Level(K);}, py::arg("K")); + .def("return_ptrs",[](MyTemplate* self, std::shared_ptr p1, std::shared_ptr p2){return self->return_ptrs(p1, p2);}, gtwrap::internal::py_arg>("p1"), gtwrap::internal::py_arg>("p2")) + .def_static("Level",[](const gtsam::Matrix& K){return MyTemplate::Level(K);}, gtwrap::internal::py_arg("K")); py::class_, MyBase, std::shared_ptr>>(m_, "MyTemplateA") .def(py::init<>()) - .def("templatedMethodPoint2",[](MyTemplate* self, const gtsam::Point2& t){return self->templatedMethod(t);}, py::arg("t")) - .def("templatedMethodPoint3",[](MyTemplate* self, const gtsam::Point3& t){return self->templatedMethod(t);}, py::arg("t")) - .def("templatedMethodVector",[](MyTemplate* self, const gtsam::Vector& t){return self->templatedMethod(t);}, py::arg("t")) - .def("templatedMethodMatrix",[](MyTemplate* self, const gtsam::Matrix& t){return self->templatedMethod(t);}, py::arg("t")) - .def("accept_T",[](MyTemplate* self, const A& value){ self->accept_T(value);}, py::arg("value")) - .def("accept_Tptr",[](MyTemplate* self, std::shared_ptr value){ self->accept_Tptr(value);}, py::arg("value")) - .def("return_Tptr",[](MyTemplate* self, std::shared_ptr value){return self->return_Tptr(value);}, py::arg("value")) - .def("return_T",[](MyTemplate* self, A* value){return self->return_T(value);}, py::arg("value")) + .def("templatedMethodPoint2",[](MyTemplate* self, const gtsam::Point2& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("templatedMethodPoint3",[](MyTemplate* self, const gtsam::Point3& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("templatedMethodVector",[](MyTemplate* self, const gtsam::Vector& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("templatedMethodMatrix",[](MyTemplate* self, const gtsam::Matrix& t){return self->templatedMethod(t);}, gtwrap::internal::py_arg("t")) + .def("accept_T",[](MyTemplate* self, const A& value){ self->accept_T(value);}, gtwrap::internal::py_arg("value")) + .def("accept_Tptr",[](MyTemplate* self, std::shared_ptr value){ self->accept_Tptr(value);}, gtwrap::internal::py_arg>("value")) + .def("return_Tptr",[](MyTemplate* self, std::shared_ptr value){return self->return_Tptr(value);}, gtwrap::internal::py_arg>("value")) + .def("return_T",[](MyTemplate* self, A* value){return self->return_T(value);}, gtwrap::internal::py_arg("value")) .def("create_ptrs",[](MyTemplate* self){return self->create_ptrs();}) .def("create_MixedPtrs",[](MyTemplate* self){return self->create_MixedPtrs();}) - .def("return_ptrs",[](MyTemplate* self, std::shared_ptr p1, std::shared_ptr p2){return self->return_ptrs(p1, p2);}, py::arg("p1"), py::arg("p2")) - .def_static("Level",[](const A& K){return MyTemplate::Level(K);}, py::arg("K")); + .def("return_ptrs",[](MyTemplate* self, std::shared_ptr p1, std::shared_ptr p2){return self->return_ptrs(p1, p2);}, gtwrap::internal::py_arg>("p1"), gtwrap::internal::py_arg>("p2")) + .def_static("Level",[](const A& K){return MyTemplate::Level(K);}, gtwrap::internal::py_arg("K")); py::class_, std::shared_ptr>(m_, "ForwardKinematicsFactor"); diff --git a/tests/expected/python/namespaces_pybind.cpp b/tests/expected/python/namespaces_pybind.cpp index ab8418b..398b8de 100644 --- a/tests/expected/python/namespaces_pybind.cpp +++ b/tests/expected/python/namespaces_pybind.cpp @@ -11,6 +11,24 @@ #include "path/to/ns3.h" #include "gtsam/nonlinear/Values.h" +#include + +namespace gtwrap { +namespace internal { + +template +struct PyArgPolicy { + static pybind11::arg make(const char* name) { return pybind11::arg(name); } +}; + +template +pybind11::arg py_arg(const char* name) { + return PyArgPolicy::type>::make(name); +} + +} // namespace internal +} // namespace gtwrap + @@ -34,8 +52,8 @@ PYBIND11_MODULE(namespaces_py, m_) { py::class_>(m_ns2, "ClassA") .def(py::init<>()) .def("memberFunction",[](ns2::ClassA* self){return self->memberFunction();}) - .def("nsArg",[](ns2::ClassA* self, const ns1::ClassB& arg){return self->nsArg(arg);}, py::arg("arg")) - .def("nsReturn",[](ns2::ClassA* self, double q){return self->nsReturn(q);}, py::arg("q")) + .def("nsArg",[](ns2::ClassA* self, const ns1::ClassB& arg){return self->nsArg(arg);}, gtwrap::internal::py_arg("arg")) + .def("nsReturn",[](ns2::ClassA* self, double q){return self->nsReturn(q);}, gtwrap::internal::py_arg("q")) .def_static("afunction",[](){return ns2::ClassA::afunction();}); pybind11::module m_ns2_ns3 = m_ns2.def_submodule("ns3", "ns3 submodule"); @@ -47,8 +65,8 @@ PYBIND11_MODULE(namespaces_py, m_) { m_ns2.attr("aNs2Var") = ns2::aNs2Var; m_ns2.def("aGlobalFunction",[](){return ns2::aGlobalFunction();}); - m_ns2.def("overloadedGlobalFunction",[](const ns1::ClassA& a){return ns2::overloadedGlobalFunction(a);}, py::arg("a")); - m_ns2.def("overloadedGlobalFunction",[](const ns1::ClassA& a, double b){return ns2::overloadedGlobalFunction(a, b);}, py::arg("a"), py::arg("b")); + m_ns2.def("overloadedGlobalFunction",[](const ns1::ClassA& a){return ns2::overloadedGlobalFunction(a);}, gtwrap::internal::py_arg("a")); + m_ns2.def("overloadedGlobalFunction",[](const ns1::ClassA& a, double b){return ns2::overloadedGlobalFunction(a, b);}, gtwrap::internal::py_arg("a"), gtwrap::internal::py_arg("b")); py::class_>(m_, "ClassD") .def(py::init<>()); @@ -56,11 +74,11 @@ PYBIND11_MODULE(namespaces_py, m_) { py::class_>(m_gtsam, "Values") .def(py::init<>()) - .def(py::init(), py::arg("other")) - .def("insert_vector",[](gtsam::Values* self, size_t j, const gtsam::Vector& vector){ self->insert(j, vector);}, py::arg("j"), py::arg("vector")) - .def("insert",[](gtsam::Values* self, size_t j, const gtsam::Vector& vector){ self->insert(j, vector);}, py::arg("j"), py::arg("vector")) - .def("insert_matrix",[](gtsam::Values* self, size_t j, const gtsam::Matrix& matrix){ self->insert(j, matrix);}, py::arg("j"), py::arg("matrix")) - .def("insert",[](gtsam::Values* self, size_t j, const gtsam::Matrix& matrix){ self->insert(j, matrix);}, py::arg("j"), py::arg("matrix")); + .def(py::init(), gtwrap::internal::py_arg("other")) + .def("insert_vector",[](gtsam::Values* self, size_t j, const gtsam::Vector& vector){ self->insert(j, vector);}, gtwrap::internal::py_arg("j"), gtwrap::internal::py_arg("vector")) + .def("insert",[](gtsam::Values* self, size_t j, const gtsam::Vector& vector){ self->insert(j, vector);}, gtwrap::internal::py_arg("j"), gtwrap::internal::py_arg("vector")) + .def("insert_matrix",[](gtsam::Values* self, size_t j, const gtsam::Matrix& matrix){ self->insert(j, matrix);}, gtwrap::internal::py_arg("j"), gtwrap::internal::py_arg("matrix")) + .def("insert",[](gtsam::Values* self, size_t j, const gtsam::Matrix& matrix){ self->insert(j, matrix);}, gtwrap::internal::py_arg("j"), gtwrap::internal::py_arg("matrix")); #include "python/specializations.h" diff --git a/tests/expected/python/operator_pybind.cpp b/tests/expected/python/operator_pybind.cpp index 028362a..672820b 100644 --- a/tests/expected/python/operator_pybind.cpp +++ b/tests/expected/python/operator_pybind.cpp @@ -6,6 +6,24 @@ #include "gtsam/geometry/Pose3.h" +#include + +namespace gtwrap { +namespace internal { + +template +struct PyArgPolicy { + static pybind11::arg make(const char* name) { return pybind11::arg(name); } +}; + +template +pybind11::arg py_arg(const char* name) { + return PyArgPolicy::type>::make(name); +} + +} // namespace internal +} // namespace gtwrap + @@ -20,7 +38,7 @@ PYBIND11_MODULE(operator_py, m_) { py::class_>(m_gtsam, "Pose3") .def(py::init<>()) - .def(py::init(), py::arg("R"), py::arg("t")) + .def(py::init(), gtwrap::internal::py_arg("R"), gtwrap::internal::py_arg("t")) .def(py::self * py::self); py::class_, std::shared_ptr>>(m_gtsam, "ContainerMatrix") diff --git a/tests/expected/python/special_cases_pybind.cpp b/tests/expected/python/special_cases_pybind.cpp index 15db647..bdbe466 100644 --- a/tests/expected/python/special_cases_pybind.cpp +++ b/tests/expected/python/special_cases_pybind.cpp @@ -6,6 +6,24 @@ #include "gtsam/geometry/Cal3Bundler.h" +#include + +namespace gtwrap { +namespace internal { + +template +struct PyArgPolicy { + static pybind11::arg make(const char* name) { return pybind11::arg(name); } +}; + +template +pybind11::arg py_arg(const char* name) { + return PyArgPolicy::type>::make(name); +} + +} // namespace internal +} // namespace gtwrap + @@ -19,7 +37,7 @@ PYBIND11_MODULE(special_cases_py, m_) { pybind11::module m_gtsam = m_.def_submodule("gtsam", "gtsam submodule"); py::class_>(m_gtsam, "NonlinearFactorGraph") - .def("addPriorPinholeCameraCal3Bundler",[](gtsam::NonlinearFactorGraph* self, size_t key, const gtsam::PinholeCamera& prior, const std::shared_ptr noiseModel){ self->addPrior>(key, prior, noiseModel);}, py::arg("key"), py::arg("prior"), py::arg("noiseModel")); + .def("addPriorPinholeCameraCal3Bundler",[](gtsam::NonlinearFactorGraph* self, size_t key, const gtsam::PinholeCamera& prior, const std::shared_ptr noiseModel){ self->addPrior>(key, prior, noiseModel);}, gtwrap::internal::py_arg("key"), gtwrap::internal::py_arg&>("prior"), gtwrap::internal::py_arg>("noiseModel")); py::class_>(m_gtsam, "SfmTrack") .def_readwrite("measurements", >sam::SfmTrack::measurements); diff --git a/tests/expected/python/templates_pybind.cpp b/tests/expected/python/templates_pybind.cpp index 4a23b1e..957d65f 100644 --- a/tests/expected/python/templates_pybind.cpp +++ b/tests/expected/python/templates_pybind.cpp @@ -5,6 +5,24 @@ #include "gtsam/nonlinear/utilities.h" // for RedirectCout. +#include + +namespace gtwrap { +namespace internal { + +template +struct PyArgPolicy { + static pybind11::arg make(const char* name) { return pybind11::arg(name); } +}; + +template +pybind11::arg py_arg(const char* name) { + return PyArgPolicy::type>::make(name); +} + +} // namespace internal +} // namespace gtwrap + @@ -18,12 +36,12 @@ PYBIND11_MODULE(templates_py, m_) { py::class_>(m_, "TemplatedConstructor") .def(py::init<>()) - .def(py::init(), py::arg("arg")) - .def(py::init(), py::arg("arg")) - .def(py::init(), py::arg("arg")); + .def(py::init(), gtwrap::internal::py_arg("arg")) + .def(py::init(), gtwrap::internal::py_arg("arg")) + .def(py::init(), gtwrap::internal::py_arg("arg")); py::class_, std::shared_ptr>>(m_, "ScopedTemplateResult") - .def(py::init(), py::arg("arg")); + .def(py::init(), gtwrap::internal::py_arg("arg")); #include "python/specializations.h" diff --git a/tests/fixtures/arg_policies.i b/tests/fixtures/arg_policies.i new file mode 100644 index 0000000..9778315 --- /dev/null +++ b/tests/fixtures/arg_policies.i @@ -0,0 +1,9 @@ +#include + +namespace testing { + +class ArgPolicyFixture { + void noConvert(const testing::SpecialView& values, int count = 1) const; +}; + +} diff --git a/tests/fixtures/matrix_views.i b/tests/fixtures/matrix_views.i new file mode 100644 index 0000000..dba1689 --- /dev/null +++ b/tests/fixtures/matrix_views.i @@ -0,0 +1,11 @@ +#include + +namespace gtsam { + +class MatrixViewFixture { + MatrixViewFixture(); + void acceptView(gtsam::ConstMatrixView points) const; + gtsam::Matrix scaleView(gtsam::ConstMatrixView points, double scale = 1.0) const; +}; + +} diff --git a/tests/fixtures/return_policies.i b/tests/fixtures/return_policies.i new file mode 100644 index 0000000..e22471a --- /dev/null +++ b/tests/fixtures/return_policies.i @@ -0,0 +1,7 @@ +#include + +class ReturnPolicyFixture { + gtsam::Matrix return_value(const gtsam::Matrix& value) const; + const gtsam::Matrix& return_const_ref(const gtsam::Matrix& value) const; + gtsam::Matrix& return_mutable_ref(const gtsam::Matrix& value); +}; diff --git a/tests/test_matlab_wrapper.py b/tests/test_matlab_wrapper.py index e95ca88..37eb692 100644 --- a/tests/test_matlab_wrapper.py +++ b/tests/test_matlab_wrapper.py @@ -79,6 +79,45 @@ def test_geometry(self): actual = osp.join(self.MATLAB_ACTUAL_DIR, file) self.compare_and_diff(file, actual) + def test_matrix_view_arguments(self): + """Test that matrix view arguments use MATLAB double arrays directly.""" + file = osp.join(self.INTERFACE_DIR, 'matrix_views.i') + + wrapper = MatlabWrapper(module_name='matrix_views', + top_module_namespace=['gtsam'], + ignore_classes=['']) + + wrapper.wrap([file], path=self.MATLAB_ACTUAL_DIR) + + cpp_file = osp.join(self.MATLAB_ACTUAL_DIR, 'matrix_views_wrapper.cpp') + with open(cpp_file, 'r', encoding='UTF-8') as f: + cpp_content = f.read() + + self.assertIn( + 'gtsam::ConstMatrixView points = unwrapMatrixView< gtsam::ConstMatrixView >(in[1]);', + cpp_content) + self.assertIn('obj->acceptView(points);', cpp_content) + self.assertIn('obj->scaleView(points,scale)', cpp_content) + self.assertNotIn('unwrap< gtsam::ConstMatrixView >', cpp_content) + self.assertNotIn('*points', cpp_content) + + m_file = osp.join(self.MATLAB_ACTUAL_DIR, '+gtsam', + 'MatrixViewFixture.m') + with open(m_file, 'r', encoding='UTF-8') as f: + matlab_content = f.read() + + self.assertIn("isa(varargin{1},'double')", matlab_content) + + matlab_header = osp.join(self.TEST_DIR, '..', 'matlab.h') + with open(matlab_header, 'r', encoding='UTF-8') as f: + header_content = f.read() + + self.assertIn('unwrapMatrixView', header_content) + self.assertIn('mxIsSparse(array)', header_content) + self.assertIn('mwSize rows', header_content) + self.assertIn('Eigen::Index m', header_content) + self.assertIn('Stride(m, 1)', header_content) + def test_functions(self): """Test interface file with function info.""" file = osp.join(self.INTERFACE_DIR, 'functions.i') diff --git a/tests/test_pybind_wrapper.py b/tests/test_pybind_wrapper.py index 28f2ed5..1341706 100644 --- a/tests/test_pybind_wrapper.py +++ b/tests/test_pybind_wrapper.py @@ -35,13 +35,15 @@ def wrap_content(self, sources, module_name, output_dir, - use_boost_serialization=False): + use_boost_serialization=False, + module_template=None): """ Common function to wrap content in `sources`. """ - with open(osp.join(self.TEST_DIR, "pybind_wrapper.tpl"), - encoding="UTF-8") as template_file: - module_template = template_file.read() + if module_template is None: + with open(osp.join(self.TEST_DIR, "pybind_wrapper.tpl"), + encoding="UTF-8") as template_file: + module_template = template_file.read() # Create Pybind wrapper instance wrapper = PybindWrapper( @@ -159,6 +161,38 @@ def test_enum(self): self.compare_and_diff('enum_pybind.cpp', output) + def test_argument_policy_hook(self): + """Test that typed args are emitted through the overridable policy hook.""" + source = osp.join(self.INTERFACE_DIR, 'arg_policies.i') + module_template = """#include + +{includes} + +#include "python/arg_policy_preamble.h" + +namespace py = pybind11; + +PYBIND11_MODULE({module_name}, m_) {{ + m_.doc() = "pybind11 wrapper of {module_name}"; + +{wrapped_namespace} + +}} +""" + output = self.wrap_content([source], + 'arg_policies_py', + self.PYTHON_ACTUAL_DIR, + module_template=module_template) + + with open(output, 'r', encoding='UTF-8') as f: + content = f.read() + + self.assertLess(content.index('template \nstruct PyArgPolicy'), + content.index('#include "python/arg_policy_preamble.h"')) + self.assertIn( + 'gtwrap::internal::py_arg("values")', + content) + self.assertIn('gtwrap::internal::py_arg("count") = 1', content) def test_const_ref_return_policy(self): """Test that methods returning const T& emit reference_internal policy. @@ -195,6 +229,21 @@ def test_const_ref_return_policy(self): self.assertNotIn('reference_internal', line) self.assertNotIn('-> const auto&', line) + source = osp.join(self.INTERFACE_DIR, 'return_policies.i') + output = self.wrap_content([source], 'return_policies_py', + self.PYTHON_ACTUAL_DIR) + + with open(output, 'r', encoding='UTF-8') as f: + content = f.read() + + for line in content.split('\n'): + if 'return_const_ref' in line: + self.assertIn('reference_internal', line) + self.assertIn('-> const auto&', line) + if '.def("return_mutable_ref"' in line or '.def("return_value"' in line: + self.assertNotIn('reference_internal', line) + self.assertNotIn('-> const auto&', line) + if __name__ == '__main__': unittest.main()