Skip to content

Commit 4e3d9fe

Browse files
EricCousineau-TRIwjakob
authored andcommitted
operators: Explicitly expose py::hash(py::self)
Add warnings about extending STL
1 parent 5309573 commit 4e3d9fe

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

include/pybind11/operators.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
147147
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
148148
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
149149
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
150+
// WARNING: This usage of `abs` should only be done for existing STL overloads.
151+
// Adding overloads directly in to the `std::` namespace is advised against:
152+
// https://en.cppreference.com/w/cpp/language/extending_std
150153
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
151154
PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
152155
PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
@@ -160,6 +163,8 @@ PYBIND11_UNARY_OPERATOR(float, float_, (double) l)
160163
NAMESPACE_END(detail)
161164

162165
using detail::self;
166+
// Add named operators so that they are accessible via `py::`.
167+
using detail::hash;
163168

164169
NAMESPACE_END(PYBIND11_NAMESPACE)
165170

tests/test_operator_overloading.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ class Vector2 {
4343
friend Vector2 operator-(float f, const Vector2 &v) { return Vector2(f - v.x, f - v.y); }
4444
friend Vector2 operator*(float f, const Vector2 &v) { return Vector2(f * v.x, f * v.y); }
4545
friend Vector2 operator/(float f, const Vector2 &v) { return Vector2(f / v.x, f / v.y); }
46+
47+
bool operator==(const Vector2 &v) const {
48+
return x == v.x && y == v.y;
49+
}
50+
bool operator!=(const Vector2 &v) const {
51+
return x != v.x || y != v.y;
52+
}
4653
private:
4754
float x, y;
4855
};
@@ -55,6 +62,11 @@ int operator+(const C2 &, const C2 &) { return 22; }
5562
int operator+(const C2 &, const C1 &) { return 21; }
5663
int operator+(const C1 &, const C2 &) { return 12; }
5764

65+
// Note: Specializing explicit within `namespace std { ... }` is done due to a
66+
// bug in GCC<7. If you are supporting compilers later than this, consider
67+
// specializing `using template<> struct std::hash<...>` in the global
68+
// namespace instead, per this recommendation:
69+
// https://en.cppreference.com/w/cpp/language/extending_std#Adding_template_specializations
5870
namespace std {
5971
template<>
6072
struct hash<Vector2> {
@@ -63,6 +75,11 @@ namespace std {
6375
};
6476
}
6577

78+
// Not a good abs function, but easy to test.
79+
std::string abs(const Vector2&) {
80+
return "abs(Vector2)";
81+
}
82+
6683
// MSVC warns about unknown pragmas, and warnings are errors.
6784
#ifndef _MSC_VER
6885
#pragma GCC diagnostic push
@@ -107,7 +124,13 @@ TEST_SUBMODULE(operators, m) {
107124
.def(float() / py::self)
108125
.def(-py::self)
109126
.def("__str__", &Vector2::toString)
110-
.def(hash(py::self))
127+
.def("__repr__", &Vector2::toString)
128+
.def(py::self == py::self)
129+
.def(py::self != py::self)
130+
.def(py::hash(py::self))
131+
// N.B. See warning about usage of `py::detail::abs(py::self)` in
132+
// `operators.h`.
133+
.def("__abs__", [](const Vector2& v) { return abs(v); })
111134
;
112135

113136
m.attr("Vector") = m.attr("Vector2");

tests/test_operator_overloading.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
def test_operator_overloading():
77
v1 = m.Vector2(1, 2)
88
v2 = m.Vector(3, -1)
9+
v3 = m.Vector2(1, 2) # Same value as v1, but different instance.
10+
assert v1 is not v3
11+
912
assert str(v1) == "[1.000000, 2.000000]"
1013
assert str(v2) == "[3.000000, -1.000000]"
1114

@@ -24,7 +27,11 @@ def test_operator_overloading():
2427
assert str(v1 * v2) == "[3.000000, -2.000000]"
2528
assert str(v2 / v1) == "[3.000000, -0.500000]"
2629

30+
assert v1 == v3
31+
assert v1 != v2
2732
assert hash(v1) == 4
33+
# TODO(eric.cousineau): Make this work.
34+
# assert abs(v1) == "abs(Vector2)"
2835

2936
v1 += 2 * v2
3037
assert str(v1) == "[7.000000, 0.000000]"
@@ -40,14 +47,17 @@ def test_operator_overloading():
4047
assert str(v2) == "[2.000000, 8.000000]"
4148

4249
cstats = ConstructorStats.get(m.Vector2)
43-
assert cstats.alive() == 2
50+
assert cstats.alive() == 3
4451
del v1
45-
assert cstats.alive() == 1
52+
assert cstats.alive() == 2
4653
del v2
54+
assert cstats.alive() == 1
55+
del v3
4756
assert cstats.alive() == 0
4857
assert cstats.values() == [
4958
'[1.000000, 2.000000]',
5059
'[3.000000, -1.000000]',
60+
'[1.000000, 2.000000]',
5161
'[-3.000000, 1.000000]',
5262
'[4.000000, 1.000000]',
5363
'[-2.000000, 3.000000]',

0 commit comments

Comments
 (0)