From 956142d3be8beeae7d0ea6545d574a35ffbfa608 Mon Sep 17 00:00:00 2001 From: Zachary Wimer Date: Wed, 12 Oct 2022 16:33:02 -0700 Subject: [PATCH] fixing unary operators --- documentation/limitations.rst | 10 +--- source/function.cpp | 87 ++++++++++++++++++++--------------- test/T12.operator.hpp | 4 ++ test/T12.operator.ref | 5 +- 4 files changed, 60 insertions(+), 46 deletions(-) diff --git a/documentation/limitations.rst b/documentation/limitations.rst index f9125461..3b7d0401 100644 --- a/documentation/limitations.rst +++ b/documentation/limitations.rst @@ -74,14 +74,6 @@ The following operators will be ignored by binder: Miscellaneous ------------- -1. The pre/post increment operators both map to ``plus_plus`, with the pre-increment operator being invoked via ``a.plus_plus()`` and post-increment via ``.plus_plus(0)``; just as the operators are technically defined in C++. The same is true for the pre/post decrement operators, both called ``minus_minus``. +1. The pre/post increment operators both map to ``plus_plus``, with the pre-increment operator being invoked via ``a.plus_plus()`` and post-increment via ``.plus_plus(0)``; just as the operators are technically defined in C++. The same is true for the pre/post decrement operators, both called ``minus_minus``. 2. User defined literals ``operator"" _foo`` end up being named as ``operator_foo``. - ----------- -Known Bugs ----------- - -1. The unary ``operator+`` and unary ``operator-`` currently map to ``__add__`` and ``__sub__`` rather than ``__pos__`` and ``__neg__``. - -2. The unary ``operator*`` (dereference operator) will currently map to ``__mul__``. diff --git a/source/function.cpp b/source/function.cpp index 6d158a41..c23bb3ac 100644 --- a/source/function.cpp +++ b/source/function.cpp @@ -40,40 +40,53 @@ using namespace fmt::literals; namespace binder { -static std::map const cpp_python_operator_map{ - {"operator+", "__add__"}, // - {"operator-", "__sub__"}, // - {"operator*", "__mul__"}, // - {"operator/", "__truediv__"}, // - {"operator%", "__mod__"}, // - {"operator~", "__invert__"}, // - {"operator|", "__or__"}, // - {"operator&", "__and__"}, // - {"operator^", "__xor__"}, // - {"operator<<", "__lshift__"}, // - {"operator>>", "__rshift__"}, // - - {"operator+=", "__iadd__"}, // - {"operator-=", "__isub__"}, // - {"operator*=", "__imul__"}, // - {"operator/=", "__itruediv__"}, // - {"operator%=", "__imod__"}, // - {"operator|=", "__ior__"}, // - {"operator&=", "__iand__"}, // - {"operator^=", "__ixor__"}, // - {"operator<<=", "__ilshift__"}, // - {"operator>>=", "__irshift__"}, // - - {"operator()", "__call__"}, // - {"operator==", "__eq__"}, // - {"operator!=", "__ne__"}, // - {"operator[]", "__getitem__"}, // - {"operator=", "assign"}, // - {"operator++", "plus_plus"}, // - {"operator--", "minus_minus"}, // - - {"operator->", "arrow"}, // -}; +// Return the python operator that maps to the C++ operator; returns "" if no mapping exists +// This correctly handles operators that have multiple meanings depending on their argument count +// For example, operator_(this, other) maps to __sub__ while operator-(this) maps to __neg__ +string cpp_python_operator(const FunctionDecl & F) { + static std::map> const m { + {"operator+", {"__pos__", "__add__"}}, // + {"operator-", {"__neg__", "__sub__"}}, // + {"operator*", {"dereference", "__mul__"}}, // + {"operator/", {"__truediv__"}}, // + {"operator%", {"__mod__"}}, // + {"operator~", {"__invert__"}}, // + {"operator|", {"__or__"}}, // + {"operator&", {"__and__"}}, // + {"operator^", {"__xor__"}}, // + {"operator<<", {"__lshift__"}}, // + {"operator>>", {"__rshift__"}}, // + + {"operator+=", {"__iadd__"}}, // + {"operator-=", {"__isub__"}}, // + {"operator*=", {"__imul__"}}, // + {"operator/=", {"__itruediv__"}}, // + {"operator%=", {"__imod__"}}, // + {"operator|=", {"__ior__"}}, // + {"operator&=", {"__iand__"}}, // + {"operator^=", {"__ixor__"}}, // + {"operator<<=", {"__ilshift__"}}, // + {"operator>>=", {"__irshift__"}}, // + + {"operator()", {"__call__"}}, // + {"operator==", {"__eq__"}}, // + {"operator!=", {"__ne__"}}, // + {"operator[]", {"__getitem__"}}, // + {"operator=", {"assign"}}, // + {"operator++", {"plus_plus"}}, // + {"operator--", {"minus_minus"}}, // + + {"operator->", {"arrow"}} // + }; + const auto & found = m.find(F.getNameAsString()); + if (found != m.end()) { + const auto & vec { found->second }; + const auto n = F.getNumParams(); + return n < vec.size() ? vec[n] : vec.back(); + } + return {}; +} + // Generate function argument list separate by comma: int, bool, std::string string function_arguments(clang::FunctionDecl const *record) @@ -211,7 +224,9 @@ string template_specialization(FunctionDecl const *F) // generate string represetiong class name that could be used in python string python_function_name(FunctionDecl const *F) { - if( F->isOverloadedOperator() ) return cpp_python_operator_map.at(F->getNameAsString()); + if( F->isOverloadedOperator() ) { + return cpp_python_operator(*F); + } else { // if( auto m = dyn_cast(F) ) { // } @@ -481,7 +496,7 @@ bool is_bindable_raw(FunctionDecl const *F) if( F->isOverloadedOperator() ) { // outs() << "Operator: " << F->getNameAsString() << '\n'; - if( !isa(F) or !cpp_python_operator_map.count(F->getNameAsString()) ) return false; + if( !isa(F) or (cpp_python_operator(*F).size() == 0) ) return false; } r &= F->getTemplatedKind() != FunctionDecl::TK_FunctionTemplate /*and !F->isOverloadedOperator()*/ and !isa(F) and !F->isDeleted(); diff --git a/test/T12.operator.hpp b/test/T12.operator.hpp index 5d185b49..59fd4235 100644 --- a/test/T12.operator.hpp +++ b/test/T12.operator.hpp @@ -16,6 +16,10 @@ struct T { T &operator~() { return *this; } + T &operator+() { return *this; } + T &operator-() { return *this; } + T &operator*() { return *this; } + T &operator+(int) { return *this; } T &operator-(int) { return *this; } T &operator*(int) { return *this; } diff --git a/test/T12.operator.ref b/test/T12.operator.ref index 94a03923..0bba291c 100644 --- a/test/T12.operator.ref +++ b/test/T12.operator.ref @@ -19,6 +19,9 @@ void bind_T12_operator(std::function< pybind11::module &(std::string const &name pybind11::class_> cl(M(""), "T", ""); cl.def( pybind11::init( [](){ return new T(); } ) ); cl.def("__invert__", (struct T & (T::*)()) &T::operator~, "C++: T::operator~() --> struct T &", pybind11::return_value_policy::automatic); + cl.def("__pos__", (struct T & (T::*)()) &T::operator+, "C++: T::operator+() --> struct T &", pybind11::return_value_policy::automatic); + cl.def("__neg__", (struct T & (T::*)()) &T::operator-, "C++: T::operator-() --> struct T &", pybind11::return_value_policy::automatic); + cl.def("dereference", (struct T & (T::*)()) &T::operator*, "C++: T::operator*() --> struct T &", pybind11::return_value_policy::automatic); cl.def("__add__", (struct T & (T::*)(int)) &T::operator+, "C++: T::operator+(int) --> struct T &", pybind11::return_value_policy::automatic, pybind11::arg("")); cl.def("__sub__", (struct T & (T::*)(int)) &T::operator-, "C++: T::operator-(int) --> struct T &", pybind11::return_value_policy::automatic, pybind11::arg("")); cl.def("__mul__", (struct T & (T::*)(int)) &T::operator*, "C++: T::operator*(int) --> struct T &", pybind11::return_value_policy::automatic, pybind11::arg("")); @@ -100,4 +103,4 @@ PYBIND11_MODULE(T12_operator, root_module) { // T12_operator.cpp // Modules list file: TEST/T12_operator.modules -// +//