Commit 7f5608b9 authored by da_woods's avatar da_woods

Allow nonmember operators in c++ classes

It's sometimes useful to have nonmember operators in c++
e.g. C operator+(int, const C&)
(So int can come first)

These could be defined outside the "cdef cppclass" section
but if they were in a pxd file they weren't easy to import
and use.

They can now be defined with the "cdef cppclass" and will
be found and used appropriately
e.g.

cdef cppclass C:
  C operator+(int, const C&)

Appropriate tests have been added
parent e61da2eb
...@@ -10124,9 +10124,8 @@ class CBinopNode(BinopNode): ...@@ -10124,9 +10124,8 @@ class CBinopNode(BinopNode):
cpp_type = None cpp_type = None
if type1.is_cpp_class or type1.is_ptr: if type1.is_cpp_class or type1.is_ptr:
cpp_type = type1.find_cpp_operation_type(self.operator, type2) cpp_type = type1.find_cpp_operation_type(self.operator, type2)
# FIXME: handle the reversed case? if cpp_type is None and (type2.is_cpp_class or type2.is_ptr):
#if cpp_type is None and (type2.is_cpp_class or type2.is_ptr): cpp_type = type2.find_cpp_operation_type(self.operator, type1)
# cpp_type = type2.find_cpp_operation_type(self.operator, type1)
# FIXME: do we need to handle other cases here? # FIXME: do we need to handle other cases here?
return cpp_type return cpp_type
......
...@@ -832,9 +832,25 @@ class Scope(object): ...@@ -832,9 +832,25 @@ class Scope(object):
if res is not None: if res is not None:
return res return res
function = self.lookup("operator%s" % operator) function = self.lookup("operator%s" % operator)
if function is None: function_alternatives = []
if function is not None:
function_alternatives = function.all_alternatives()
# look-up nonmember methods listed within a class
method_alternatives = []
if (len(operands)==2 # binary operators only
and operands[1].type.is_cpp_class):
obj_type = operands[1].type
method = obj_type.scope.lookup("operator%s" % operator)
if method is not None:
# also allow lookup with things defined in the global scope
method_alternatives = method.all_alternatives()
if (not method_alternatives) and (not function_alternatives):
return None return None
return PyrexTypes.best_match(operands, function.all_alternatives())
return PyrexTypes.best_match(operands,
method_alternatives+function_alternatives)
def lookup_operator_for_types(self, pos, operator, types): def lookup_operator_for_types(self, pos, operator, types):
from .Nodes import Node from .Nodes import Node
......
...@@ -27,18 +27,29 @@ cdef extern from "cpp_operators_helper.h": ...@@ -27,18 +27,29 @@ cdef extern from "cpp_operators_helper.h":
const_char* operator--(int) const_char* operator--(int)
const_char* operator+(int) const_char* operator+(int)
const_char* operator+(int,const TestOps&)
const_char* operator-(int) const_char* operator-(int)
const_char* operator-(int,const TestOps&)
const_char* operator*(int) const_char* operator*(int)
# deliberately omitted operator* to test case where only defined outside class
const_char* operator/(int) const_char* operator/(int)
const_char* operator/(int,const TestOps&)
const_char* operator%(int) const_char* operator%(int)
const_char* operator%(int,const TestOps&)
const_char* operator|(int) const_char* operator|(int)
const_char* operator|(int,const TestOps&)
const_char* operator&(int) const_char* operator&(int)
const_char* operator&(int,const TestOps&)
const_char* operator^(int) const_char* operator^(int)
const_char* operator^(int,const TestOps&)
const_char* operator,(int) const_char* operator,(int)
const_char* operator,(int,const TestOps&)
const_char* operator<<(int) const_char* operator<<(int)
const_char* operator<<(int,const TestOps&)
const_char* operator>>(int) const_char* operator>>(int)
const_char* operator>>(int,const TestOps&)
const_char* operator==(int) const_char* operator==(int)
const_char* operator!=(int) const_char* operator!=(int)
...@@ -50,6 +61,23 @@ cdef extern from "cpp_operators_helper.h": ...@@ -50,6 +61,23 @@ cdef extern from "cpp_operators_helper.h":
const_char* operator[](int) const_char* operator[](int)
const_char* operator()(int) const_char* operator()(int)
# Defining the operator outside the class does work
# but doesn't help when importing from pxd files
# (they don't get imported)
const_char* operator+(float,const TestOps&)
# deliberately omitted operator- to test case where only defined in class
const_char* operator*(float,const TestOps&)
const_char* operator/(float,const TestOps&)
const_char* operator%(float,const TestOps&)
const_char* operator|(float,const TestOps&)
const_char* operator&(float,const TestOps&)
const_char* operator^(float,const TestOps&)
const_char* operator,(float,const TestOps&)
const_char* operator<<(float,const TestOps&)
const_char* operator>>(float,const TestOps&)
cppclass TruthClass: cppclass TruthClass:
TruthClass() TruthClass()
TruthClass(bool) TruthClass(bool)
...@@ -126,6 +154,63 @@ def test_binop(): ...@@ -126,6 +154,63 @@ def test_binop():
out(x, typeof(x)) out(x, typeof(x))
del t del t
def test_nonmember_binop():
"""
>>> test_nonmember_binop()
nonmember binary + [const_char *]
nonmember binary - [const_char *]
nonmember binary / [const_char *]
nonmember binary % [const_char *]
nonmember binary & [const_char *]
nonmember binary | [const_char *]
nonmember binary ^ [const_char *]
nonmember binary << [const_char *]
nonmember binary >> [const_char *]
nonmember binary COMMA [const_char *]
nonmember binary2 + [const_char *]
nonmember binary2 * [const_char *]
nonmember binary2 / [const_char *]
nonmember binary2 % [const_char *]
nonmember binary2 & [const_char *]
nonmember binary2 | [const_char *]
nonmember binary2 ^ [const_char *]
nonmember binary2 << [const_char *]
nonmember binary2 >> [const_char *]
nonmember binary2 COMMA [const_char *]
"""
cdef TestOps* t = new TestOps()
out(1 + t[0], typeof(1 + t[0]))
out(1 - t[0], typeof(1 - t[0]))
# * deliberately omitted
out(1 / t[0], typeof(1 / t[0]))
out(1 % t[0], typeof(1 % t[0]))
out(1 & t[0], typeof(1 & t[0]))
out(1 | t[0], typeof(1 | t[0]))
out(1 ^ t[0], typeof(1 ^ t[0]))
out(1 << t[0], typeof(1 << t[0]))
out(1 >> t[0], typeof(1 >> t[0]))
x = cython.operator.comma(1, t[0])
out(x, typeof(x))
# now test float operators defined outside class
out(1. + t[0], typeof(1. + t[0]))
# operator - deliberately omitted
out(1. * t[0], typeof(1. * t[0]))
out(1. / t[0], typeof(1. / t[0]))
out(1. % t[0], typeof(1. % t[0]))
out(1. & t[0], typeof(1. & t[0]))
out(1. | t[0], typeof(1. | t[0]))
out(1. ^ t[0], typeof(1. ^ t[0]))
out(1. << t[0], typeof(1. << t[0]))
out(1. >> t[0], typeof(1. >> t[0]))
# for some reason we need a cdef here - not sure this is quite right
y = cython.operator.comma(1., t[0])
out(y, typeof(y))
del t
def test_cmp(): def test_cmp():
""" """
>>> test_cmp() >>> test_cmp()
......
#define UN_OP(op) const char* operator op () { return "unary "#op; } #define UN_OP(op) const char* operator op () { return "unary "#op; }
#define POST_UN_OP(op) const char* operator op (int x) { x++; return "post "#op; } #define POST_UN_OP(op) const char* operator op (int x) { x++; return "post "#op; }
#define BIN_OP(op) const char* operator op (int x) { x++; return "binary "#op; } #define BIN_OP(op) const char* operator op (int x) { x++; return "binary "#op; }
#define NONMEMBER_BIN_OP(op) const char* operator op (int x, const TestOps&) { x++; return "nonmember binary "#op; }
#define NONMEMBER_BIN_OP2(op) const char* operator op (double x, const TestOps&) { x++; return "nonmember binary2 "#op; }
#define COMMA , #define COMMA ,
...@@ -46,6 +48,34 @@ public: ...@@ -46,6 +48,34 @@ public:
}; };
NONMEMBER_BIN_OP(+)
NONMEMBER_BIN_OP(-)
NONMEMBER_BIN_OP(*)
NONMEMBER_BIN_OP(/)
NONMEMBER_BIN_OP(%)
NONMEMBER_BIN_OP(<<)
NONMEMBER_BIN_OP(>>)
NONMEMBER_BIN_OP(|)
NONMEMBER_BIN_OP(&)
NONMEMBER_BIN_OP(^)
NONMEMBER_BIN_OP(COMMA)
NONMEMBER_BIN_OP2(+)
NONMEMBER_BIN_OP2(-)
NONMEMBER_BIN_OP2(*)
NONMEMBER_BIN_OP2(/)
NONMEMBER_BIN_OP2(%)
NONMEMBER_BIN_OP2(<<)
NONMEMBER_BIN_OP2(>>)
NONMEMBER_BIN_OP2(|)
NONMEMBER_BIN_OP2(&)
NONMEMBER_BIN_OP2(^)
NONMEMBER_BIN_OP2(COMMA)
class TruthClass { class TruthClass {
public: public:
TruthClass() : value(false) {} TruthClass() : value(false) {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment