Skip to content

Commit 08819b4

Browse files
committed
Fix assertion failure for unions (#1685)
In def_readonly and def_readwrite, there is an assertion that the member comes from the class or a base class: static_assert(std::is_base_of<C, type>::value, "..."); However, if C and type are the same type, is_base_of will still only be true if they are the same _non-union_ type. This means we can't define accessors for the members of a union type because of this assertion. Update the assertion to test std::is_same<C, type>::value || std::is_base_of<C, type>::value which will allow union types, or members of base classes. Also add a basic unit test for accessing unions.
1 parent 25abf7e commit 08819b4

File tree

4 files changed

+33
-2
lines changed

4 files changed

+33
-2
lines changed

include/pybind11/pybind11.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -1176,7 +1176,7 @@ class class_ : public detail::generic_type {
11761176

11771177
template <typename C, typename D, typename... Extra>
11781178
class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) {
1179-
static_assert(std::is_base_of<C, type>::value, "def_readwrite() requires a class member (or base class member)");
1179+
static_assert(std::is_same<C, type>::value || std::is_base_of<C, type>::value, "def_readwrite() requires a class member (or base class member)");
11801180
cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)),
11811181
fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this));
11821182
def_property(name, fget, fset, return_value_policy::reference_internal, extra...);
@@ -1185,7 +1185,7 @@ class class_ : public detail::generic_type {
11851185

11861186
template <typename C, typename D, typename... Extra>
11871187
class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) {
1188-
static_assert(std::is_base_of<C, type>::value, "def_readonly() requires a class member (or base class member)");
1188+
static_assert(std::is_same<C, type>::value || std::is_base_of<C, type>::value, "def_readonly() requires a class member (or base class member)");
11891189
cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this));
11901190
def_property_readonly(name, fget, return_value_policy::reference_internal, extra...);
11911191
return *this;

tests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ set(PYBIND11_TEST_FILES
5959
test_stl.cpp
6060
test_stl_binders.cpp
6161
test_tagbased_polymorphic.cpp
62+
test_union.cpp
6263
test_virtual_functions.cpp
6364
)
6465

tests/test_union.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
tests/test_class.cpp -- test py::class_ definitions and basic functionality
3+
4+
Copyright (c) 2019 Roland Dreier <[email protected]>
5+
6+
All rights reserved. Use of this source code is governed by a
7+
BSD-style license that can be found in the LICENSE file.
8+
*/
9+
10+
#include "pybind11_tests.h"
11+
12+
TEST_SUBMODULE(union_, m) {
13+
union TestUnion {
14+
int value_int;
15+
unsigned value_uint;
16+
};
17+
18+
py::class_<TestUnion>(m, "TestUnion")
19+
.def(py::init<>())
20+
.def_readonly("as_int", &TestUnion::value_int)
21+
.def_readwrite("as_uint", &TestUnion::value_uint);
22+
}

tests/test_union.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from pybind11_tests import union_ as m
2+
3+
4+
def test_union():
5+
instance = m.TestUnion()
6+
7+
instance.as_uint = 10
8+
assert instance.as_int == 10

0 commit comments

Comments
 (0)