Skip to content

Commit

Permalink
Added 'bind_set(...)' to std_binds
Browse files Browse the repository at this point in the history
Based off github.com/beatmax's solution.
  • Loading branch information
bradendubois committed Jan 8, 2025
1 parent a09cf61 commit 9680e85
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 0 deletions.
76 changes: 76 additions & 0 deletions include/pybind11/stl_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,82 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
return cl;
}

//
// std::set
//
template <typename Set, typename holder_type = std::unique_ptr<Set>, typename... Args>
class_<Set, holder_type> bind_set(handle scope, std::string const &name, Args &&...args) {
using Class_ = class_<Set, holder_type>;
using T = typename Set::value_type;
using ItType = typename Set::iterator;

auto vtype_info = detail::get_type_info(typeid(T));
bool local = !vtype_info || vtype_info->module_local;

Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
cl.def(init<>());
cl.def(init<const Set &>(), "Copy constructor");
cl.def(init([](iterable it) {
auto s = std::unique_ptr<Set>(new Set());
for (handle h : it)
s->insert(h.cast<T>());
return s.release();
}));
cl.def(self == self);
cl.def(self != self);
cl.def(
"remove",
[](Set &s, const T &x) {
auto p = s.find(x);
if (p != s.end())
s.erase(p);
else
throw value_error();
},
arg("x"),
"Remove the item from the set whose value is x. "
"It is an error if there is no such item.");
cl.def(
"__contains__",
[](const Set &s, const T &x) { return s.find(x) != s.end(); },
arg("x"),
"Return true if the container contains ``x``.");
cl.def(
"add",
[](Set &s, const T &value) { s.insert(value); },
arg("x"),
"Add an item to the set.");
cl.def("clear", [](Set &s) { s.clear(); }, "Clear the contents.");
cl.def(
"__iter__",
[](Set &s) {
return make_iterator<return_value_policy::copy, ItType, ItType, T>(s.begin(), s.end());
},
keep_alive<0, 1>() /* Essential: keep set alive while iterator exists */
);
cl.def(
"__repr__",
[name](Set &s) {
std::ostringstream os;
os << name << '{';
for (auto it = s.begin(); it != s.end(); ++it) {
if (it != s.begin())
os << ", ";
os << *it;
}
os << '}';
return os.str();
},
"Return the canonical string representation of this set.");
cl.def(
"__bool__",
[](const Set &s) -> bool { return !s.empty(); },
"Check whether the set is nonempty");
cl.def("__len__", &Set::size);

return cl;
}

//
// std::map, std::unordered_map
//
Expand Down
4 changes: 4 additions & 0 deletions tests/test_stl_binders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <deque>
#include <map>
#include <set>
#include <unordered_map>
#include <vector>

Expand Down Expand Up @@ -183,6 +184,9 @@ TEST_SUBMODULE(stl_binders, m) {
py::bind_vector<std::vector<El>>(m, "VectorEl");
py::bind_vector<std::vector<std::vector<El>>>(m, "VectorVectorEl");

// test_set_int
py::bind_set<std::set<int>>(m, "SetInt");

// test_map_string_double
py::bind_map<std::map<std::string, double>>(m, "MapStringDouble");
py::bind_map<std::unordered_map<std::string, double>>(m, "UnorderedMapStringDouble");
Expand Down
23 changes: 23 additions & 0 deletions tests/test_stl_binders.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,29 @@ def test_vector_custom():
assert str(vv_b) == "VectorEl[El{1}, El{2}]"


def test_set_int():
s_a = m.SetInt()
s_b = m.SetInt()

assert len(s_a) == 0
assert s_a == s_b

s_a.add(1)

assert 1 in s_a
assert str(s_a) == "SetInt{1}"
assert s_a != s_b

for i in range(5):
s_a.add(i)

assert sorted(s_a) == [0, 1, 2, 3, 4]

s_a.clear()
assert len(s_a) == 0
assert str(s_a) == "SetInt{}"


def test_map_string_double():
mm = m.MapStringDouble()
mm["a"] = 1
Expand Down

0 comments on commit 9680e85

Please sign in to comment.