Skip to content

Commit 6f0fb2a

Browse files
authored
Merge pull request #317 from gummif/gfa/typesafe-send-recv
Problem: send/recv functions lack type-safety
2 parents ec8a30a + 88cee88 commit 6f0fb2a

File tree

8 files changed

+998
-31
lines changed

8 files changed

+998
-31
lines changed

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ Supported platforms
3939
- Any platform supported by libzmq that provides a sufficiently recent gcc (4.8.1 or newer) or clang (3.3 or newer)
4040
- Visual Studio 2012+ x86/x64
4141

42+
Examples
43+
========
44+
```c++
45+
#include <string>
46+
#include <zmq.hpp>
47+
int main()
48+
{
49+
zmq::context_t ctx;
50+
zmq::socket_t sock(ctx, zmq::socket_type::push);
51+
sock.bind("inproc://test");
52+
const std::string_view m = "Hello, world";
53+
sock.send(zmq::buffer(m), zmq::send_flags::dontwait);
54+
}
55+
```
56+
4257
Contribution policy
4358
===================
4459

@@ -74,5 +89,3 @@ cpp zmq (which will also include libzmq for you).
7489
find_package(cppzmq)
7590
target_link_libraries(*Your Project Name* cppzmq)
7691
```
77-
78-

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ find_package(Threads)
1717

1818
add_executable(
1919
unit_tests
20+
buffer.cpp
2021
message.cpp
2122
context.cpp
2223
socket.cpp

tests/active_poller.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ TEST_CASE("poll basic", "[active_poller]")
157157
{
158158
server_client_setup s;
159159

160-
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}));
160+
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}, zmq::send_flags::none));
161161

162162
zmq::active_poller_t active_poller;
163163
bool message_received = false;
@@ -184,7 +184,7 @@ TEST_CASE("client server", "[active_poller]")
184184
zmq::active_poller_t::handler_t handler = [&](short e) {
185185
if (0 != (e & ZMQ_POLLIN)) {
186186
zmq::message_t zmq_msg;
187-
CHECK_NOTHROW(s.server.recv(&zmq_msg)); // get message
187+
CHECK_NOTHROW(s.server.recv(zmq_msg)); // get message
188188
std::string recv_msg(zmq_msg.data<char>(), zmq_msg.size());
189189
CHECK(send_msg == recv_msg);
190190
} else if (0 != (e & ~ZMQ_POLLOUT)) {
@@ -197,7 +197,7 @@ TEST_CASE("client server", "[active_poller]")
197197
CHECK_NOTHROW(active_poller.add(s.server, ZMQ_POLLIN, handler));
198198

199199
// client sends message
200-
CHECK_NOTHROW(s.client.send(zmq::message_t{send_msg}));
200+
CHECK_NOTHROW(s.client.send(zmq::message_t{send_msg}, zmq::send_flags::none));
201201

202202
CHECK(1 == active_poller.wait(std::chrono::milliseconds{-1}));
203203
CHECK(events == ZMQ_POLLIN);
@@ -236,7 +236,7 @@ TEST_CASE("remove invalid socket throws", "[active_poller]")
236236
TEST_CASE("wait on added empty handler", "[active_poller]")
237237
{
238238
server_client_setup s;
239-
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}));
239+
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}, zmq::send_flags::none));
240240
zmq::active_poller_t active_poller;
241241
zmq::active_poller_t::handler_t handler;
242242
CHECK_NOTHROW(active_poller.add(s.server, ZMQ_POLLIN, handler));
@@ -291,7 +291,7 @@ TEST_CASE("poll client server", "[active_poller]")
291291
CHECK_NOTHROW(active_poller.add(s.server, ZMQ_POLLIN, s.handler));
292292

293293
// client sends message
294-
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}));
294+
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}, zmq::send_flags::none));
295295

296296
// wait for message and verify events
297297
CHECK_NOTHROW(active_poller.wait(std::chrono::milliseconds{500}));
@@ -316,7 +316,7 @@ TEST_CASE("wait one return", "[active_poller]")
316316
active_poller.add(s.server, ZMQ_POLLIN, [&count](short) { ++count; }));
317317

318318
// client sends message
319-
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}));
319+
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}, zmq::send_flags::none));
320320

321321
// wait for message and verify events
322322
CHECK(1 == active_poller.wait(std::chrono::milliseconds{500}));
@@ -326,7 +326,7 @@ TEST_CASE("wait one return", "[active_poller]")
326326
TEST_CASE("wait on move constructed active_poller", "[active_poller]")
327327
{
328328
server_client_setup s;
329-
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}));
329+
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}, zmq::send_flags::none));
330330
zmq::active_poller_t a;
331331
zmq::active_poller_t::handler_t handler;
332332
CHECK_NOTHROW(a.add(s.server, ZMQ_POLLIN, handler));
@@ -340,7 +340,7 @@ TEST_CASE("wait on move constructed active_poller", "[active_poller]")
340340
TEST_CASE("wait on move assigned active_poller", "[active_poller]")
341341
{
342342
server_client_setup s;
343-
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}));
343+
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}, zmq::send_flags::none));
344344
zmq::active_poller_t a;
345345
zmq::active_poller_t::handler_t handler;
346346
CHECK_NOTHROW(a.add(s.server, ZMQ_POLLIN, handler));
@@ -361,14 +361,14 @@ TEST_CASE("received on move constructed active_poller", "[active_poller]")
361361
zmq::active_poller_t a;
362362
CHECK_NOTHROW(a.add(s.server, ZMQ_POLLIN, [&count](short) { ++count; }));
363363
// client sends message
364-
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}));
364+
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}, zmq::send_flags::none));
365365
// wait for message and verify it is received
366366
CHECK(1 == a.wait(std::chrono::milliseconds{500}));
367367
CHECK(1u == count);
368368
// Move construct active_poller b
369369
zmq::active_poller_t b{std::move(a)};
370370
// client sends message again
371-
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}));
371+
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}, zmq::send_flags::none));
372372
// wait for message and verify it is received
373373
CHECK(1 == b.wait(std::chrono::milliseconds{500}));
374374
CHECK(2u == count);
@@ -399,7 +399,7 @@ TEST_CASE("remove from handler", "[active_poller]")
399399
CHECK(ITER_NO == active_poller.size());
400400
// Clients send messages
401401
for (auto &s : setup_list) {
402-
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}));
402+
CHECK_NOTHROW(s.client.send(zmq::message_t{"Hi"}, zmq::send_flags::none));
403403
}
404404

405405
// Wait for all servers to receive a message

tests/buffer.cpp

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
#include <catch.hpp>
2+
#include <zmq.hpp>
3+
4+
#ifdef ZMQ_CPP17
5+
static_assert(std::is_nothrow_swappable_v<zmq::const_buffer>);
6+
static_assert(std::is_nothrow_swappable_v<zmq::mutable_buffer>);
7+
static_assert(std::is_trivially_copyable_v<zmq::const_buffer>);
8+
static_assert(std::is_trivially_copyable_v<zmq::mutable_buffer>);
9+
#endif
10+
11+
#ifdef ZMQ_CPP11
12+
13+
using BT = int16_t;
14+
15+
TEST_CASE("buffer default ctor", "[buffer]")
16+
{
17+
constexpr zmq::mutable_buffer mb;
18+
constexpr zmq::const_buffer cb;
19+
CHECK(mb.size() == 0);
20+
CHECK(mb.data() == nullptr);
21+
CHECK(cb.size() == 0);
22+
CHECK(cb.data() == nullptr);
23+
}
24+
25+
TEST_CASE("buffer data ctor", "[buffer]")
26+
{
27+
std::vector<BT> v(10);
28+
zmq::const_buffer cb(v.data(), v.size() * sizeof(BT));
29+
CHECK(cb.size() == v.size() * sizeof(BT));
30+
CHECK(cb.data() == v.data());
31+
zmq::mutable_buffer mb(v.data(), v.size() * sizeof(BT));
32+
CHECK(mb.size() == v.size() * sizeof(BT));
33+
CHECK(mb.data() == v.data());
34+
zmq::const_buffer from_mut = mb;
35+
CHECK(mb.size() == from_mut.size());
36+
CHECK(mb.data() == from_mut.data());
37+
const auto cmb = mb;
38+
static_assert(std::is_same<decltype(cmb.data()), void*>::value, "");
39+
40+
constexpr const void* cp = nullptr;
41+
constexpr void* p = nullptr;
42+
constexpr zmq::const_buffer cecb = zmq::buffer(p, 0);
43+
constexpr zmq::mutable_buffer cemb = zmq::buffer(p, 0);
44+
CHECK(cecb.data() == nullptr);
45+
CHECK(cemb.data() == nullptr);
46+
}
47+
48+
TEST_CASE("const_buffer operator+", "[buffer]")
49+
{
50+
std::vector<BT> v(10);
51+
zmq::const_buffer cb(v.data(), v.size() * sizeof(BT));
52+
const size_t shift = 4;
53+
auto shifted = cb + shift;
54+
CHECK(shifted.size() == v.size() * sizeof(BT) - shift);
55+
CHECK(shifted.data() == v.data() + shift / sizeof(BT));
56+
auto shifted2 = shift + cb;
57+
CHECK(shifted.size() == shifted2.size());
58+
CHECK(shifted.data() == shifted2.data());
59+
auto cbinp = cb;
60+
cbinp += shift;
61+
CHECK(shifted.size() == cbinp.size());
62+
CHECK(shifted.data() == cbinp.data());
63+
}
64+
65+
TEST_CASE("mutable_buffer operator+", "[buffer]")
66+
{
67+
std::vector<BT> v(10);
68+
zmq::mutable_buffer mb(v.data(), v.size() * sizeof(BT));
69+
const size_t shift = 4;
70+
auto shifted = mb + shift;
71+
CHECK(shifted.size() == v.size() * sizeof(BT) - shift);
72+
CHECK(shifted.data() == v.data() + shift / sizeof(BT));
73+
auto shifted2 = shift + mb;
74+
CHECK(shifted.size() == shifted2.size());
75+
CHECK(shifted.data() == shifted2.data());
76+
auto mbinp = mb;
77+
mbinp += shift;
78+
CHECK(shifted.size() == mbinp.size());
79+
CHECK(shifted.data() == mbinp.data());
80+
}
81+
82+
TEST_CASE("mutable_buffer creation basic", "[buffer]")
83+
{
84+
std::vector<BT> v(10);
85+
zmq::mutable_buffer mb(v.data(), v.size() * sizeof(BT));
86+
zmq::mutable_buffer mb2 = zmq::buffer(v.data(), v.size() * sizeof(BT));
87+
CHECK(mb.data() == mb2.data());
88+
CHECK(mb.size() == mb2.size());
89+
zmq::mutable_buffer mb3 = zmq::buffer(mb);
90+
CHECK(mb.data() == mb3.data());
91+
CHECK(mb.size() == mb3.size());
92+
zmq::mutable_buffer mb4 = zmq::buffer(mb, 10 * v.size() * sizeof(BT));
93+
CHECK(mb.data() == mb4.data());
94+
CHECK(mb.size() == mb4.size());
95+
zmq::mutable_buffer mb5 = zmq::buffer(mb, 4);
96+
CHECK(mb.data() == mb5.data());
97+
CHECK(4 == mb5.size());
98+
}
99+
100+
TEST_CASE("const_buffer creation basic", "[buffer]")
101+
{
102+
const std::vector<BT> v(10);
103+
zmq::const_buffer cb(v.data(), v.size() * sizeof(BT));
104+
zmq::const_buffer cb2 = zmq::buffer(v.data(), v.size() * sizeof(BT));
105+
CHECK(cb.data() == cb2.data());
106+
CHECK(cb.size() == cb2.size());
107+
zmq::const_buffer cb3 = zmq::buffer(cb);
108+
CHECK(cb.data() == cb3.data());
109+
CHECK(cb.size() == cb3.size());
110+
zmq::const_buffer cb4 = zmq::buffer(cb, 10 * v.size() * sizeof(BT));
111+
CHECK(cb.data() == cb4.data());
112+
CHECK(cb.size() == cb4.size());
113+
zmq::const_buffer cb5 = zmq::buffer(cb, 4);
114+
CHECK(cb.data() == cb5.data());
115+
CHECK(4 == cb5.size());
116+
}
117+
118+
TEST_CASE("mutable_buffer creation C array", "[buffer]")
119+
{
120+
BT d[10] = {};
121+
zmq::mutable_buffer b = zmq::buffer(d);
122+
CHECK(b.size() == 10 * sizeof(BT));
123+
CHECK(b.data() == static_cast<BT*>(d));
124+
zmq::const_buffer b2 = zmq::buffer(d, 4);
125+
CHECK(b2.size() == 4);
126+
CHECK(b2.data() == static_cast<BT*>(d));
127+
}
128+
129+
TEST_CASE("const_buffer creation C array", "[buffer]")
130+
{
131+
const BT d[10] = {};
132+
zmq::const_buffer b = zmq::buffer(d);
133+
CHECK(b.size() == 10 * sizeof(BT));
134+
CHECK(b.data() == static_cast<const BT*>(d));
135+
zmq::const_buffer b2 = zmq::buffer(d, 4);
136+
CHECK(b2.size() == 4);
137+
CHECK(b2.data() == static_cast<const BT*>(d));
138+
}
139+
140+
TEST_CASE("mutable_buffer creation array", "[buffer]")
141+
{
142+
std::array<BT, 10> d = {};
143+
zmq::mutable_buffer b = zmq::buffer(d);
144+
CHECK(b.size() == d.size() * sizeof(BT));
145+
CHECK(b.data() == d.data());
146+
zmq::mutable_buffer b2 = zmq::buffer(d, 4);
147+
CHECK(b2.size() == 4);
148+
CHECK(b2.data() == d.data());
149+
}
150+
151+
TEST_CASE("const_buffer creation array", "[buffer]")
152+
{
153+
const std::array<BT, 10> d = {};
154+
zmq::const_buffer b = zmq::buffer(d);
155+
CHECK(b.size() == d.size() * sizeof(BT));
156+
CHECK(b.data() == d.data());
157+
zmq::const_buffer b2 = zmq::buffer(d, 4);
158+
CHECK(b2.size() == 4);
159+
CHECK(b2.data() == d.data());
160+
}
161+
162+
TEST_CASE("const_buffer creation array 2", "[buffer]")
163+
{
164+
std::array<const BT, 10> d = {{}};
165+
zmq::const_buffer b = zmq::buffer(d);
166+
CHECK(b.size() == d.size() * sizeof(BT));
167+
CHECK(b.data() == d.data());
168+
zmq::const_buffer b2 = zmq::buffer(d, 4);
169+
CHECK(b2.size() == 4);
170+
CHECK(b2.data() == d.data());
171+
}
172+
173+
TEST_CASE("mutable_buffer creation vector", "[buffer]")
174+
{
175+
std::vector<BT> d(10);
176+
zmq::mutable_buffer b = zmq::buffer(d);
177+
CHECK(b.size() == d.size() * sizeof(BT));
178+
CHECK(b.data() == d.data());
179+
zmq::mutable_buffer b2 = zmq::buffer(d, 4);
180+
CHECK(b2.size() == 4);
181+
CHECK(b2.data() == d.data());
182+
d.clear();
183+
b = zmq::buffer(d);
184+
CHECK(b.size() == 0);
185+
CHECK(b.data() == nullptr);
186+
}
187+
188+
TEST_CASE("const_buffer creation vector", "[buffer]")
189+
{
190+
std::vector<BT> d(10);
191+
zmq::const_buffer b = zmq::buffer(static_cast<const std::vector<BT>&>(d));
192+
CHECK(b.size() == d.size() * sizeof(BT));
193+
CHECK(b.data() == d.data());
194+
zmq::const_buffer b2 = zmq::buffer(static_cast<const std::vector<BT>&>(d), 4);
195+
CHECK(b2.size() == 4);
196+
CHECK(b2.data() == d.data());
197+
d.clear();
198+
b = zmq::buffer(static_cast<const std::vector<BT>&>(d));
199+
CHECK(b.size() == 0);
200+
CHECK(b.data() == nullptr);
201+
}
202+
203+
TEST_CASE("const_buffer creation string", "[buffer]")
204+
{
205+
const std::wstring d(10, L'a');
206+
zmq::const_buffer b = zmq::buffer(d);
207+
CHECK(b.size() == d.size() * sizeof(wchar_t));
208+
CHECK(b.data() == d.data());
209+
zmq::const_buffer b2 = zmq::buffer(d, 4);
210+
CHECK(b2.size() == 4);
211+
CHECK(b2.data() == d.data());
212+
}
213+
214+
TEST_CASE("mutable_buffer creation string", "[buffer]")
215+
{
216+
std::wstring d(10, L'a');
217+
zmq::mutable_buffer b = zmq::buffer(d);
218+
CHECK(b.size() == d.size() * sizeof(wchar_t));
219+
CHECK(b.data() == d.data());
220+
zmq::mutable_buffer b2 = zmq::buffer(d, 4);
221+
CHECK(b2.size() == 4);
222+
CHECK(b2.data() == d.data());
223+
}
224+
225+
#ifdef ZMQ_CPP17
226+
TEST_CASE("const_buffer creation string_view", "[buffer]")
227+
{
228+
std::wstring dstr(10, L'a');
229+
std::wstring_view d = dstr;
230+
zmq::const_buffer b = zmq::buffer(d);
231+
CHECK(b.size() == d.size() * sizeof(wchar_t));
232+
CHECK(b.data() == d.data());
233+
zmq::const_buffer b2 = zmq::buffer(d, 4);
234+
CHECK(b2.size() == 4);
235+
CHECK(b2.data() == d.data());
236+
}
237+
#endif
238+
239+
TEST_CASE("buffer of structs", "[buffer]")
240+
{
241+
struct some_pod
242+
{
243+
int64_t val;
244+
char arr[8];
245+
};
246+
struct some_non_pod
247+
{
248+
int64_t val;
249+
char arr[8];
250+
std::vector<int> s; // not trivially copyable
251+
};
252+
static_assert(zmq::detail::is_pod_like<some_pod>::value, "");
253+
static_assert(!zmq::detail::is_pod_like<some_non_pod>::value, "");
254+
std::array<some_pod, 1> d;
255+
zmq::mutable_buffer b = zmq::buffer(d);
256+
CHECK(b.size() == d.size() * sizeof(some_pod));
257+
CHECK(b.data() == d.data());
258+
}
259+
260+
#endif

0 commit comments

Comments
 (0)