diff --git a/wrap/DOCS.md b/wrap/DOCS.md index 00104c123..c0c4310fa 100644 --- a/wrap/DOCS.md +++ b/wrap/DOCS.md @@ -98,6 +98,7 @@ The python wrapper supports keyword arguments for functions/methods. Hence, the - Virtual inheritance - Specify fully-qualified base classes, i.e. `virtual class Derived : ns::Base {` where `ns` is the namespace. - Mark with `virtual` keyword, e.g. `virtual class Base {`, and also `virtual class Derived : ns::Base {`. + - Base classes can be templated, e.g. `virtual class Dog: ns::Animal {};`. This is useful when you want to inherit from specialized classes. - Forward declarations must also be marked virtual, e.g. `virtual class ns::Base;` and also `virtual class ns::Derived;`. - Pure virtual (abstract) classes should list no constructors in the interface file. diff --git a/wrap/cmake/GtwrapUtils.cmake b/wrap/cmake/GtwrapUtils.cmake index 8479015dc..3c26e70ae 100644 --- a/wrap/cmake/GtwrapUtils.cmake +++ b/wrap/cmake/GtwrapUtils.cmake @@ -103,3 +103,37 @@ macro(gtwrap_get_python_version) configure_python_variables() endmacro() + +# Concatenate multiple wrapper interface headers into one. +# The concatenation will be (re)performed if and only if any interface files +# change. +# +# Arguments: +# ~~~ +# destination: The concatenated master interface header file will be placed here. +# inputs (optional): All the input interface header files +function(combine_interface_headers + destination + #inputs + ) + # check if any interface headers changed + foreach(INTERFACE_FILE ${ARGN}) + if(NOT EXISTS ${destination} OR + ${INTERFACE_FILE} IS_NEWER_THAN ${destination}) + set(UPDATE_INTERFACE TRUE) + endif() + # trigger cmake on file change + set_property(DIRECTORY + APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${INTERFACE_FILE}) + endforeach() + # if so, then update the overall interface file + if (UPDATE_INTERFACE) + file(WRITE ${destination} "") + # append additional interface headers to end of gtdynamics.i + foreach(INTERFACE_FILE ${ARGN}) + file(READ ${INTERFACE_FILE} interface_contents) + file(APPEND ${destination} "${interface_contents}") + endforeach() + endif() +endfunction() diff --git a/wrap/gtwrap/interface_parser/classes.py b/wrap/gtwrap/interface_parser/classes.py index e2c28d8ed..255c26137 100644 --- a/wrap/gtwrap/interface_parser/classes.py +++ b/wrap/gtwrap/interface_parser/classes.py @@ -279,7 +279,7 @@ class Class: elif isinstance(m, Operator): self.operators.append(m) - _parent = COLON + Typename.rule("parent_class") + _parent = COLON + (TemplatedType.rule ^ Typename.rule)("parent_class") rule = ( Optional(Template.rule("template")) # + Optional(VIRTUAL("is_virtual")) # @@ -319,11 +319,16 @@ class Class: self.is_virtual = is_virtual self.name = name if parent_class: + # If it is in an iterable, extract the parent class. if isinstance(parent_class, Iterable): - self.parent_class = parent_class[0] - else: - self.parent_class = parent_class + parent_class = parent_class[0] + # If the base class is a TemplatedType, + # we want the instantiated Typename + if isinstance(parent_class, TemplatedType): + parent_class = parent_class.typename + + self.parent_class = parent_class else: self.parent_class = '' diff --git a/wrap/gtwrap/pybind_wrapper.py b/wrap/gtwrap/pybind_wrapper.py index 4b532f6e1..1e0d412a0 100755 --- a/wrap/gtwrap/pybind_wrapper.py +++ b/wrap/gtwrap/pybind_wrapper.py @@ -125,6 +125,11 @@ class PybindWrapper: )) if method.name == 'print': + # Redirect stdout - see pybind docs for why this is a good idea: + # https://pybind11.readthedocs.io/en/stable/advanced/pycpp/utilities.html#capturing-standard-output-from-ostream + ret = ret.replace('self->', 'py::scoped_ostream_redirect output; self->') + + # __repr__() uses print's implementation: type_list = method.args.to_cpp(self.use_boost) if len(type_list) > 0 and type_list[0].strip() == 'string': ret += '''{prefix}.def("__repr__", diff --git a/wrap/tests/expected/matlab/inheritance_wrapper.cpp b/wrap/tests/expected/matlab/inheritance_wrapper.cpp index f12f6ce57..e68b3a6e4 100644 --- a/wrap/tests/expected/matlab/inheritance_wrapper.cpp +++ b/wrap/tests/expected/matlab/inheritance_wrapper.cpp @@ -50,6 +50,8 @@ typedef std::set*> Collector_MyTemplatePoint static Collector_MyTemplatePoint2 collector_MyTemplatePoint2; typedef std::set*> Collector_MyTemplateMatrix; static Collector_MyTemplateMatrix collector_MyTemplateMatrix; +typedef std::set*> Collector_ForwardKinematicsFactor; +static Collector_ForwardKinematicsFactor collector_ForwardKinematicsFactor; void _deleteAllObjects() { @@ -141,6 +143,12 @@ void _deleteAllObjects() collector_MyTemplateMatrix.erase(iter++); anyDeleted = true; } } + { for(Collector_ForwardKinematicsFactor::iterator iter = collector_ForwardKinematicsFactor.begin(); + iter != collector_ForwardKinematicsFactor.end(); ) { + delete *iter; + collector_ForwardKinematicsFactor.erase(iter++); + anyDeleted = true; + } } if(anyDeleted) cout << "WARNING: Wrap modules with variables in the workspace have been reloaded due to\n" @@ -156,6 +164,7 @@ void _inheritance_RTTIRegister() { types.insert(std::make_pair(typeid(MyBase).name(), "MyBase")); types.insert(std::make_pair(typeid(MyTemplatePoint2).name(), "MyTemplatePoint2")); types.insert(std::make_pair(typeid(MyTemplateMatrix).name(), "MyTemplateMatrix")); + types.insert(std::make_pair(typeid(ForwardKinematicsFactor).name(), "ForwardKinematicsFactor")); mxArray *registry = mexGetVariable("global", "gtsamwrap_rttiRegistry"); if(!registry) @@ -555,6 +564,40 @@ void MyTemplateMatrix_Level_34(int nargout, mxArray *out[], int nargin, const mx out[0] = wrap_shared_ptr(boost::make_shared>(MyTemplate::Level(K)),"MyTemplateMatrix", false); } +void Test_set_container_35(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +{ + checkArguments("set_container",nargout,nargin-1,1); + auto obj = unwrap_shared_ptr(in[0], "ptr_Test"); + boost::shared_ptr> container = unwrap_shared_ptr< std::vector >(in[1], "ptr_stdvectorTest"); + obj->set_container(*container); +} + +void ForwardKinematicsFactor_collectorInsertAndMakeBase_35(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +{ + mexAtExit(&_deleteAllObjects); + typedef boost::shared_ptr Shared; + + Shared *self = *reinterpret_cast (mxGetData(in[0])); + collector_ForwardKinematicsFactor.insert(self); + + typedef boost::shared_ptr> SharedBase; + out[0] = mxCreateNumericMatrix(1, 1, mxUINT32OR64_CLASS, mxREAL); + *reinterpret_cast(mxGetData(out[0])) = new SharedBase(*self); +} + +void ForwardKinematicsFactor_deconstructor_37(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +{ + typedef boost::shared_ptr Shared; + checkArguments("delete_ForwardKinematicsFactor",nargout,nargin,1); + Shared *self = *reinterpret_cast(mxGetData(in[0])); + Collector_ForwardKinematicsFactor::iterator item; + item = collector_ForwardKinematicsFactor.find(self); + if(item != collector_ForwardKinematicsFactor.end()) { + delete self; + collector_ForwardKinematicsFactor.erase(item); + } +} + void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) { @@ -672,6 +715,15 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) case 34: MyTemplateMatrix_Level_34(nargout, out, nargin-1, in+1); break; + case 35: + Test_set_container_35(nargout, out, nargin-1, in+1); + break; + case 36: + ForwardKinematicsFactor_collectorInsertAndMakeBase_35(nargout, out, nargin-1, in+1); + break; + case 37: + ForwardKinematicsFactor_deconstructor_37(nargout, out, nargin-1, in+1); + break; } } catch(const std::exception& e) { mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str()); diff --git a/wrap/tests/expected/matlab/namespaces_wrapper.cpp b/wrap/tests/expected/matlab/namespaces_wrapper.cpp index 67bbb00c9..aba5c49ea 100644 --- a/wrap/tests/expected/matlab/namespaces_wrapper.cpp +++ b/wrap/tests/expected/matlab/namespaces_wrapper.cpp @@ -55,6 +55,8 @@ typedef std::set*> Collector_MyTemplatePoint static Collector_MyTemplatePoint2 collector_MyTemplatePoint2; typedef std::set*> Collector_MyTemplateMatrix; static Collector_MyTemplateMatrix collector_MyTemplateMatrix; +typedef std::set*> Collector_ForwardKinematicsFactor; +static Collector_ForwardKinematicsFactor collector_ForwardKinematicsFactor; typedef std::set*> Collector_ns1ClassA; static Collector_ns1ClassA collector_ns1ClassA; typedef std::set*> Collector_ns1ClassB; @@ -158,6 +160,12 @@ void _deleteAllObjects() collector_MyTemplateMatrix.erase(iter++); anyDeleted = true; } } + { for(Collector_ForwardKinematicsFactor::iterator iter = collector_ForwardKinematicsFactor.begin(); + iter != collector_ForwardKinematicsFactor.end(); ) { + delete *iter; + collector_ForwardKinematicsFactor.erase(iter++); + anyDeleted = true; + } } { for(Collector_ns1ClassA::iterator iter = collector_ns1ClassA.begin(); iter != collector_ns1ClassA.end(); ) { delete *iter; @@ -209,6 +217,7 @@ void _namespaces_RTTIRegister() { types.insert(std::make_pair(typeid(MyBase).name(), "MyBase")); types.insert(std::make_pair(typeid(MyTemplatePoint2).name(), "MyTemplatePoint2")); types.insert(std::make_pair(typeid(MyTemplateMatrix).name(), "MyTemplateMatrix")); + types.insert(std::make_pair(typeid(ForwardKinematicsFactor).name(), "ForwardKinematicsFactor")); mxArray *registry = mexGetVariable("global", "gtsamwrap_rttiRegistry"); if(!registry) diff --git a/wrap/tests/expected/matlab/special_cases_wrapper.cpp b/wrap/tests/expected/matlab/special_cases_wrapper.cpp index b0ee1bcf8..d79a41ace 100644 --- a/wrap/tests/expected/matlab/special_cases_wrapper.cpp +++ b/wrap/tests/expected/matlab/special_cases_wrapper.cpp @@ -58,6 +58,8 @@ typedef std::set*> Collector_MyTemplatePoint static Collector_MyTemplatePoint2 collector_MyTemplatePoint2; typedef std::set*> Collector_MyTemplateMatrix; static Collector_MyTemplateMatrix collector_MyTemplateMatrix; +typedef std::set*> Collector_ForwardKinematicsFactor; +static Collector_ForwardKinematicsFactor collector_ForwardKinematicsFactor; typedef std::set*> Collector_ns1ClassA; static Collector_ns1ClassA collector_ns1ClassA; typedef std::set*> Collector_ns1ClassB; @@ -169,6 +171,12 @@ void _deleteAllObjects() collector_MyTemplateMatrix.erase(iter++); anyDeleted = true; } } + { for(Collector_ForwardKinematicsFactor::iterator iter = collector_ForwardKinematicsFactor.begin(); + iter != collector_ForwardKinematicsFactor.end(); ) { + delete *iter; + collector_ForwardKinematicsFactor.erase(iter++); + anyDeleted = true; + } } { for(Collector_ns1ClassA::iterator iter = collector_ns1ClassA.begin(); iter != collector_ns1ClassA.end(); ) { delete *iter; @@ -244,6 +252,7 @@ void _special_cases_RTTIRegister() { types.insert(std::make_pair(typeid(MyBase).name(), "MyBase")); types.insert(std::make_pair(typeid(MyTemplatePoint2).name(), "MyTemplatePoint2")); types.insert(std::make_pair(typeid(MyTemplateMatrix).name(), "MyTemplateMatrix")); + types.insert(std::make_pair(typeid(ForwardKinematicsFactor).name(), "ForwardKinematicsFactor")); mxArray *registry = mexGetVariable("global", "gtsamwrap_rttiRegistry"); if(!registry) diff --git a/wrap/tests/expected/python/class_pybind.cpp b/wrap/tests/expected/python/class_pybind.cpp index 68f7a42e4..43705778b 100644 --- a/wrap/tests/expected/python/class_pybind.cpp +++ b/wrap/tests/expected/python/class_pybind.cpp @@ -55,7 +55,7 @@ PYBIND11_MODULE(class_py, m_) { .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("print_",[](Test* self){ self->print();}) + .def("print_",[](Test* self){ py::scoped_ostream_redirect output; self->print();}) .def("__repr__", [](const Test &a) { gtsam::RedirectCout redirect; diff --git a/wrap/tests/expected/python/inheritance_pybind.cpp b/wrap/tests/expected/python/inheritance_pybind.cpp index 6f34d39b6..d6cd77ca0 100644 --- a/wrap/tests/expected/python/inheritance_pybind.cpp +++ b/wrap/tests/expected/python/inheritance_pybind.cpp @@ -54,6 +54,8 @@ PYBIND11_MODULE(inheritance_py, m_) { .def("return_ptrs",[](MyTemplate* self, const std::shared_ptr p1, const 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")); + py::class_, std::shared_ptr>(m_, "ForwardKinematicsFactor"); + #include "python/specializations.h" diff --git a/wrap/tests/fixtures/inheritance.i b/wrap/tests/fixtures/inheritance.i index e104c5b99..ddf9745df 100644 --- a/wrap/tests/fixtures/inheritance.i +++ b/wrap/tests/fixtures/inheritance.i @@ -22,3 +22,6 @@ virtual class MyTemplate : MyBase { static This Level(const T& K); }; + + +virtual class ForwardKinematicsFactor : gtsam::BetweenFactor {}; diff --git a/wrap/tests/test_interface_parser.py b/wrap/tests/test_interface_parser.py index 23f931fdd..d3c2ad52a 100644 --- a/wrap/tests/test_interface_parser.py +++ b/wrap/tests/test_interface_parser.py @@ -388,6 +388,15 @@ class TestInterfaceParser(unittest.TestCase): ret.parent_class.namespaces) self.assertTrue(ret.is_virtual) + ret = Class.rule.parseString( + "class ForwardKinematicsFactor : gtsam::BetweenFactor {};" + )[0] + self.assertEqual("ForwardKinematicsFactor", ret.name) + self.assertEqual("BetweenFactor", ret.parent_class.name) + self.assertEqual(["gtsam"], ret.parent_class.namespaces) + self.assertEqual("Pose3", ret.parent_class.instantiations[0].name) + self.assertEqual(["gtsam"], ret.parent_class.instantiations[0].namespaces) + def test_include(self): """Test for include statements.""" include = Include.rule.parseString(