Skip to content

Commit 3d4be81

Browse files
committed
Problem: send/recv functions lack type-safety
Solution: Add functions taking buffers and enum class flags
1 parent 35ba5be commit 3d4be81

File tree

8 files changed

+866
-24
lines changed

8 files changed

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

0 commit comments

Comments
 (0)