Problem: Socket options lack type-safety (#393)

Solution: Implement a type for each socket option.

Each option has an associated type, therefore they
can't be simply defined as an enum class.

Use new sockopt getter in test util

Add socket option get function for strings and tests
This commit is contained in:
Gudmundur Adalsteinsson 2020-03-24 08:33:50 +00:00 committed by GitHub
parent 10431084bb
commit a3e5b54c3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 846 additions and 37 deletions

View File

@ -47,22 +47,424 @@ TEST_CASE("socket swap", "[socket]")
swap(socket1, socket2);
}
#ifdef ZMQ_CPP11
TEST_CASE("socket options", "[socket]")
{
zmq::context_t context;
zmq::socket_t socket(context, zmq::socket_type::router);
#ifdef ZMQ_IMMEDIATE
socket.set(zmq::sockopt::immediate, 0);
socket.set(zmq::sockopt::immediate, false);
CHECK(socket.get(zmq::sockopt::immediate) == false);
// unit out of range
CHECK_THROWS_AS(socket.set(zmq::sockopt::immediate, 80), const zmq::error_t &);
#endif
#ifdef ZMQ_LINGER
socket.set(zmq::sockopt::linger, 55);
CHECK(socket.get(zmq::sockopt::linger) == 55);
#endif
#ifdef ZMQ_ROUTING_ID
const std::string id = "foobar";
socket.set(zmq::sockopt::routing_id, "foobar");
socket.set(zmq::sockopt::routing_id, zmq::buffer(id));
socket.set(zmq::sockopt::routing_id, id);
#ifdef ZMQ_CPP17
socket.set(zmq::sockopt::routing_id, std::string_view{id});
#endif
std::string id_ret(10, ' ');
auto size = socket.get(zmq::sockopt::routing_id, zmq::buffer(id_ret));
id_ret.resize(size);
CHECK(id == id_ret);
auto stropt = socket.get(zmq::sockopt::routing_id);
CHECK(id == stropt);
std::string id_ret_small(3, ' ');
// truncated
CHECK_THROWS_AS(socket.get(zmq::sockopt::routing_id, zmq::buffer(id_ret_small)),
const zmq::error_t &);
#endif
}
template<class T>
void check_array_opt(T opt,
zmq::socket_t &sock,
std::string info,
bool set_only = false)
{
const std::string val = "foobar";
INFO("setting " + info);
sock.set(opt, val);
if (set_only)
return;
INFO("getting " + info);
auto s = sock.get(opt);
CHECK(s == val);
}
template<class T>
void check_array_opt_get(T opt, zmq::socket_t &sock, std::string info)
{
INFO("getting " + info);
(void) sock.get(opt);
}
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 0, 0)
template<class T> void check_bin_z85(T opt, zmq::socket_t &sock, std::string str_val)
{
std::vector<uint8_t> bin_val(32);
const auto dret = zmq_z85_decode(bin_val.data(), str_val.c_str());
CHECK(dret != nullptr);
sock.set(opt, str_val);
sock.set(opt, zmq::buffer(bin_val));
auto sv = sock.get(opt);
CHECK(sv == str_val);
auto bv = sock.get(opt, 32);
REQUIRE(bv.size() == bin_val.size());
CHECK(std::memcmp(bv.data(), bin_val.data(), bin_val.size()) == 0);
}
#endif
TEST_CASE("socket check array options", "[socket]")
{
zmq::context_t context;
zmq::socket_t router(context, zmq::socket_type::router);
zmq::socket_t xpub(context, zmq::socket_type::xpub);
zmq::socket_t sub(context, zmq::socket_type::sub);
#ifdef ZMQ_BINDTODEVICE
// requires setting CAP_NET_RAW
//check_array_opt(zmq::sockopt::bindtodevice, router, "bindtodevice");
#endif
#ifdef ZMQ_CONNECT_ROUTING_ID
check_array_opt(zmq::sockopt::connect_routing_id, router, "connect_routing_id",
true);
#endif
#ifdef ZMQ_LAST_ENDPOINT
check_array_opt_get(zmq::sockopt::last_endpoint, router, "last_endpoint");
#endif
#ifdef ZMQ_METADATA
router.set(zmq::sockopt::metadata, zmq::str_buffer("X-foo:bar"));
#endif
#ifdef ZMQ_PLAIN_PASSWORD
check_array_opt(zmq::sockopt::plain_password, router, "plain_password");
#endif
#ifdef ZMQ_PLAIN_USERNAME
check_array_opt(zmq::sockopt::plain_username, router, "plain_username");
#endif
#ifdef ZMQ_ROUTING_ID
check_array_opt(zmq::sockopt::routing_id, router, "routing_id");
#endif
#ifdef ZMQ_SOCKS_PROXY
check_array_opt(zmq::sockopt::socks_proxy, router, "socks_proxy");
#endif
#ifdef ZMQ_SUBSCRIBE
check_array_opt(zmq::sockopt::subscribe, sub, "subscribe", true);
#endif
#ifdef ZMQ_UNSUBSCRIBE
check_array_opt(zmq::sockopt::unsubscribe, sub, "unsubscribe", true);
#endif
#ifdef ZMQ_XPUB_WELCOME_MSG
check_array_opt(zmq::sockopt::xpub_welcome_msg, xpub, "xpub_welcome_msg", true);
#endif
#ifdef ZMQ_ZAP_DOMAIN
check_array_opt(zmq::sockopt::zap_domain, router, "zap_domain");
#endif
// curve
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 0, 0) && defined(ZMQ_HAS_CAPABILITIES)
if (zmq_has("curve") == 1) {
const std::string spk = "rq:rM>}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7";
const std::string ssk = "JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6";
const std::string cpk = "Yne@$w-vo<fVvi]a<NY6T1ed:M$fCG*[IaLV{hID";
const std::string csk = "D:)Q[IlAW!ahhC2ac:9*A}h:p?([4%wOTJ%JR%cs";
zmq::socket_t curve_server(context, zmq::socket_type::router);
curve_server.set(zmq::sockopt::curve_server, true);
CHECK(curve_server.get(zmq::sockopt::curve_server));
check_bin_z85(zmq::sockopt::curve_secretkey, curve_server, ssk);
zmq::socket_t curve_client(context, zmq::socket_type::router);
curve_client.set(zmq::sockopt::curve_server, false);
CHECK_FALSE(curve_client.get(zmq::sockopt::curve_server));
check_bin_z85(zmq::sockopt::curve_serverkey, curve_client, spk);
check_bin_z85(zmq::sockopt::curve_publickey, curve_client, cpk);
check_bin_z85(zmq::sockopt::curve_secretkey, curve_client, csk);
}
#endif
// gssapi
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0) && defined(ZMQ_HAS_CAPABILITIES)
if (zmq_has("gssapi") == 1 && false) // TODO enable
{
zmq::socket_t gss_server(context, zmq::socket_type::router);
gss_server.set(zmq::sockopt::gssapi_server, true);
CHECK(gss_server.get(zmq::sockopt::gssapi_server) == 1);
gss_server.set(zmq::sockopt::gssapi_plaintext, false);
CHECK(gss_server.get(zmq::sockopt::gssapi_plaintext) == 0);
check_array_opt(zmq::sockopt::gssapi_principal, gss_server,
"gssapi_principal");
zmq::socket_t gss_client(context, zmq::socket_type::router);
CHECK(gss_client.get(zmq::sockopt::gssapi_server) == 0);
check_array_opt(zmq::sockopt::gssapi_principal, gss_client,
"gssapi_principal");
check_array_opt(zmq::sockopt::gssapi_service_principal, gss_client,
"gssapi_service_principal");
}
#endif
}
template<class T, class Opt>
void check_integral_opt(Opt opt,
zmq::socket_t &sock,
std::string info,
bool set_only = false)
{
const T val = 1;
INFO("setting " + info);
sock.set(opt, val);
if (set_only)
return;
INFO("getting " + info);
auto s = sock.get(opt);
CHECK(s == val);
}
template<class T, class Opt>
void check_integral_opt_get(Opt opt, zmq::socket_t &sock, std::string info)
{
INFO("getting " + info);
(void) sock.get(opt);
}
TEST_CASE("socket check integral options", "[socket]")
{
zmq::context_t context;
zmq::socket_t router(context, zmq::socket_type::router);
zmq::socket_t xpub(context, zmq::socket_type::xpub);
zmq::socket_t req(context, zmq::socket_type::req);
#ifdef ZMQ_STREAM_NOTIFY
zmq::socket_t stream(context, zmq::socket_type::stream);
#endif
#ifdef ZMQ_AFFINITY
check_integral_opt<uint64_t>(zmq::sockopt::affinity, router, "affinity");
#endif
#ifdef ZMQ_BACKLOG
check_integral_opt<int>(zmq::sockopt::backlog, router, "backlog");
#endif
#ifdef ZMQ_CONFLATE
check_integral_opt<int>(zmq::sockopt::conflate, router, "conflate");
#endif
#ifdef ZMQ_CONNECT_TIMEOUT
check_integral_opt<int>(zmq::sockopt::connect_timeout, router,
"connect_timeout");
#endif
#ifdef ZMQ_EVENTS
check_integral_opt_get<int>(zmq::sockopt::events, router, "events");
#endif
#ifdef ZMQ_FD
check_integral_opt_get<zmq::sockopt::cppzmq_fd_t>(zmq::sockopt::fd, router,
"fd");
#endif
#ifdef ZMQ_HANDSHAKE_IVL
check_integral_opt<int>(zmq::sockopt::handshake_ivl, router, "handshake_ivl");
#endif
#ifdef ZMQ_HEARTBEAT_IVL
check_integral_opt<int>(zmq::sockopt::heartbeat_ivl, router, "heartbeat_ivl");
#endif
#ifdef ZMQ_HEARTBEAT_TIMEOUT
check_integral_opt<int>(zmq::sockopt::heartbeat_timeout, router,
"heartbeat_timeout");
#endif
#ifdef ZMQ_HEARTBEAT_TTL
router.set(zmq::sockopt::heartbeat_ttl, 100);
CHECK(router.get(zmq::sockopt::heartbeat_ttl) == 100);
#endif
#ifdef ZMQ_IMMEDIATE
check_integral_opt<int>(zmq::sockopt::immediate, router, "immediate");
#endif
#ifdef ZMQ_INVERT_MATCHING
check_integral_opt<int>(zmq::sockopt::invert_matching, router,
"invert_matching");
#endif
#ifdef ZMQ_IPV6
check_integral_opt<int>(zmq::sockopt::ipv6, router, "ipv6");
#endif
#ifdef ZMQ_LINGER
check_integral_opt<int>(zmq::sockopt::linger, router, "linger");
#endif
#ifdef ZMQ_MAXMSGSIZE
check_integral_opt<int64_t>(zmq::sockopt::maxmsgsize, router, "maxmsgsize");
#endif
#ifdef ZMQ_MECHANISM
check_integral_opt_get<int>(zmq::sockopt::mechanism, router, "mechanism");
#endif
#ifdef ZMQ_MULTICAST_HOPS
check_integral_opt<int>(zmq::sockopt::multicast_hops, router, "multicast_hops");
#endif
#ifdef ZMQ_MULTICAST_LOOP
check_integral_opt<int>(zmq::sockopt::multicast_loop, router, "multicast_loop");
#endif
#ifdef ZMQ_MULTICAST_MAXTPDU
check_integral_opt<int>(zmq::sockopt::multicast_maxtpdu, router,
"multicast_maxtpdu");
#endif
#ifdef ZMQ_PLAIN_SERVER
check_integral_opt<int>(zmq::sockopt::plain_server, router, "plain_server");
#endif
#ifdef ZMQ_USE_FD
check_integral_opt<int>(zmq::sockopt::use_fd, router, "use_fd");
#endif
#ifdef ZMQ_PROBE_ROUTER
check_integral_opt<int>(zmq::sockopt::probe_router, router, "probe_router",
true);
#endif
#ifdef ZMQ_RATE
check_integral_opt<int>(zmq::sockopt::rate, router, "rate");
#endif
#ifdef ZMQ_RCVBUF
check_integral_opt<int>(zmq::sockopt::rcvbuf, router, "rcvbuf");
#endif
#ifdef ZMQ_RCVHWM
check_integral_opt<int>(zmq::sockopt::rcvhwm, router, "rcvhwm");
#endif
#ifdef ZMQ_RCVMORE
check_integral_opt_get<int>(zmq::sockopt::rcvmore, router, "rcvmore");
#endif
#ifdef ZMQ_RCVTIMEO
check_integral_opt<int>(zmq::sockopt::rcvtimeo, router, "rcvtimeo");
#endif
#ifdef ZMQ_RECONNECT_IVL
check_integral_opt<int>(zmq::sockopt::reconnect_ivl, router, "reconnect_ivl");
#endif
#ifdef ZMQ_RECONNECT_IVL_MAX
check_integral_opt<int>(zmq::sockopt::reconnect_ivl_max, router,
"reconnect_ivl_max");
#endif
#ifdef ZMQ_RECOVERY_IVL
check_integral_opt<int>(zmq::sockopt::recovery_ivl, router, "recovery_ivl");
#endif
#ifdef ZMQ_REQ_CORRELATE
check_integral_opt<int>(zmq::sockopt::req_correlate, req, "req_correlate", true);
#endif
#ifdef ZMQ_REQ_RELAXED
check_integral_opt<int>(zmq::sockopt::req_relaxed, req, "req_relaxed", true);
#endif
#ifdef ZMQ_ROUTER_HANDOVER
check_integral_opt<int>(zmq::sockopt::router_handover, router, "router_handover",
true);
#endif
#ifdef ZMQ_ROUTER_MANDATORY
check_integral_opt<int>(zmq::sockopt::router_mandatory, router,
"router_mandatory", true);
#endif
#ifdef ZMQ_ROUTER_NOTIFY
check_integral_opt<int>(zmq::sockopt::router_notify, router, "router_notify");
#endif
#ifdef ZMQ_SNDBUF
check_integral_opt<int>(zmq::sockopt::sndbuf, router, "sndbuf");
#endif
#ifdef ZMQ_SNDHWM
check_integral_opt<int>(zmq::sockopt::sndhwm, router, "sndhwm");
#endif
#ifdef ZMQ_SNDTIMEO
check_integral_opt<int>(zmq::sockopt::sndtimeo, router, "sndtimeo");
#endif
#ifdef ZMQ_STREAM_NOTIFY
check_integral_opt<int>(zmq::sockopt::stream_notify, stream, "stream_notify",
true);
#endif
#ifdef ZMQ_TCP_KEEPALIVE
check_integral_opt<int>(zmq::sockopt::tcp_keepalive, router, "tcp_keepalive");
#endif
#ifdef ZMQ_TCP_KEEPALIVE_CNT
check_integral_opt<int>(zmq::sockopt::tcp_keepalive_cnt, router,
"tcp_keepalive_cnt");
#endif
#ifdef ZMQ_TCP_KEEPALIVE_IDLE
check_integral_opt<int>(zmq::sockopt::tcp_keepalive_idle, router,
"tcp_keepalive_idle");
#endif
#ifdef ZMQ_TCP_KEEPALIVE_INTVL
check_integral_opt<int>(zmq::sockopt::tcp_keepalive_intvl, router,
"tcp_keepalive_intvl");
#endif
#ifdef ZMQ_TCP_MAXRT
check_integral_opt<int>(zmq::sockopt::tcp_maxrt, router, "tcp_maxrt");
#endif
#ifdef ZMQ_THREAD_SAFE
check_integral_opt_get<bool>(zmq::sockopt::thread_safe, router, "thread_safe");
#endif
#ifdef ZMQ_TOS
check_integral_opt<int>(zmq::sockopt::tos, router, "tos");
#endif
#ifdef ZMQ_TYPE
check_integral_opt_get<int>(zmq::sockopt::type, router, "type");
#endif
#ifdef ZMQ_HAVE_VMCI
#ifdef ZMQ_VMCI_BUFFER_SIZE
check_integral_opt<uint64_t>(zmq::sockopt::vmci_buffer_size, router,
"vmci_buffer_size");
#endif
#ifdef ZMQ_VMCI_BUFFER_MIN_SIZE
check_integral_opt<uint64_t>(zmq::sockopt::vmci_buffer_min_size, router,
"vmci_buffer_min_size");
#endif
#ifdef ZMQ_VMCI_BUFFER_MAX_SIZE
check_integral_opt<uint64_t>(zmq::sockopt::vmci_buffer_max_size, router,
"vmci_buffer_max_size");
#endif
#ifdef ZMQ_VMCI_CONNECT_TIMEOUT
check_integral_opt<int>(zmq::sockopt::vmci_connect_timeout, router,
"vmci_connect_timeout");
#endif
#endif
#ifdef ZMQ_XPUB_VERBOSE
check_integral_opt<int>(zmq::sockopt::xpub_verbose, xpub, "xpub_verbose", true);
#endif
#ifdef ZMQ_XPUB_VERBOSER
check_integral_opt<int>(zmq::sockopt::xpub_verboser, xpub, "xpub_verboser",
true);
#endif
#ifdef ZMQ_XPUB_MANUAL
check_integral_opt<int>(zmq::sockopt::xpub_manual, xpub, "xpub_manual", true);
#endif
#ifdef ZMQ_XPUB_NODROP
check_integral_opt<int>(zmq::sockopt::xpub_nodrop, xpub, "xpub_nodrop", true);
#endif
#ifdef ZMQ_ZAP_ENFORCE_DOMAIN
check_integral_opt<int>(zmq::sockopt::zap_enforce_domain, router,
"zap_enforce_domain");
#endif
}
#endif
TEST_CASE("socket flags", "[socket]")
{
CHECK((zmq::recv_flags::dontwait | zmq::recv_flags::none)
== static_cast<zmq::recv_flags>(ZMQ_DONTWAIT | 0));
== static_cast<zmq::recv_flags>(ZMQ_DONTWAIT | 0));
CHECK((zmq::recv_flags::dontwait & zmq::recv_flags::none)
== static_cast<zmq::recv_flags>(ZMQ_DONTWAIT & 0));
== static_cast<zmq::recv_flags>(ZMQ_DONTWAIT & 0));
CHECK((zmq::recv_flags::dontwait ^ zmq::recv_flags::none)
== static_cast<zmq::recv_flags>(ZMQ_DONTWAIT ^ 0));
== static_cast<zmq::recv_flags>(ZMQ_DONTWAIT ^ 0));
CHECK(~zmq::recv_flags::dontwait == static_cast<zmq::recv_flags>(~ZMQ_DONTWAIT));
CHECK((zmq::send_flags::dontwait | zmq::send_flags::sndmore)
== static_cast<zmq::send_flags>(ZMQ_DONTWAIT | ZMQ_SNDMORE));
== static_cast<zmq::send_flags>(ZMQ_DONTWAIT | ZMQ_SNDMORE));
CHECK((zmq::send_flags::dontwait & zmq::send_flags::sndmore)
== static_cast<zmq::send_flags>(ZMQ_DONTWAIT & ZMQ_SNDMORE));
== static_cast<zmq::send_flags>(ZMQ_DONTWAIT & ZMQ_SNDMORE));
CHECK((zmq::send_flags::dontwait ^ zmq::send_flags::sndmore)
== static_cast<zmq::send_flags>(ZMQ_DONTWAIT ^ ZMQ_SNDMORE));
== static_cast<zmq::send_flags>(ZMQ_DONTWAIT ^ ZMQ_SNDMORE));
CHECK(~zmq::send_flags::dontwait == static_cast<zmq::send_flags>(~ZMQ_DONTWAIT));
}
@ -125,8 +527,8 @@ TEST_CASE("socket send dontwait", "[socket]")
std::vector<char> buf(4);
auto res = s.send(zmq::buffer(buf), zmq::send_flags::dontwait);
CHECK(!res);
res = s.send(zmq::buffer(buf),
zmq::send_flags::dontwait | zmq::send_flags::sndmore);
res =
s.send(zmq::buffer(buf), zmq::send_flags::dontwait | zmq::send_flags::sndmore);
CHECK(!res);
zmq::message_t msg;
@ -253,8 +655,7 @@ TEST_CASE("socket proxy", "[socket]")
front.bind("inproc://test1");
back.bind("inproc://test2");
capture.bind("inproc://test3");
auto f = std::async(std::launch::async, [&]()
{
auto f = std::async(std::launch::async, [&]() {
auto s1 = std::move(front);
auto s2 = std::move(back);
auto s3 = std::move(capture);
@ -279,8 +680,7 @@ TEST_CASE("socket proxy steerable", "[socket]")
front.bind("inproc://test1");
back.bind("inproc://test2");
control.connect("inproc://test3");
auto f = std::async(std::launch::async, [&]()
{
auto f = std::async(std::launch::async, [&]() {
auto s1 = std::move(front);
auto s2 = std::move(back);
auto s3 = std::move(control);

View File

@ -4,30 +4,14 @@
#include <zmq.hpp>
#if defined(ZMQ_CPP11)
#include <array>
class loopback_ip4_binder
inline std::string bind_ip4_loopback(zmq::socket_t &socket)
{
public:
loopback_ip4_binder(zmq::socket_t &socket) { bind(socket); }
std::string endpoint() { return endpoint_; }
private:
// Helper function used in constructor
// as Gtest allows ASSERT_* only in void returning functions
// and constructor/destructor are not.
void bind(zmq::socket_t &socket)
{
REQUIRE_NOTHROW(socket.bind("tcp://127.0.0.1:*"));
std::array<char, 100> endpoint{};
size_t endpoint_size = endpoint.size();
REQUIRE_NOTHROW(
socket.getsockopt(ZMQ_LAST_ENDPOINT, endpoint.data(), &endpoint_size));
REQUIRE(endpoint_size < endpoint.size());
endpoint_ = std::string{endpoint.data()};
}
std::string endpoint_;
};
socket.bind("tcp://127.0.0.1:*");
std::string endpoint(100, ' ');
endpoint.resize(socket.get(zmq::sockopt::last_endpoint, zmq::buffer(endpoint)));
return endpoint;
}
struct common_server_client_setup
{
@ -39,7 +23,7 @@ struct common_server_client_setup
void init()
{
endpoint = loopback_ip4_binder{server}.endpoint();
endpoint = bind_ip4_loopback(server);
REQUIRE_NOTHROW(client.connect(endpoint));
}

429
zmq.hpp
View File

@ -80,6 +80,11 @@
#define ZMQ_CONSTEXPR_VAR const
#define ZMQ_CPP11_DEPRECATED(msg)
#endif
#if defined(ZMQ_CPP17)
#define ZMQ_INLINE_VAR inline
#else
#define ZMQ_INLINE_VAR
#endif
#include <zmq.h>
@ -1268,6 +1273,297 @@ constexpr const_buffer operator"" _zbuf(const char32_t *str, size_t len) noexcep
#endif // ZMQ_CPP11
#ifdef ZMQ_CPP11
namespace sockopt
{
// There are two types of options,
// integral type with known compiler time size (int, bool, int64_t, uint64_t)
// and arrays with dynamic size (strings, binary data).
// BoolUnit: if true accepts values of type bool (but passed as T into libzmq)
template<int Opt, class T, bool BoolUnit = false> struct integral_option
{
};
// NullTerm:
// 0: binary data
// 1: null-terminated string (`getsockopt` size includes null)
// 2: binary (size 32) or Z85 encoder string of size 41 (null included)
template<int Opt, int NullTerm = 1> struct array_option
{
};
#define ZMQ_DEFINE_INTEGRAL_OPT(OPT, NAME, TYPE) \
using NAME##_t = integral_option<OPT, TYPE, false>; \
ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME
#define ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(OPT, NAME, TYPE) \
using NAME##_t = integral_option<OPT, TYPE, true>; \
ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME
#define ZMQ_DEFINE_ARRAY_OPT(OPT, NAME) \
using NAME##_t = array_option<OPT>; \
ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME
#define ZMQ_DEFINE_ARRAY_OPT_BINARY(OPT, NAME) \
using NAME##_t = array_option<OPT, 0>; \
ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME
#define ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(OPT, NAME) \
using NAME##_t = array_option<OPT, 2>; \
ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME
// duplicate definition from libzmq 4.3.3
#if defined _WIN32
#if defined _WIN64
typedef unsigned __int64 cppzmq_fd_t;
#else
typedef unsigned int cppzmq_fd_t;
#endif
#else
typedef int cppzmq_fd_t;
#endif
#ifdef ZMQ_AFFINITY
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_AFFINITY, affinity, uint64_t);
#endif
#ifdef ZMQ_BACKLOG
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_BACKLOG, backlog, int);
#endif
#ifdef ZMQ_BINDTODEVICE
ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_BINDTODEVICE, bindtodevice);
#endif
#ifdef ZMQ_CONFLATE
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CONFLATE, conflate, int);
#endif
#ifdef ZMQ_CONNECT_ROUTING_ID
ZMQ_DEFINE_ARRAY_OPT(ZMQ_CONNECT_ROUTING_ID, connect_routing_id);
#endif
#ifdef ZMQ_CONNECT_TIMEOUT
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_CONNECT_TIMEOUT, connect_timeout, int);
#endif
#ifdef ZMQ_CURVE_PUBLICKEY
ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_PUBLICKEY, curve_publickey);
#endif
#ifdef ZMQ_CURVE_SECRETKEY
ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SECRETKEY, curve_secretkey);
#endif
#ifdef ZMQ_CURVE_SERVER
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CURVE_SERVER, curve_server, int);
#endif
#ifdef ZMQ_CURVE_SERVERKEY
ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SERVERKEY, curve_serverkey);
#endif
#ifdef ZMQ_EVENTS
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_EVENTS, events, int);
#endif
#ifdef ZMQ_FD
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_FD, fd, cppzmq_fd_t);
#endif
#ifdef ZMQ_GSSAPI_PLAINTEXT
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_GSSAPI_PLAINTEXT, gssapi_plaintext, int);
#endif
#ifdef ZMQ_GSSAPI_SERVER
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_GSSAPI_SERVER, gssapi_server, int);
#endif
#ifdef ZMQ_GSSAPI_SERVICE_PRINCIPAL
ZMQ_DEFINE_ARRAY_OPT(ZMQ_GSSAPI_SERVICE_PRINCIPAL, gssapi_service_principal);
#endif
#ifdef ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE,
gssapi_service_principal_nametype,
int);
#endif
#ifdef ZMQ_GSSAPI_PRINCIPAL
ZMQ_DEFINE_ARRAY_OPT(ZMQ_GSSAPI_PRINCIPAL, gssapi_principal);
#endif
#ifdef ZMQ_GSSAPI_PRINCIPAL_NAMETYPE
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_GSSAPI_PRINCIPAL_NAMETYPE,
gssapi_principal_nametype,
int);
#endif
#ifdef ZMQ_HANDSHAKE_IVL
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HANDSHAKE_IVL, handshake_ivl, int);
#endif
#ifdef ZMQ_HEARTBEAT_IVL
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_IVL, heartbeat_ivl, int);
#endif
#ifdef ZMQ_HEARTBEAT_TIMEOUT
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TIMEOUT, heartbeat_timeout, int);
#endif
#ifdef ZMQ_HEARTBEAT_TTL
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TTL, heartbeat_ttl, int);
#endif
#ifdef ZMQ_IMMEDIATE
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IMMEDIATE, immediate, int);
#endif
#ifdef ZMQ_INVERT_MATCHING
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_INVERT_MATCHING, invert_matching, int);
#endif
#ifdef ZMQ_IPV6
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IPV6, ipv6, int);
#endif
#ifdef ZMQ_LAST_ENDPOINT
ZMQ_DEFINE_ARRAY_OPT(ZMQ_LAST_ENDPOINT, last_endpoint);
#endif
#ifdef ZMQ_LINGER
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_LINGER, linger, int);
#endif
#ifdef ZMQ_MAXMSGSIZE
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MAXMSGSIZE, maxmsgsize, int64_t);
#endif
#ifdef ZMQ_MECHANISM
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MECHANISM, mechanism, int);
#endif
#ifdef ZMQ_METADATA
ZMQ_DEFINE_ARRAY_OPT(ZMQ_METADATA, metadata);
#endif
#ifdef ZMQ_MULTICAST_HOPS
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_HOPS, multicast_hops, int);
#endif
#ifdef ZMQ_MULTICAST_LOOP
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_MULTICAST_LOOP, multicast_loop, int);
#endif
#ifdef ZMQ_MULTICAST_MAXTPDU
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_MAXTPDU, multicast_maxtpdu, int);
#endif
#ifdef ZMQ_PLAIN_SERVER
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PLAIN_SERVER, plain_server, int);
#endif
#ifdef ZMQ_PLAIN_PASSWORD
ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_PASSWORD, plain_password);
#endif
#ifdef ZMQ_PLAIN_USERNAME
ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_USERNAME, plain_username);
#endif
#ifdef ZMQ_USE_FD
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_USE_FD, use_fd, int);
#endif
#ifdef ZMQ_PROBE_ROUTER
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PROBE_ROUTER, probe_router, int);
#endif
#ifdef ZMQ_RATE
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RATE, rate, int);
#endif
#ifdef ZMQ_RCVBUF
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVBUF, rcvbuf, int);
#endif
#ifdef ZMQ_RCVHWM
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVHWM, rcvhwm, int);
#endif
#ifdef ZMQ_RCVMORE
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_RCVMORE, rcvmore, int);
#endif
#ifdef ZMQ_RCVTIMEO
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVTIMEO, rcvtimeo, int);
#endif
#ifdef ZMQ_RECONNECT_IVL
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL, reconnect_ivl, int);
#endif
#ifdef ZMQ_RECONNECT_IVL_MAX
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL_MAX, reconnect_ivl_max, int);
#endif
#ifdef ZMQ_RECOVERY_IVL
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECOVERY_IVL, recovery_ivl, int);
#endif
#ifdef ZMQ_REQ_CORRELATE
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_REQ_CORRELATE, req_correlate, int);
#endif
#ifdef ZMQ_REQ_RELAXED
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_REQ_RELAXED, req_relaxed, int);
#endif
#ifdef ZMQ_ROUTER_HANDOVER
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_HANDOVER, router_handover, int);
#endif
#ifdef ZMQ_ROUTER_MANDATORY
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_MANDATORY, router_mandatory, int);
#endif
#ifdef ZMQ_ROUTER_NOTIFY
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_ROUTER_NOTIFY, router_notify, int);
#endif
#ifdef ZMQ_ROUTING_ID
ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_ROUTING_ID, routing_id);
#endif
#ifdef ZMQ_SNDBUF
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDBUF, sndbuf, int);
#endif
#ifdef ZMQ_SNDHWM
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDHWM, sndhwm, int);
#endif
#ifdef ZMQ_SNDTIMEO
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDTIMEO, sndtimeo, int);
#endif
#ifdef ZMQ_SOCKS_PROXY
ZMQ_DEFINE_ARRAY_OPT(ZMQ_SOCKS_PROXY, socks_proxy);
#endif
#ifdef ZMQ_STREAM_NOTIFY
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_STREAM_NOTIFY, stream_notify, int);
#endif
#ifdef ZMQ_SUBSCRIBE
ZMQ_DEFINE_ARRAY_OPT(ZMQ_SUBSCRIBE, subscribe);
#endif
#ifdef ZMQ_TCP_KEEPALIVE
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE, tcp_keepalive, int);
#endif
#ifdef ZMQ_TCP_KEEPALIVE_CNT
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_CNT, tcp_keepalive_cnt, int);
#endif
#ifdef ZMQ_TCP_KEEPALIVE_IDLE
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_IDLE, tcp_keepalive_idle, int);
#endif
#ifdef ZMQ_TCP_KEEPALIVE_INTVL
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_INTVL, tcp_keepalive_intvl, int);
#endif
#ifdef ZMQ_TCP_MAXRT
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_MAXRT, tcp_maxrt, int);
#endif
#ifdef ZMQ_THREAD_SAFE
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_THREAD_SAFE, thread_safe, int);
#endif
#ifdef ZMQ_TOS
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TOS, tos, int);
#endif
#ifdef ZMQ_TYPE
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TYPE, type, int);
#endif
#ifdef ZMQ_UNSUBSCRIBE
ZMQ_DEFINE_ARRAY_OPT(ZMQ_UNSUBSCRIBE, unsubscribe);
#endif
#ifdef ZMQ_VMCI_BUFFER_SIZE
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_SIZE, vmci_buffer_size, uint64_t);
#endif
#ifdef ZMQ_VMCI_BUFFER_MIN_SIZE
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_MIN_SIZE, vmci_buffer_min_size, uint64_t);
#endif
#ifdef ZMQ_VMCI_BUFFER_MAX_SIZE
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_MAX_SIZE, vmci_buffer_max_size, uint64_t);
#endif
#ifdef ZMQ_VMCI_CONNECT_TIMEOUT
ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_CONNECT_TIMEOUT, vmci_connect_timeout, int);
#endif
#ifdef ZMQ_XPUB_VERBOSE
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSE, xpub_verbose, int);
#endif
#ifdef ZMQ_XPUB_VERBOSER
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSER, xpub_verboser, int);
#endif
#ifdef ZMQ_XPUB_MANUAL
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_MANUAL, xpub_manual, int);
#endif
#ifdef ZMQ_XPUB_NODROP
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_NODROP, xpub_nodrop, int);
#endif
#ifdef ZMQ_XPUB_WELCOME_MSG
ZMQ_DEFINE_ARRAY_OPT(ZMQ_XPUB_WELCOME_MSG, xpub_welcome_msg);
#endif
#ifdef ZMQ_ZAP_ENFORCE_DOMAIN
ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ZAP_ENFORCE_DOMAIN, zap_enforce_domain, int);
#endif
#ifdef ZMQ_ZAP_DOMAIN
ZMQ_DEFINE_ARRAY_OPT(ZMQ_ZAP_DOMAIN, zap_domain);
#endif
} // namespace sockopt
#endif // ZMQ_CPP11
namespace detail
{
class socket_base
@ -1276,11 +1572,14 @@ class socket_base
socket_base() ZMQ_NOTHROW : _handle(ZMQ_NULLPTR) {}
ZMQ_EXPLICIT socket_base(void *handle) ZMQ_NOTHROW : _handle(handle) {}
template<typename T> void setsockopt(int option_, T const &optval)
template<typename T>
ZMQ_CPP11_DEPRECATED("from 4.7.0, use `set` taking option from zmq::sockopt")
void setsockopt(int option_, T const &optval)
{
setsockopt(option_, &optval, sizeof(T));
}
ZMQ_CPP11_DEPRECATED("from 4.7.0, use `set` taking option from zmq::sockopt")
void setsockopt(int option_, const void *optval_, size_t optvallen_)
{
int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_);
@ -1288,6 +1587,7 @@ class socket_base
throw error_t();
}
ZMQ_CPP11_DEPRECATED("from 4.7.0, use `get` taking option from zmq::sockopt")
void getsockopt(int option_, void *optval_, size_t *optvallen_) const
{
int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_);
@ -1295,7 +1595,9 @@ class socket_base
throw error_t();
}
template<typename T> T getsockopt(int option_) const
template<typename T>
ZMQ_CPP11_DEPRECATED("from 4.7.0, use `get` taking option from zmq::sockopt")
T getsockopt(int option_) const
{
T optval;
size_t optlen = sizeof(T);
@ -1303,6 +1605,114 @@ class socket_base
return optval;
}
#ifdef ZMQ_CPP11
// Set integral socket option, e.g.
// `socket.set(zmq::sockopt::linger, 0)`
template<int Opt, class T, bool BoolUnit>
void set(sockopt::integral_option<Opt, T, BoolUnit>, const T &val)
{
static_assert(std::is_integral<T>::value, "T must be integral");
set_option(Opt, &val, sizeof val);
}
// Set integral socket option from boolean, e.g.
// `socket.set(zmq::sockopt::immediate, false)`
template<int Opt, class T>
void set(sockopt::integral_option<Opt, T, true>, bool val)
{
static_assert(std::is_integral<T>::value, "T must be integral");
T rep_val = val;
set_option(Opt, &rep_val, sizeof rep_val);
}
// Set array socket option, e.g.
// `socket.set(zmq::sockopt::plain_username, "foo123")`
template<int Opt, int NullTerm>
void set(sockopt::array_option<Opt, NullTerm>, const char *buf)
{
set_option(Opt, buf, std::strlen(buf));
}
// Set array socket option, e.g.
// `socket.set(zmq::sockopt::routing_id, zmq::buffer(id))`
template<int Opt, int NullTerm>
void set(sockopt::array_option<Opt, NullTerm>, const_buffer buf)
{
set_option(Opt, buf.data(), buf.size());
}
// Set array socket option, e.g.
// `socket.set(zmq::sockopt::routing_id, id_str)`
template<int Opt, int NullTerm>
void set(sockopt::array_option<Opt, NullTerm>, const std::string &buf)
{
set_option(Opt, buf.data(), buf.size());
}
#ifdef ZMQ_CPP17
// Set array socket option, e.g.
// `socket.set(zmq::sockopt::routing_id, id_str)`
template<int Opt, int NullTerm>
void set(sockopt::array_option<Opt, NullTerm>, std::string_view buf)
{
set_option(Opt, buf.data(), buf.size());
}
#endif
// Get scalar socket option, e.g.
// `auto opt = socket.get(zmq::sockopt::linger)`
template<int Opt, class T, bool BoolUnit>
ZMQ_NODISCARD T get(sockopt::integral_option<Opt, T, BoolUnit>) const
{
static_assert(std::is_integral<T>::value, "T must be integral");
T val;
size_t size = sizeof val;
get_option(Opt, &val, &size);
assert(size == sizeof val);
return val;
}
// Get array socket option, writes to buf, returns option size in bytes, e.g.
// `size_t optsize = socket.get(zmq::sockopt::routing_id, zmq::buffer(id))`
template<int Opt, int NullTerm>
ZMQ_NODISCARD size_t get(sockopt::array_option<Opt, NullTerm>,
mutable_buffer buf) const
{
size_t size = buf.size();
get_option(Opt, buf.data(), &size);
return size;
}
// Get array socket option as string (initializes the string buffer size to init_size) e.g.
// `auto s = socket.get(zmq::sockopt::routing_id)`
// Note: removes the null character from null-terminated string options,
// i.e. the string size excludes the null character.
template<int Opt, int NullTerm>
ZMQ_NODISCARD std::string get(sockopt::array_option<Opt, NullTerm>,
size_t init_size = 1024) const
{
if (NullTerm == 2 && init_size == 1024) {
init_size = 41; // get as Z85 string
}
std::string str(init_size, '\0');
size_t size = get(sockopt::array_option<Opt>{}, buffer(str));
if (NullTerm == 1) {
if (size > 0) {
assert(str[size - 1] == '\0');
--size;
}
} else if (NullTerm == 2) {
assert(size == 32 || size == 41);
if (size == 41) {
assert(str[size - 1] == '\0');
--size;
}
}
str.resize(size);
return str;
}
#endif
void bind(std::string const &addr) { bind(addr.c_str()); }
void bind(const char *addr_)
@ -1502,6 +1912,21 @@ class socket_base
protected:
void *_handle;
private:
void set_option(int option_, const void *optval_, size_t optvallen_)
{
int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_);
if (rc != 0)
throw error_t();
}
void get_option(int option_, void *optval_, size_t *optvallen_) const
{
int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_);
if (rc != 0)
throw error_t();
}
};
} // namespace detail