Merging 'master' into 'wrap'

release/4.3a0
Varun Agrawal 2024-01-16 15:04:09 -05:00
commit 3bc2d97447
12 changed files with 245 additions and 34 deletions

View File

@ -12,13 +12,14 @@ Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar, Varun Agrawal, and Frank Dellae
from typing import Any, Iterable, List, Union
from pyparsing import Literal, Optional, ZeroOrMore # type: ignore
from pyparsing import ZeroOrMore # type: ignore
from pyparsing import Literal, Optional, Word, alphas
from .enum import Enum
from .function import ArgumentList, ReturnType
from .template import Template
from .tokens import (CLASS, COLON, CONST, IDENT, LBRACE, LPAREN, OPERATOR,
RBRACE, RPAREN, SEMI_COLON, STATIC, VIRTUAL)
from .tokens import (CLASS, COLON, CONST, DUNDER, IDENT, LBRACE, LPAREN,
OPERATOR, RBRACE, RPAREN, SEMI_COLON, STATIC, VIRTUAL)
from .type import TemplatedType, Typename
from .utils import collect_namespaces
from .variable import Variable
@ -212,6 +213,26 @@ class Operator:
)
class DunderMethod:
"""Special Python double-underscore (dunder) methods, e.g. __iter__, __contains__"""
rule = (
DUNDER #
+ (Word(alphas))("name") #
+ DUNDER #
+ LPAREN #
+ ArgumentList.rule("args_list") #
+ RPAREN #
+ SEMI_COLON # BR
).setParseAction(lambda t: DunderMethod(t.name, t.args_list))
def __init__(self, name: str, args: ArgumentList):
self.name = name
self.args = args
def __repr__(self) -> str:
return f"DunderMethod: __{self.name}__({self.args})"
class Class:
"""
Rule to parse a class defined in the interface file.
@ -223,11 +244,13 @@ class Class:
};
```
"""
class Members:
"""
Rule for all the members within a class.
"""
rule = ZeroOrMore(Constructor.rule #
rule = ZeroOrMore(DunderMethod.rule #
^ Constructor.rule #
^ Method.rule #
^ StaticMethod.rule #
^ Variable.rule #
@ -235,11 +258,12 @@ class Class:
^ Enum.rule #
).setParseAction(lambda t: Class.Members(t.asList()))
def __init__(self,
members: List[Union[Constructor, Method, StaticMethod,
Variable, Operator]]):
def __init__(self, members: List[Union[Constructor, Method,
StaticMethod, Variable,
Operator, Enum, DunderMethod]]):
self.ctors = []
self.methods = []
self.dunder_methods = []
self.static_methods = []
self.properties = []
self.operators = []
@ -251,6 +275,8 @@ class Class:
self.methods.append(m)
elif isinstance(m, StaticMethod):
self.static_methods.append(m)
elif isinstance(m, DunderMethod):
self.dunder_methods.append(m)
elif isinstance(m, Variable):
self.properties.append(m)
elif isinstance(m, Operator):
@ -271,8 +297,8 @@ class Class:
+ SEMI_COLON # BR
).setParseAction(lambda t: Class(
t.template, t.is_virtual, t.name, t.parent_class, t.members.ctors, t.
members.methods, t.members.static_methods, t.members.properties, t.
members.operators, t.members.enums))
members.methods, t.members.static_methods, t.members.dunder_methods, t.
members.properties, t.members.operators, t.members.enums))
def __init__(
self,
@ -283,6 +309,7 @@ class Class:
ctors: List[Constructor],
methods: List[Method],
static_methods: List[StaticMethod],
dunder_methods: List[DunderMethod],
properties: List[Variable],
operators: List[Operator],
enums: List[Enum],
@ -308,6 +335,7 @@ class Class:
self.ctors = ctors
self.methods = methods
self.static_methods = static_methods
self.dunder_methods = dunder_methods
self.properties = properties
self.operators = operators
self.enums = enums
@ -326,6 +354,8 @@ class Class:
method.parent = self
for static_method in self.static_methods:
static_method.parent = self
for dunder_method in self.dunder_methods:
dunder_method.parent = self
for _property in self.properties:
_property.parent = self

View File

@ -82,7 +82,7 @@ class ArgumentList:
return ArgumentList([])
def __repr__(self) -> str:
return repr(tuple(self.args_list))
return ",".join([repr(x) for x in self.args_list])
def __len__(self) -> int:
return len(self.args_list)

View File

@ -22,6 +22,7 @@ RAW_POINTER, SHARED_POINTER, REF = map(Literal, "@*&")
LPAREN, RPAREN, LBRACE, RBRACE, COLON, SEMI_COLON = map(Suppress, "(){}:;")
LOPBRACK, ROPBRACK, COMMA, EQUAL = map(Suppress, "<>,=")
DUNDER = Suppress(Literal("__"))
# Default argument passed to functions/methods.
# Allow anything up to ',' or ';' except when they

View File

@ -45,6 +45,8 @@ class PybindWrapper:
'continue', 'global', 'pass'
]
self.dunder_methods = ('len', 'contains', 'iter')
# amount of indentation to add before each function/method declaration.
self.method_indent = '\n' + (' ' * 8)
@ -153,6 +155,51 @@ class PybindWrapper:
suffix=suffix)
return ret
def _wrap_dunder(self,
method,
cpp_class,
prefix,
suffix,
method_suffix=""):
"""
Wrap a Python double-underscore (dunder) method.
E.g. __len__() gets wrapped as `.def("__len__", [](gtsam::KeySet* self) {return self->size();})`
Supported methods are:
- __contains__(T x)
- __len__()
- __iter__()
"""
py_method = method.name + method_suffix
args_names = method.args.names()
py_args_names = self._py_args_names(method.args)
args_signature_with_names = self._method_args_signature(method.args)
if method.name == 'len':
function_call = "return std::distance(self->begin(), self->end());"
elif method.name == 'contains':
function_call = f"return std::find(self->begin(), self->end(), {method.args.args_list[0].name}) != self->end();"
elif method.name == 'iter':
function_call = "return py::make_iterator(self->begin(), self->end());"
ret = ('{prefix}.def("__{py_method}__",'
'[]({self}{opt_comma}{args_signature_with_names}){{'
'{function_call}'
'}}'
'{py_args_names}){suffix}'.format(
prefix=prefix,
py_method=py_method,
self=f"{cpp_class}* self",
opt_comma=', ' if args_names else '',
args_signature_with_names=args_signature_with_names,
function_call=function_call,
py_args_names=py_args_names,
suffix=suffix,
))
return ret
def _wrap_method(self,
method,
cpp_class,
@ -235,6 +282,20 @@ class PybindWrapper:
return ret
def wrap_dunder_methods(self,
methods,
cpp_class,
prefix='\n' + ' ' * 8,
suffix=''):
res = ""
for method in methods:
res += self._wrap_dunder(method=method,
cpp_class=cpp_class,
prefix=prefix,
suffix=suffix)
return res
def wrap_methods(self,
methods,
cpp_class,
@ -398,6 +459,7 @@ class PybindWrapper:
'{wrapped_ctors}'
'{wrapped_methods}'
'{wrapped_static_methods}'
'{wrapped_dunder_methods}'
'{wrapped_properties}'
'{wrapped_operators};\n'.format(
class_declaration=class_declaration,
@ -406,6 +468,8 @@ class PybindWrapper:
instantiated_class.methods, cpp_class),
wrapped_static_methods=self.wrap_methods(
instantiated_class.static_methods, cpp_class),
wrapped_dunder_methods=self.wrap_dunder_methods(
instantiated_class.dunder_methods, cpp_class),
wrapped_properties=self.wrap_properties(
instantiated_class.properties, cpp_class),
wrapped_operators=self.wrap_operators(

View File

@ -58,6 +58,8 @@ class InstantiatedClass(parser.Class):
# Instantiate all instance methods
self.methods = self.instantiate_methods(typenames)
self.dunder_methods = original.dunder_methods
super().__init__(
self.template,
self.is_virtual,
@ -66,6 +68,7 @@ class InstantiatedClass(parser.Class):
self.ctors,
self.methods,
self.static_methods,
self.dunder_methods,
self.properties,
self.operators,
self.enums,

View File

@ -1,2 +1,2 @@
pyparsing==2.4.7
pyparsing==3.1.1
pytest>=6.2.4

View File

@ -0,0 +1,36 @@
%class FastSet, see Doxygen page for details
%at https://gtsam.org/doxygen/
%
%-------Constructors-------
%FastSet()
%
classdef FastSet < handle
properties
ptr_FastSet = 0
end
methods
function obj = FastSet(varargin)
if nargin == 2 && isa(varargin{1}, 'uint64') && varargin{1} == uint64(5139824614673773682)
my_ptr = varargin{2};
class_wrapper(73, my_ptr);
elseif nargin == 0
my_ptr = class_wrapper(74);
else
error('Arguments do not match any overload of FastSet constructor');
end
obj.ptr_FastSet = my_ptr;
end
function delete(obj)
class_wrapper(75, obj.ptr_FastSet);
end
function display(obj), obj.print(''); end
%DISPLAY Calls print on the object
function disp(obj), obj.display; end
%DISP Calls print on the object
end
methods(Static = true)
end
end

View File

@ -15,9 +15,9 @@ classdef MyFactorPosePoint2 < handle
function obj = MyFactorPosePoint2(varargin)
if nargin == 2 && isa(varargin{1}, 'uint64') && varargin{1} == uint64(5139824614673773682)
my_ptr = varargin{2};
class_wrapper(73, my_ptr);
class_wrapper(76, my_ptr);
elseif nargin == 4 && isa(varargin{1},'numeric') && isa(varargin{2},'numeric') && isa(varargin{3},'double') && isa(varargin{4},'gtsam.noiseModel.Base')
my_ptr = class_wrapper(74, varargin{1}, varargin{2}, varargin{3}, varargin{4});
my_ptr = class_wrapper(77, varargin{1}, varargin{2}, varargin{3}, varargin{4});
else
error('Arguments do not match any overload of MyFactorPosePoint2 constructor');
end
@ -25,7 +25,7 @@ classdef MyFactorPosePoint2 < handle
end
function delete(obj)
class_wrapper(75, obj.ptr_MyFactorPosePoint2);
class_wrapper(78, obj.ptr_MyFactorPosePoint2);
end
function display(obj), obj.print(''); end
@ -36,19 +36,19 @@ classdef MyFactorPosePoint2 < handle
% PRINT usage: print(string s, KeyFormatter keyFormatter) : returns void
% Doxygen can be found at https://gtsam.org/doxygen/
if length(varargin) == 2 && isa(varargin{1},'char') && isa(varargin{2},'gtsam.KeyFormatter')
class_wrapper(76, this, varargin{:});
class_wrapper(79, this, varargin{:});
return
end
% PRINT usage: print(string s) : returns void
% Doxygen can be found at https://gtsam.org/doxygen/
if length(varargin) == 1 && isa(varargin{1},'char')
class_wrapper(77, this, varargin{:});
class_wrapper(80, this, varargin{:});
return
end
% PRINT usage: print() : returns void
% Doxygen can be found at https://gtsam.org/doxygen/
if length(varargin) == 0
class_wrapper(78, this, varargin{:});
class_wrapper(81, this, varargin{:});
return
end
error('Arguments do not match any overload of function MyFactorPosePoint2.print');

View File

@ -31,6 +31,8 @@ typedef std::set<std::shared_ptr<ForwardKinematics>*> Collector_ForwardKinematic
static Collector_ForwardKinematics collector_ForwardKinematics;
typedef std::set<std::shared_ptr<TemplatedConstructor>*> Collector_TemplatedConstructor;
static Collector_TemplatedConstructor collector_TemplatedConstructor;
typedef std::set<std::shared_ptr<FastSet>*> Collector_FastSet;
static Collector_FastSet collector_FastSet;
typedef std::set<std::shared_ptr<MyFactorPosePoint2>*> Collector_MyFactorPosePoint2;
static Collector_MyFactorPosePoint2 collector_MyFactorPosePoint2;
@ -101,6 +103,12 @@ void _deleteAllObjects()
collector_TemplatedConstructor.erase(iter++);
anyDeleted = true;
} }
{ for(Collector_FastSet::iterator iter = collector_FastSet.begin();
iter != collector_FastSet.end(); ) {
delete *iter;
collector_FastSet.erase(iter++);
anyDeleted = true;
} }
{ for(Collector_MyFactorPosePoint2::iterator iter = collector_MyFactorPosePoint2.begin();
iter != collector_MyFactorPosePoint2.end(); ) {
delete *iter;
@ -844,7 +852,40 @@ void TemplatedConstructor_deconstructor_72(int nargout, mxArray *out[], int narg
delete self;
}
void MyFactorPosePoint2_collectorInsertAndMakeBase_73(int nargout, mxArray *out[], int nargin, const mxArray *in[])
void FastSet_collectorInsertAndMakeBase_73(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
mexAtExit(&_deleteAllObjects);
typedef std::shared_ptr<FastSet> Shared;
Shared *self = *reinterpret_cast<Shared**> (mxGetData(in[0]));
collector_FastSet.insert(self);
}
void FastSet_constructor_74(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
mexAtExit(&_deleteAllObjects);
typedef std::shared_ptr<FastSet> Shared;
Shared *self = new Shared(new FastSet());
collector_FastSet.insert(self);
out[0] = mxCreateNumericMatrix(1, 1, mxUINT32OR64_CLASS, mxREAL);
*reinterpret_cast<Shared**> (mxGetData(out[0])) = self;
}
void FastSet_deconstructor_75(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
typedef std::shared_ptr<FastSet> Shared;
checkArguments("delete_FastSet",nargout,nargin,1);
Shared *self = *reinterpret_cast<Shared**>(mxGetData(in[0]));
Collector_FastSet::iterator item;
item = collector_FastSet.find(self);
if(item != collector_FastSet.end()) {
collector_FastSet.erase(item);
}
delete self;
}
void MyFactorPosePoint2_collectorInsertAndMakeBase_76(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
mexAtExit(&_deleteAllObjects);
typedef std::shared_ptr<MyFactor<gtsam::Pose2, gtsam::Matrix>> Shared;
@ -853,7 +894,7 @@ void MyFactorPosePoint2_collectorInsertAndMakeBase_73(int nargout, mxArray *out[
collector_MyFactorPosePoint2.insert(self);
}
void MyFactorPosePoint2_constructor_74(int nargout, mxArray *out[], int nargin, const mxArray *in[])
void MyFactorPosePoint2_constructor_77(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
mexAtExit(&_deleteAllObjects);
typedef std::shared_ptr<MyFactor<gtsam::Pose2, gtsam::Matrix>> Shared;
@ -868,7 +909,7 @@ void MyFactorPosePoint2_constructor_74(int nargout, mxArray *out[], int nargin,
*reinterpret_cast<Shared**> (mxGetData(out[0])) = self;
}
void MyFactorPosePoint2_deconstructor_75(int nargout, mxArray *out[], int nargin, const mxArray *in[])
void MyFactorPosePoint2_deconstructor_78(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
typedef std::shared_ptr<MyFactor<gtsam::Pose2, gtsam::Matrix>> Shared;
checkArguments("delete_MyFactorPosePoint2",nargout,nargin,1);
@ -881,7 +922,7 @@ void MyFactorPosePoint2_deconstructor_75(int nargout, mxArray *out[], int nargin
delete self;
}
void MyFactorPosePoint2_print_76(int nargout, mxArray *out[], int nargin, const mxArray *in[])
void MyFactorPosePoint2_print_79(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
checkArguments("print",nargout,nargin-1,2);
auto obj = unwrap_shared_ptr<MyFactor<gtsam::Pose2, gtsam::Matrix>>(in[0], "ptr_MyFactorPosePoint2");
@ -890,7 +931,7 @@ void MyFactorPosePoint2_print_76(int nargout, mxArray *out[], int nargin, const
obj->print(s,keyFormatter);
}
void MyFactorPosePoint2_print_77(int nargout, mxArray *out[], int nargin, const mxArray *in[])
void MyFactorPosePoint2_print_80(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
checkArguments("print",nargout,nargin-1,1);
auto obj = unwrap_shared_ptr<MyFactor<gtsam::Pose2, gtsam::Matrix>>(in[0], "ptr_MyFactorPosePoint2");
@ -898,7 +939,7 @@ void MyFactorPosePoint2_print_77(int nargout, mxArray *out[], int nargin, const
obj->print(s,gtsam::DefaultKeyFormatter);
}
void MyFactorPosePoint2_print_78(int nargout, mxArray *out[], int nargin, const mxArray *in[])
void MyFactorPosePoint2_print_81(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
checkArguments("print",nargout,nargin-1,0);
auto obj = unwrap_shared_ptr<MyFactor<gtsam::Pose2, gtsam::Matrix>>(in[0], "ptr_MyFactorPosePoint2");
@ -1137,22 +1178,31 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[])
TemplatedConstructor_deconstructor_72(nargout, out, nargin-1, in+1);
break;
case 73:
MyFactorPosePoint2_collectorInsertAndMakeBase_73(nargout, out, nargin-1, in+1);
FastSet_collectorInsertAndMakeBase_73(nargout, out, nargin-1, in+1);
break;
case 74:
MyFactorPosePoint2_constructor_74(nargout, out, nargin-1, in+1);
FastSet_constructor_74(nargout, out, nargin-1, in+1);
break;
case 75:
MyFactorPosePoint2_deconstructor_75(nargout, out, nargin-1, in+1);
FastSet_deconstructor_75(nargout, out, nargin-1, in+1);
break;
case 76:
MyFactorPosePoint2_print_76(nargout, out, nargin-1, in+1);
MyFactorPosePoint2_collectorInsertAndMakeBase_76(nargout, out, nargin-1, in+1);
break;
case 77:
MyFactorPosePoint2_print_77(nargout, out, nargin-1, in+1);
MyFactorPosePoint2_constructor_77(nargout, out, nargin-1, in+1);
break;
case 78:
MyFactorPosePoint2_print_78(nargout, out, nargin-1, in+1);
MyFactorPosePoint2_deconstructor_78(nargout, out, nargin-1, in+1);
break;
case 79:
MyFactorPosePoint2_print_79(nargout, out, nargin-1, in+1);
break;
case 80:
MyFactorPosePoint2_print_80(nargout, out, nargin-1, in+1);
break;
case 81:
MyFactorPosePoint2_print_81(nargout, out, nargin-1, in+1);
break;
}
} catch(const std::exception& e) {

View File

@ -91,6 +91,12 @@ PYBIND11_MODULE(class_py, m_) {
.def(py::init<const int&>(), py::arg("arg"))
.def(py::init<const double&>(), py::arg("arg"));
py::class_<FastSet, std::shared_ptr<FastSet>>(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("__iter__",[](FastSet* self){return py::make_iterator(self->begin(), self->end());});
py::class_<MyFactor<gtsam::Pose2, gtsam::Matrix>, std::shared_ptr<MyFactor<gtsam::Pose2, gtsam::Matrix>>>(m_, "MyFactorPosePoint2")
.def(py::init<size_t, size_t, double, const std::shared_ptr<gtsam::noiseModel::Base>>(), py::arg("key1"), py::arg("key2"), py::arg("measured"), py::arg("noiseModel"))
.def("print",[](MyFactor<gtsam::Pose2, gtsam::Matrix>* 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)

View File

@ -145,3 +145,12 @@ class TemplatedConstructor {
class SuperCoolFactor;
typedef SuperCoolFactor<gtsam::Pose3> SuperCoolFactorPose3;
/// @brief class with dunder methods for container behavior
class FastSet {
FastSet();
__len__();
__contains__(size_t key);
__iter__();
};

View File

@ -18,11 +18,12 @@ import unittest
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from gtwrap.interface_parser import (ArgumentList, Class, Constructor, Enum,
Enumerator, ForwardDeclaration,
GlobalFunction, Include, Method, Module,
Namespace, Operator, ReturnType,
StaticMethod, TemplatedType, Type,
from gtwrap.interface_parser import (ArgumentList, Class, Constructor,
DunderMethod, Enum, Enumerator,
ForwardDeclaration, GlobalFunction,
Include, Method, Module, Namespace,
Operator, ReturnType, StaticMethod,
TemplatedType, Type,
TypedefTemplateInstantiation, Typename,
Variable)
from gtwrap.template_instantiator.classes import InstantiatedClass
@ -344,6 +345,17 @@ class TestInterfaceParser(unittest.TestCase):
self.assertEqual(1, len(ret.args))
self.assertEqual("const T & name", ret.args.args_list[0].to_cpp())
def test_dunder_method(self):
"""Test for special python dunder methods."""
iter_string = "__iter__();"
ret = DunderMethod.rule.parse_string(iter_string)[0]
self.assertEqual("iter", ret.name)
contains_string = "__contains__(size_t key);"
ret = DunderMethod.rule.parse_string(contains_string)[0]
self.assertEqual("contains", ret.name)
self.assertTrue(len(ret.args) == 1)
def test_operator_overload(self):
"""Test for operator overloading."""
# Unary operator