Skip to content

Commit 9182490

Browse files
author
gerrymanoim
authored
MAINT: Move sparsehash code to library_wrappers/sparsehash (#174)
* MAINT: Move sparsehash code to library_wrappers/sparsehash * TST: Move sequence and mapper to object tests * ENH: Pure wrappers for dense/sparse_hash_map/set.
1 parent 3f501bb commit 9182490

File tree

9 files changed

+249
-190
lines changed

9 files changed

+249
-190
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,10 @@ GTEST_SRCS := $(wildcard $(GTEST_DIR)/src/*.cc) \
150150
$(wildcard $(GTEST_DIR)/src/*.h) $(GTEST_HEADERS)
151151
GTEST_FILTER ?= '*'
152152

153-
TEST_SOURCES := $(wildcard tests/*.cc)
153+
TEST_SOURCES := $(wildcard tests/*.cc) $(wildcard tests/library_wrappers/*.cc)
154154
TEST_DFILES := $(TEST_SOURCES:.cc=.d)
155155
TEST_OBJECTS := $(TEST_SOURCES:.cc=.o)
156-
TEST_HEADERS := $(wildcard tests/*.h) $(GTEST_HEADERS)
156+
TEST_HEADERS := $(wildcard tests/*.h) $(wildcard tests/library_wrappers/*.h) $(GTEST_HEADERS)
157157
TEST_INCLUDE := -I tests -I $(GTEST_DIR)/include
158158
TEST_MODULE := tests/_runner$(SO_SUFFIX)
159159
PYTHON_TESTS := $(wildcard tests/*.py)

README.rst

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,13 @@ libpy requires:
2828
- gcc>=8 or clang>=10
2929
- numpy>=1.11.3
3030

31-
libpy also depends on the following system packages:
31+
Optional Requirements
32+
---------------------
3233

33-
- google sparsehash
34-
35-
To install these dependencies:
36-
37-
ubuntu
38-
~~~~~~
39-
40-
.. code-block:: bash
34+
libpy optionally provides wrappers for the following libraries:
4135

42-
$ sudo apt install libsparsehash-dev
43-
44-
macOS
45-
~~~~~
46-
47-
.. code-block:: bash
36+
- google sparsehash
4837

49-
$ brew install google-sparsehash
5038

5139
Install
5240
-------

docs/source/install.rst

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,12 @@ lipy requires:
1414
- gcc>=8 or clang>=10
1515
- numpy>=1.11.3
1616

17-
libpy also depends on the following system packages:
17+
Optional Requirements
18+
---------------------
1819

19-
- google sparsehash
20-
21-
To install these dependencies:
22-
23-
ubuntu
24-
~~~~~~
25-
26-
.. code-block:: bash
20+
libpy optionally provides wrappers for the following libraries:
2721

28-
$ sudo apt install libsparsehash-dev
29-
30-
macOS
31-
~~~~~
32-
33-
.. code-block:: bash
34-
35-
$ brew install google-sparsehash
22+
- google sparsehash
3623

3724
Install
3825
-------

include/libpy/dense_hash_map.h renamed to include/libpy/library_wrappers/sparsehash.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <sparsehash/dense_hash_map>
66
#include <sparsehash/dense_hash_set>
77
#include <sparsehash/sparse_hash_map>
8+
#include <sparsehash/sparse_hash_set>
89

910
#include "libpy/to_object.h"
1011

@@ -153,5 +154,21 @@ template<typename Key, typename HashFcn, typename EqualKey, typename Alloc>
153154
struct to_object<dense_hash_set<Key, HashFcn, EqualKey, Alloc>>
154155
: public set_to_object<dense_hash_set<Key, HashFcn, EqualKey, Alloc>> {};
155156

157+
template<typename Key, typename T, typename HashFcn, typename EqualKey, typename Alloc>
158+
struct to_object<google::dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>>
159+
: public map_to_object<google::dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>> {};
160+
161+
template<typename Key, typename T, typename HashFcn, typename EqualKey, typename Alloc>
162+
struct to_object<google::sparse_hash_map<Key, T, HashFcn, EqualKey, Alloc>>
163+
: public map_to_object<google::sparse_hash_map<Key, T, HashFcn, EqualKey, Alloc>> {};
164+
165+
template<typename Key, typename HashFcn, typename EqualKey, typename Alloc>
166+
struct to_object<google::dense_hash_set<Key, HashFcn, EqualKey, Alloc>>
167+
: public set_to_object<google::dense_hash_set<Key, HashFcn, EqualKey, Alloc>> {};
168+
169+
template<typename Key, typename HashFcn, typename EqualKey, typename Alloc>
170+
struct to_object<google::sparse_hash_set<Key, HashFcn, EqualKey, Alloc>>
171+
: public set_to_object<google::sparse_hash_set<Key, HashFcn, EqualKey, Alloc>> {};
172+
156173
} // namespace dispatch
157174
} // namespace py

tests/test_dense_hash_map.cc renamed to tests/library_wrappers/test_sparsehash.cc

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,44 @@
44
#include "gtest/gtest.h"
55

66
#include "libpy/datetime64.h"
7-
#include "libpy/dense_hash_map.h"
7+
#include "libpy/itertools.h"
8+
#include "libpy/library_wrappers/sparsehash.h"
9+
10+
#include "test_utils.h"
11+
12+
namespace test_sparsehash {
13+
14+
using namespace std::literals;
15+
using namespace py::cs::literals;
16+
17+
class sparsehash_to_object : public with_python_interpreter {};
18+
19+
TEST_F(sparsehash_to_object, sparse_hash_map) {
20+
auto map = google::sparse_hash_map<std::string, bool>();
21+
py_test::test_map_to_object_impl(map);
22+
}
23+
24+
TEST_F(sparsehash_to_object, dense_hash_map) {
25+
auto map = google::dense_hash_map<std::string, bool>();
26+
map.set_empty_key("the_empty_key"s);
27+
28+
py_test::test_map_to_object_impl(map);
29+
}
30+
31+
TEST_F(sparsehash_to_object, sparse_hash_set) {
32+
auto filler = py_test::examples<std::string>();
33+
auto a = google::sparse_hash_set<std::string>(filler.begin(), filler.end());
34+
py_test::test_set_to_object_impl(a);
35+
}
36+
37+
TEST_F(sparsehash_to_object, dense_hash_set) {
38+
auto filler = py_test::examples<std::string>();
39+
auto a = google::dense_hash_set<std::string>(filler.begin(),
40+
filler.end(),
41+
"the_empty_key"s);
42+
py_test::test_set_to_object_impl(a);
43+
}
844

9-
namespace test_dense_hash_map {
1045
TEST(dense_hash_map, invalid_empty_key) {
1146
using double_key = py::dense_hash_map<double, int>;
1247
EXPECT_THROW((double_key{std::numeric_limits<double>::quiet_NaN()}),
@@ -42,4 +77,5 @@ TEST(dense_hash_set, invalid_empty_key) {
4277
EXPECT_THROW((M8_key{py::datetime64ns::nat()}), std::invalid_argument);
4378
EXPECT_THROW((M8_key{py::datetime64ns::nat(), 10}), std::invalid_argument);
4479
}
45-
} // namespace test_dense_hash_map
80+
81+
} // namespace test_sparsehash

tests/test_any.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#include "gtest/gtest.h"
55

66
#include "libpy/any.h"
7-
#include "libpy/dense_hash_map.h"
87

98
namespace test_any {
109
TEST(any_vtable, void_vtable) {
@@ -88,7 +87,7 @@ TEST(any_vtable, ostream_format) {
8887
}
8988

9089
TEST(any_vtable, map_key) {
91-
py::dense_hash_map<py::any_vtable, int> map(py::any_vtable{});
90+
std::unordered_map<py::any_vtable, int> map;
9291

9392
map[py::any_vtable::make<int>()] = 0;
9493
map[py::any_vtable::make<float>()] = 1;

tests/test_object_map_key.cc

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
#include "gtest/gtest.h"
55

6-
#include "libpy/dense_hash_map.h"
76
#include "libpy/exception.h"
87
#include "libpy/meta.h"
98
#include "libpy/object_map_key.h"
@@ -241,8 +240,6 @@ void test_use_in_map(M map) {
241240
TEST_F(object_map_key, use_in_map) {
242241
test_use_in_map(std::map<py::object_map_key, int>{});
243242
test_use_in_map(std::unordered_map<py::object_map_key, int>{});
244-
test_use_in_map(py::sparse_hash_map<py::object_map_key, int>{});
245-
test_use_in_map(py::dense_hash_map<py::object_map_key, int>{py::object_map_key{}});
246243
}
247244

248245
TEST_F(object_map_key, convert) {

tests/test_to_object.cc

Lines changed: 10 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
#include "libpy/any.h"
88
#include "libpy/char_sequence.h"
9-
#include "libpy/dense_hash_map.h"
109
#include "libpy/itertools.h"
1110
#include "libpy/meta.h"
1211
#include "libpy/numpy_utils.h"
@@ -21,162 +20,26 @@ using namespace py::cs::literals;
2120

2221
class to_object : public with_python_interpreter {};
2322

24-
template<typename T>
25-
std::array<T, 3> examples();
26-
27-
template<>
28-
std::array<std::int64_t, 3> examples() {
29-
return {-200, 0, 1000};
30-
}
31-
32-
template<>
33-
std::array<std::string, 3> examples() {
34-
return {"foo", "", "arglebargle"};
35-
}
36-
37-
template<>
38-
std::array<std::array<char, 3>, 3> examples() {
39-
std::array<char, 3> foo{'f', 'o', 'o'};
40-
std::array<char, 3> bar{'b', 'a', 'r'};
41-
std::array<char, 3> baz{'b', 'a', 'z'};
42-
return {foo, bar, baz};
43-
}
44-
45-
template<>
46-
std::array<bool, 3> examples() {
47-
return {true, false, true};
48-
}
49-
50-
template<>
51-
std::array<double, 3> examples() {
52-
return {-1.0, -0.0, 100.0};
53-
}
54-
55-
template<>
56-
std::array<py::owned_ref<>, 3> examples() {
57-
Py_INCREF(Py_True);
58-
Py_INCREF(Py_False);
59-
Py_INCREF(Py_None);
60-
return {py::owned_ref<>(Py_True),
61-
py::owned_ref<>(Py_False),
62-
py::owned_ref<>(Py_None)};
63-
}
64-
65-
template<typename M>
66-
void test_map_to_object_impl(M m) {
67-
68-
// Fill the map with some example values.
69-
auto it = py::zip(examples<typename M::key_type>(),
70-
examples<typename M::mapped_type>());
71-
for (auto [key, value] : it) {
72-
m[key] = value;
73-
}
74-
75-
auto check_python_map = [&](py::owned_ref<PyObject> ob) {
76-
ASSERT_TRUE(ob) << "to_object should not return null";
77-
EXPECT_TRUE(PyDict_Check(ob.get()));
78-
79-
// Python map should be the same length as C++ map.
80-
Py_ssize_t len = PyDict_Size(ob.get());
81-
EXPECT_EQ(std::size_t(len), m.size())
82-
<< "Python dict length should match C++ map length.";
83-
84-
// Key/Value pairs in the python map should match the result of calling
85-
// to_object on each key/value pair in the C++ map.
86-
for (auto& [cxx_key, cxx_value] : m) {
87-
auto py_key = py::to_object(cxx_key);
88-
auto py_value = py::to_object(cxx_value);
89-
90-
py::borrowed_ref result = PyDict_GetItem(ob.get(), py_key.get());
91-
ASSERT_TRUE(result) << "Key should have been in the map";
92-
93-
bool values_equal =
94-
PyObject_RichCompareBool(py_value.get(), result.get(), Py_EQ);
95-
EXPECT_EQ(values_equal, 1) << "Dict values were not equal";
96-
}
97-
};
98-
99-
// Check to_object with value, const value, and rvalue reference.
100-
101-
py::owned_ref<PyObject> result = py::to_object(m);
102-
check_python_map(result);
103-
104-
const M& const_ref = m;
105-
py::owned_ref<PyObject> constref_result = py::to_object(const_ref);
106-
check_python_map(constref_result);
107-
108-
M copy = m; // Make a copy before moving b/c the lambda above uses ``m``.
109-
py::owned_ref<PyObject> rvalueref_result = py::to_object(std::move(copy));
110-
check_python_map(rvalueref_result);
111-
}
112-
11323
TEST_F(to_object, map_to_object) {
114-
// NOTE: This test takes a long time to compile (about a .5s per entry in this
115-
// tuple). This is just enough coverage to test all three of our hash table types,
116-
// and a few important key/value types.
117-
auto maps = std::make_tuple(py::dense_hash_map<std::string, py::owned_ref<PyObject>>(
118-
"missing_value"s),
119-
py::sparse_hash_map<std::int64_t, std::array<char, 3>>(),
120-
std::unordered_map<std::string, bool>());
121-
122-
// Call test_map_to_object_impl on each entry in ``maps``.
123-
std::apply([&](auto... map) { (test_map_to_object_impl(map), ...); }, maps);
124-
}
125-
126-
template<typename V>
127-
void test_sequence_to_object_impl(V v) {
128-
auto check_python_list = [&](py::owned_ref<PyObject> ob) {
129-
ASSERT_TRUE(ob) << "to_object should not return null";
130-
EXPECT_EQ(PyList_Check(ob.get()), 1) << "ob should be a list";
131-
132-
Py_ssize_t len = PyList_GET_SIZE(ob.get());
133-
EXPECT_EQ(std::size_t(len), v.size())
134-
<< "Python list length should match C++ vector length.";
135-
136-
// Values in Python list should be the result of calling to_object on each entry
137-
// in the C++ vector.
138-
for (auto [i, cxx_value] : py::enumerate(v)) {
139-
auto py_value = py::to_object(cxx_value);
140-
141-
py::borrowed_ref result = PyList_GetItem(ob.get(), i);
142-
ASSERT_TRUE(result) << "Should have had a value at index " << i;
143-
144-
bool values_equal =
145-
PyObject_RichCompareBool(py_value.get(), result.get(), Py_EQ);
146-
EXPECT_EQ(values_equal, 1)
147-
<< "List values at index " << i << " were not equal";
148-
}
149-
};
150-
151-
// Check to_object with value, const value, and rvalue reference.
152-
153-
py::owned_ref<PyObject> result = py::to_object(v);
154-
check_python_list(result);
155-
156-
const V& const_ref = v;
157-
py::owned_ref<PyObject> constref_result = py::to_object(const_ref);
158-
check_python_list(constref_result);
159-
160-
V copy = v; // Make a copy before moving b/c the lambda above uses ``v``.
161-
py::owned_ref<PyObject> rvalueref_result = py::to_object(std::move(copy));
162-
check_python_list(rvalueref_result);
24+
auto map = std::unordered_map<std::string, bool>();
25+
py_test::test_map_to_object_impl(map);
16326
}
16427

16528
TEST_F(to_object, vector_to_object) {
16629
auto to_vec = [](const auto& arr) { return std::vector(arr.begin(), arr.end()); };
167-
auto vectors = std::make_tuple(to_vec(examples<std::string>()),
168-
to_vec(examples<double>()),
169-
to_vec(examples<py::owned_ref<>>()));
30+
auto vectors = std::make_tuple(to_vec(py_test::examples<std::string>()),
31+
to_vec(py_test::examples<double>()),
32+
to_vec(py_test::examples<py::owned_ref<>>()));
17033
// Call test_sequence_to_object_impl on each entry in `vectors`.
171-
std::apply([&](auto... vec) { (test_sequence_to_object_impl(vec), ...); }, vectors);
34+
std::apply([&](auto... vec) { (py_test::test_sequence_to_object_impl(vec), ...); }, vectors);
17235
}
17336

17437
TEST_F(to_object, array_to_object) {
175-
auto arrays = std::make_tuple(examples<std::string>(),
176-
examples<double>(),
177-
examples<py::owned_ref<>>());
38+
auto arrays = std::make_tuple(py_test::examples<std::string>(),
39+
py_test::examples<double>(),
40+
py_test::examples<py::owned_ref<>>());
17841
// Call test_sequence_to_object_impl on each entry in `arrays`.
179-
std::apply([&](auto... arr) { (test_sequence_to_object_impl(arr), ...); }, arrays);
42+
std::apply([&](auto... arr) { (py_test::test_sequence_to_object_impl(arr), ...); }, arrays);
18043
}
18144

18245
template<typename R, typename T>

0 commit comments

Comments
 (0)