zmq_addon.hpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 2016-2017 ZeroMQ community
3  Copyright (c) 2016 VOCA AS / Harald Nøkland
4 
5  Permission is hereby granted, free of charge, to any person obtaining a copy
6  of this software and associated documentation files (the "Software"), to
7  deal in the Software without restriction, including without limitation the
8  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  sell copies of the Software, and to permit persons to whom the Software is
10  furnished to do so, subject to the following conditions:
11 
12  The above copyright notice and this permission notice shall be included in
13  all copies or substantial portions of the Software.
14 
15  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  IN THE SOFTWARE.
22 */
23 
24 #ifndef __ZMQ_ADDON_HPP_INCLUDED__
25 #define __ZMQ_ADDON_HPP_INCLUDED__
26 
27 #include "zmq.hpp"
28 
29 #include <deque>
30 #include <iomanip>
31 #include <sstream>
32 #include <stdexcept>
33 #ifdef ZMQ_CPP11
34 #include <limits>
35 #include <functional>
36 #include <unordered_map>
37 #endif
38 
39 namespace zmq
40 {
41 #ifdef ZMQ_CPP11
42 
43 namespace detail
44 {
45 template<bool CheckN, class OutputIt>
46 recv_result_t
47 recv_multipart_n(socket_ref s, OutputIt out, size_t n, recv_flags flags)
48 {
49  size_t msg_count = 0;
50  message_t msg;
51  while (true) {
52  if ZMQ_CONSTEXPR_IF (CheckN) {
53  if (msg_count >= n)
54  throw std::runtime_error(
55  "Too many message parts in recv_multipart_n");
56  }
57  if (!s.recv(msg, flags)) {
58  // zmq ensures atomic delivery of messages
59  assert(msg_count == 0);
60  return {};
61  }
62  ++msg_count;
63  const bool more = msg.more();
64  *out++ = std::move(msg);
65  if (!more)
66  break;
67  }
68  return msg_count;
69 }
70 
71 inline bool is_little_endian()
72 {
73  const uint16_t i = 0x01;
74  return *reinterpret_cast<const uint8_t *>(&i) == 0x01;
75 }
76 
77 inline void write_network_order(unsigned char *buf, const uint32_t value)
78 {
79  if (is_little_endian()) {
80  ZMQ_CONSTEXPR_VAR uint32_t mask = std::numeric_limits<std::uint8_t>::max();
81  *buf++ = static_cast<unsigned char>((value >> 24) & mask);
82  *buf++ = static_cast<unsigned char>((value >> 16) & mask);
83  *buf++ = static_cast<unsigned char>((value >> 8) & mask);
84  *buf++ = static_cast<unsigned char>(value & mask);
85  } else {
86  std::memcpy(buf, &value, sizeof(value));
87  }
88 }
89 
90 inline uint32_t read_u32_network_order(const unsigned char *buf)
91 {
92  if (is_little_endian()) {
93  return (static_cast<uint32_t>(buf[0]) << 24)
94  + (static_cast<uint32_t>(buf[1]) << 16)
95  + (static_cast<uint32_t>(buf[2]) << 8)
96  + static_cast<uint32_t>(buf[3]);
97  } else {
98  uint32_t value;
99  std::memcpy(&value, buf, sizeof(value));
100  return value;
101  }
102 }
103 } // namespace detail
104 
105 /* Receive a multipart message.
106 
107  Writes the zmq::message_t objects to OutputIterator out.
108  The out iterator must handle an unspecified number of writes,
109  e.g. by using std::back_inserter.
110 
111  Returns: the number of messages received or nullopt (on EAGAIN).
112  Throws: if recv throws. Any exceptions thrown
113  by the out iterator will be propagated and the message
114  may have been only partially received with pending
115  message parts. It is adviced to close this socket in that event.
116 */
117 template<class OutputIt>
118 ZMQ_NODISCARD recv_result_t recv_multipart(socket_ref s,
119  OutputIt out,
120  recv_flags flags = recv_flags::none)
121 {
122  return detail::recv_multipart_n<false>(s, std::move(out), 0, flags);
123 }
124 
125 /* Receive a multipart message.
126 
127  Writes at most n zmq::message_t objects to OutputIterator out.
128  If the number of message parts of the incoming message exceeds n
129  then an exception will be thrown.
130 
131  Returns: the number of messages received or nullopt (on EAGAIN).
132  Throws: if recv throws. Throws std::runtime_error if the number
133  of message parts exceeds n (exactly n messages will have been written
134  to out). Any exceptions thrown
135  by the out iterator will be propagated and the message
136  may have been only partially received with pending
137  message parts. It is adviced to close this socket in that event.
138 */
139 template<class OutputIt>
140 ZMQ_NODISCARD recv_result_t recv_multipart_n(socket_ref s,
141  OutputIt out,
142  size_t n,
143  recv_flags flags = recv_flags::none)
144 {
145  return detail::recv_multipart_n<true>(s, std::move(out), n, flags);
146 }
147 
148 /* Send a multipart message.
149 
150  The range must be a ForwardRange of zmq::message_t,
151  zmq::const_buffer or zmq::mutable_buffer.
152  The flags may be zmq::send_flags::sndmore if there are
153  more message parts to be sent after the call to this function.
154 
155  Returns: the number of messages sent (exactly msgs.size()) or nullopt (on EAGAIN).
156  Throws: if send throws. Any exceptions thrown
157  by the msgs range will be propagated and the message
158  may have been only partially sent. It is adviced to close this socket in that event.
159 */
160 template<class Range
161 #ifndef ZMQ_CPP11_PARTIAL
162  ,
163  typename = typename std::enable_if<
165  && (std::is_same<detail::range_value_t<Range>, message_t>::value
166  || detail::is_buffer<detail::range_value_t<Range>>::value)>::type
167 #endif
168  >
169 send_result_t
170 send_multipart(socket_ref s, Range &&msgs, send_flags flags = send_flags::none)
171 {
172  using std::begin;
173  using std::end;
174  auto it = begin(msgs);
175  const auto end_it = end(msgs);
176  size_t msg_count = 0;
177  while (it != end_it) {
178  const auto next = std::next(it);
179  const auto msg_flags =
180  flags | (next == end_it ? send_flags::none : send_flags::sndmore);
181  if (!s.send(*it, msg_flags)) {
182  // zmq ensures atomic delivery of messages
183  assert(it == begin(msgs));
184  return {};
185  }
186  ++msg_count;
187  it = next;
188  }
189  return msg_count;
190 }
191 
192 /* Encode a multipart message.
193 
194  The range must be a ForwardRange of zmq::message_t. A
195  zmq::multipart_t or STL container may be passed for encoding.
196 
197  Returns: a zmq::message_t holding the encoded multipart data.
198 
199  Throws: std::range_error is thrown if the size of any single part
200  can not fit in an unsigned 32 bit integer.
201 
202  The encoding is compatible with that used by the CZMQ function
203  zmsg_encode(), see https://rfc.zeromq.org/spec/50/.
204  Each part consists of a size followed by the data.
205  These are placed contiguously into the output message. A part of
206  size less than 255 bytes will have a single byte size value.
207  Larger parts will have a five byte size value with the first byte
208  set to 0xFF and the remaining four bytes holding the size of the
209  part's data.
210 */
211 template<class Range
212 #ifndef ZMQ_CPP11_PARTIAL
213  ,
214  typename = typename std::enable_if<
216  && (std::is_same<detail::range_value_t<Range>, message_t>::value
217  || detail::is_buffer<detail::range_value_t<Range>>::value)>::type
218 #endif
219  >
220 message_t encode(const Range &parts)
221 {
222  size_t mmsg_size = 0;
223 
224  // First pass check sizes
225  for (const auto &part : parts) {
226  const size_t part_size = part.size();
227  if (part_size > std::numeric_limits<std::uint32_t>::max()) {
228  // Size value must fit into uint32_t.
229  throw std::range_error("Invalid size, message part too large");
230  }
231  const size_t count_size =
232  part_size < std::numeric_limits<std::uint8_t>::max() ? 1 : 5;
233  mmsg_size += part_size + count_size;
234  }
235 
236  message_t encoded(mmsg_size);
237  unsigned char *buf = encoded.data<unsigned char>();
238  for (const auto &part : parts) {
239  const uint32_t part_size = static_cast<uint32_t>(part.size());
240  const unsigned char *part_data =
241  static_cast<const unsigned char *>(part.data());
242 
243  if (part_size < std::numeric_limits<std::uint8_t>::max()) {
244  // small part
245  *buf++ = (unsigned char) part_size;
246  } else {
247  // big part
248  *buf++ = std::numeric_limits<uint8_t>::max();
249  detail::write_network_order(buf, part_size);
250  buf += sizeof(part_size);
251  }
252  std::memcpy(buf, part_data, part_size);
253  buf += part_size;
254  }
255 
256  assert(static_cast<size_t>(buf - encoded.data<unsigned char>()) == mmsg_size);
257  return encoded;
258 }
259 
260 /* Decode an encoded message to multiple parts.
261 
262  The given output iterator must be a ForwardIterator to a container
263  holding zmq::message_t such as a zmq::multipart_t or various STL
264  containers.
265 
266  Returns the ForwardIterator advanced once past the last decoded
267  part.
268 
269  Throws: a std::out_of_range is thrown if the encoded part sizes
270  lead to exceeding the message data bounds.
271 
272  The decoding assumes the message is encoded in the manner
273  performed by zmq::encode(), see https://rfc.zeromq.org/spec/50/.
274  */
275 template<class OutputIt> OutputIt decode(const message_t &encoded, OutputIt out)
276 {
277  const unsigned char *source = encoded.data<unsigned char>();
278  const unsigned char *const limit = source + encoded.size();
279 
280  while (source < limit) {
281  size_t part_size = *source++;
282  if (part_size == std::numeric_limits<std::uint8_t>::max()) {
283  if (static_cast<size_t>(limit - source) < sizeof(uint32_t)) {
284  throw std::out_of_range(
285  "Malformed encoding, overflow in reading size");
286  }
287  part_size = detail::read_u32_network_order(source);
288  // the part size is allowed to be less than 0xFF
289  source += sizeof(uint32_t);
290  }
291 
292  if (static_cast<size_t>(limit - source) < part_size) {
293  throw std::out_of_range("Malformed encoding, overflow in reading part");
294  }
295  *out = message_t(source, part_size);
296  ++out;
297  source += part_size;
298  }
299 
300  assert(source == limit);
301  return out;
302 }
303 
304 #endif
305 
306 
307 #ifdef ZMQ_HAS_RVALUE_REFS
308 
309 /*
310  This class handles multipart messaging. It is the C++ equivalent of zmsg.h,
311  which is part of CZMQ (the high-level C binding). Furthermore, it is a major
312  improvement compared to zmsg.hpp, which is part of the examples in the ØMQ
313  Guide. Unnecessary copying is avoided by using move semantics to efficiently
314  add/remove parts.
315 */
316 class multipart_t
317 {
318  private:
319  std::deque<message_t> m_parts;
320 
321  public:
322  typedef std::deque<message_t>::value_type value_type;
323 
324  typedef std::deque<message_t>::iterator iterator;
325  typedef std::deque<message_t>::const_iterator const_iterator;
326 
327  typedef std::deque<message_t>::reverse_iterator reverse_iterator;
328  typedef std::deque<message_t>::const_reverse_iterator const_reverse_iterator;
329 
330  // Default constructor
331  multipart_t() {}
332 
333  // Construct from socket receive
334  multipart_t(socket_ref socket) { recv(socket); }
335 
336  // Construct from memory block
337  multipart_t(const void *src, size_t size) { addmem(src, size); }
338 
339  // Construct from string
340  multipart_t(const std::string &string) { addstr(string); }
341 
342  // Construct from message part
343  multipart_t(message_t &&message) { add(std::move(message)); }
344 
345  // Move constructor
346  multipart_t(multipart_t &&other) { m_parts = std::move(other.m_parts); }
347 
348  // Move assignment operator
349  multipart_t &operator=(multipart_t &&other)
350  {
351  m_parts = std::move(other.m_parts);
352  return *this;
353  }
354 
355  // Destructor
356  virtual ~multipart_t() { clear(); }
357 
358  message_t &operator[](size_t n) { return m_parts[n]; }
359 
360  const message_t &operator[](size_t n) const { return m_parts[n]; }
361 
362  message_t &at(size_t n) { return m_parts.at(n); }
363 
364  const message_t &at(size_t n) const { return m_parts.at(n); }
365 
366  iterator begin() { return m_parts.begin(); }
367 
368  const_iterator begin() const { return m_parts.begin(); }
369 
370  const_iterator cbegin() const { return m_parts.cbegin(); }
371 
372  reverse_iterator rbegin() { return m_parts.rbegin(); }
373 
374  const_reverse_iterator rbegin() const { return m_parts.rbegin(); }
375 
376  iterator end() { return m_parts.end(); }
377 
378  const_iterator end() const { return m_parts.end(); }
379 
380  const_iterator cend() const { return m_parts.cend(); }
381 
382  reverse_iterator rend() { return m_parts.rend(); }
383 
384  const_reverse_iterator rend() const { return m_parts.rend(); }
385 
386  // Delete all parts
387  void clear() { m_parts.clear(); }
388 
389  // Get number of parts
390  size_t size() const { return m_parts.size(); }
391 
392  // Check if number of parts is zero
393  bool empty() const { return m_parts.empty(); }
394 
395  // Receive multipart message from socket
396  bool recv(socket_ref socket, int flags = 0)
397  {
398  clear();
399  bool more = true;
400  while (more) {
401  message_t message;
402 #ifdef ZMQ_CPP11
403  if (!socket.recv(message, static_cast<recv_flags>(flags)))
404  return false;
405 #else
406  if (!socket.recv(&message, flags))
407  return false;
408 #endif
409  more = message.more();
410  add(std::move(message));
411  }
412  return true;
413  }
414 
415  // Send multipart message to socket
416  bool send(socket_ref socket, int flags = 0)
417  {
418  flags &= ~(ZMQ_SNDMORE);
419  bool more = size() > 0;
420  while (more) {
421  message_t message = pop();
422  more = size() > 0;
423 #ifdef ZMQ_CPP11
424  if (!socket.send(message, static_cast<send_flags>(
425  (more ? ZMQ_SNDMORE : 0) | flags)))
426  return false;
427 #else
428  if (!socket.send(message, (more ? ZMQ_SNDMORE : 0) | flags))
429  return false;
430 #endif
431  }
432  clear();
433  return true;
434  }
435 
436  // Concatenate other multipart to front
437  void prepend(multipart_t &&other)
438  {
439  while (!other.empty())
440  push(other.remove());
441  }
442 
443  // Concatenate other multipart to back
444  void append(multipart_t &&other)
445  {
446  while (!other.empty())
447  add(other.pop());
448  }
449 
450  // Push memory block to front
451  void pushmem(const void *src, size_t size)
452  {
453  m_parts.push_front(message_t(src, size));
454  }
455 
456  // Push memory block to back
457  void addmem(const void *src, size_t size)
458  {
459  m_parts.push_back(message_t(src, size));
460  }
461 
462  // Push string to front
463  void pushstr(const std::string &string)
464  {
465  m_parts.push_front(message_t(string.data(), string.size()));
466  }
467 
468  // Push string to back
469  void addstr(const std::string &string)
470  {
471  m_parts.push_back(message_t(string.data(), string.size()));
472  }
473 
474  // Push type (fixed-size) to front
475  template<typename T> void pushtyp(const T &type)
476  {
478  "Use pushstr() instead of pushtyp<std::string>()");
479  m_parts.push_front(message_t(&type, sizeof(type)));
480  }
481 
482  // Push type (fixed-size) to back
483  template<typename T> void addtyp(const T &type)
484  {
486  "Use addstr() instead of addtyp<std::string>()");
487  m_parts.push_back(message_t(&type, sizeof(type)));
488  }
489 
490  // Push message part to front
491  void push(message_t &&message) { m_parts.push_front(std::move(message)); }
492 
493  // Push message part to back
494  void add(message_t &&message) { m_parts.push_back(std::move(message)); }
495 
496  // Alias to allow std::back_inserter()
497  void push_back(message_t &&message) { m_parts.push_back(std::move(message)); }
498 
499  // Pop string from front
500  std::string popstr()
501  {
502  std::string string(m_parts.front().data<char>(), m_parts.front().size());
503  m_parts.pop_front();
504  return string;
505  }
506 
507  // Pop type (fixed-size) from front
508  template<typename T> T poptyp()
509  {
511  "Use popstr() instead of poptyp<std::string>()");
512  if (sizeof(T) != m_parts.front().size())
513  throw std::runtime_error(
514  "Invalid type, size does not match the message size");
515  T type = *m_parts.front().data<T>();
516  m_parts.pop_front();
517  return type;
518  }
519 
520  // Pop message part from front
521  message_t pop()
522  {
523  message_t message = std::move(m_parts.front());
524  m_parts.pop_front();
525  return message;
526  }
527 
528  // Pop message part from back
529  message_t remove()
530  {
531  message_t message = std::move(m_parts.back());
532  m_parts.pop_back();
533  return message;
534  }
535 
536  // get message part from front
537  const message_t &front() { return m_parts.front(); }
538 
539  // get message part from back
540  const message_t &back() { return m_parts.back(); }
541 
542  // Get pointer to a specific message part
543  const message_t *peek(size_t index) const { return &m_parts[index]; }
544 
545  // Get a string copy of a specific message part
546  std::string peekstr(size_t index) const
547  {
548  std::string string(m_parts[index].data<char>(), m_parts[index].size());
549  return string;
550  }
551 
552  // Peek type (fixed-size) from front
553  template<typename T> T peektyp(size_t index) const
554  {
556  "Use peekstr() instead of peektyp<std::string>()");
557  if (sizeof(T) != m_parts[index].size())
558  throw std::runtime_error(
559  "Invalid type, size does not match the message size");
560  T type = *m_parts[index].data<T>();
561  return type;
562  }
563 
564  // Create multipart from type (fixed-size)
565  template<typename T> static multipart_t create(const T &type)
566  {
567  multipart_t multipart;
568  multipart.addtyp(type);
569  return multipart;
570  }
571 
572  // Copy multipart
573  multipart_t clone() const
574  {
575  multipart_t multipart;
576  for (size_t i = 0; i < size(); i++)
577  multipart.addmem(m_parts[i].data(), m_parts[i].size());
578  return multipart;
579  }
580 
581  // Dump content to string
582  std::string str() const
583  {
584  std::stringstream ss;
585  for (size_t i = 0; i < m_parts.size(); i++) {
586  const unsigned char *data = m_parts[i].data<unsigned char>();
587  size_t size = m_parts[i].size();
588 
589  // Dump the message as text or binary
590  bool isText = true;
591  for (size_t j = 0; j < size; j++) {
592  if (data[j] < 32 || data[j] > 127) {
593  isText = false;
594  break;
595  }
596  }
597  ss << "\n[" << std::dec << std::setw(3) << std::setfill('0') << size
598  << "] ";
599  if (size >= 1000) {
600  ss << "... (too big to print)";
601  continue;
602  }
603  for (size_t j = 0; j < size; j++) {
604  if (isText)
605  ss << static_cast<char>(data[j]);
606  else
607  ss << std::hex << std::setw(2) << std::setfill('0')
608  << static_cast<short>(data[j]);
609  }
610  }
611  return ss.str();
612  }
613 
614  // Check if equal to other multipart
615  bool equal(const multipart_t *other) const ZMQ_NOTHROW
616  {
617  return *this == *other;
618  }
619 
620  bool operator==(const multipart_t &other) const ZMQ_NOTHROW
621  {
622  if (size() != other.size())
623  return false;
624  for (size_t i = 0; i < size(); i++)
625  if (at(i) != other.at(i))
626  return false;
627  return true;
628  }
629 
630  bool operator!=(const multipart_t &other) const ZMQ_NOTHROW
631  {
632  return !(*this == other);
633  }
634 
635 #ifdef ZMQ_CPP11
636 
637  // Return single part message_t encoded from this multipart_t.
638  message_t encode() const { return zmq::encode(*this); }
639 
640  // Decode encoded message into multiple parts and append to self.
641  void decode_append(const message_t &encoded)
642  {
643  zmq::decode(encoded, std::back_inserter(*this));
644  }
645 
646  // Return a new multipart_t containing the decoded message_t.
647  static multipart_t decode(const message_t &encoded)
648  {
649  multipart_t tmp;
650  zmq::decode(encoded, std::back_inserter(tmp));
651  return tmp;
652  }
653 
654 #endif
655 
656  private:
657  // Disable implicit copying (moving is more efficient)
658  multipart_t(const multipart_t &other) ZMQ_DELETED_FUNCTION;
659  void operator=(const multipart_t &other) ZMQ_DELETED_FUNCTION;
660 }; // class multipart_t
661 
662 inline std::ostream &operator<<(std::ostream &os, const multipart_t &msg)
663 {
664  return os << msg.str();
665 }
666 
667 #endif // ZMQ_HAS_RVALUE_REFS
668 
669 #if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER)
670 class active_poller_t
671 {
672  public:
673  active_poller_t() = default;
674  ~active_poller_t() = default;
675 
676  active_poller_t(const active_poller_t &) = delete;
677  active_poller_t &operator=(const active_poller_t &) = delete;
678 
679  active_poller_t(active_poller_t &&src) = default;
680  active_poller_t &operator=(active_poller_t &&src) = default;
681 
682  using handler_type = std::function<void(event_flags)>;
683 
684  void add(zmq::socket_ref socket, event_flags events, handler_type handler)
685  {
686  if (!handler)
687  throw std::invalid_argument("null handler in active_poller_t::add");
688  auto ret = handlers.emplace(
689  socket, std::make_shared<handler_type>(std::move(handler)));
690  if (!ret.second)
691  throw error_t(EINVAL); // already added
692  try {
693  base_poller.add(socket, events, ret.first->second.get());
694  need_rebuild = true;
695  }
696  catch (...) {
697  // rollback
698  handlers.erase(socket);
699  throw;
700  }
701  }
702 
703  void remove(zmq::socket_ref socket)
704  {
705  base_poller.remove(socket);
706  handlers.erase(socket);
707  need_rebuild = true;
708  }
709 
710  void modify(zmq::socket_ref socket, event_flags events)
711  {
712  base_poller.modify(socket, events);
713  }
714 
715  size_t wait(std::chrono::milliseconds timeout)
716  {
717  if (need_rebuild) {
718  poller_events.resize(handlers.size());
719  poller_handlers.clear();
720  poller_handlers.reserve(handlers.size());
721  for (const auto &handler : handlers) {
722  poller_handlers.push_back(handler.second);
723  }
724  need_rebuild = false;
725  }
726  const auto count = base_poller.wait_all(poller_events, timeout);
727  std::for_each(poller_events.begin(),
728  poller_events.begin() + static_cast<ptrdiff_t>(count),
729  [](decltype(base_poller)::event_type &event) {
730  assert(event.user_data != nullptr);
731  (*event.user_data)(event.events);
732  });
733  return count;
734  }
735 
736  ZMQ_NODISCARD bool empty() const noexcept { return handlers.empty(); }
737 
738  size_t size() const noexcept { return handlers.size(); }
739 
740  private:
741  bool need_rebuild{false};
742 
743  poller_t<handler_type> base_poller{};
744  std::unordered_map<socket_ref, std::shared_ptr<handler_type>> handlers{};
745  std::vector<decltype(base_poller)::event_type> poller_events{};
746  std::vector<std::shared_ptr<handler_type>> poller_handlers{};
747 }; // class active_poller_t
748 #endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER)
749 
750 
751 } // namespace zmq
752 
753 #endif // __ZMQ_ADDON_HPP_INCLUDED__
cx::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: wildcards.hpp:636
ZMQ_CONSTEXPR_VAR
#define ZMQ_CONSTEXPR_VAR
Definition: 3rdparty/cppzmq/zmq.hpp:94
BT::enable_if
typename std::enable_if< Predicate::value >::type * enable_if
Definition: basic_types.h:305
lexyd::peek
constexpr auto peek(Rule)
Definition: peek.hpp:166
detail::is_range
Definition: json.hpp:3725
cx::cbegin
constexpr auto cbegin(const C &c) -> decltype(cx::begin(c))
Definition: wildcards.hpp:681
ZMQ_NOTHROW
#define ZMQ_NOTHROW
Definition: 3rdparty/cppzmq/zmq.hpp:89
cx::empty
constexpr auto empty(const C &c) -> decltype(c.empty())
Definition: wildcards.hpp:646
detail
detail namespace with internal helper functions
Definition: json.hpp:248
lexy::_detail::next
constexpr Iterator next(Iterator iter)
Definition: iterator.hpp:38
lexyd::hex
_d< 16 > hex
Definition: digit.hpp:237
magic_enum::detail::value
constexpr E value(std::size_t i) noexcept
Definition: magic_enum.hpp:664
ZMQ_NODISCARD
#define ZMQ_NODISCARD
Definition: 3rdparty/cppzmq/zmq.hpp:77
zmq::operator==
bool operator==(const detail::socket_base &a, const detail::socket_base &b) ZMQ_NOTHROW
Definition: 3rdparty/cppzmq/zmq.hpp:2113
zmq::socket_ref
Definition: 3rdparty/cppzmq/zmq.hpp:2081
cx::end
constexpr auto end(const C &c) -> decltype(c.end())
Definition: wildcards.hpp:686
detail::void
j template void())
Definition: json.hpp:4893
zmq
Definition: 3rdparty/cppzmq/zmq.hpp:228
lexy::count
constexpr auto count
Sink that counts all arguments.
Definition: fold.hpp:88
ZMQ_CONSTEXPR_IF
#define ZMQ_CONSTEXPR_IF
Definition: 3rdparty/cppzmq/zmq.hpp:105
zmq::operator<<
std::ostream & operator<<(std::ostream &os, const message_t &msg)
Definition: 3rdparty/cppzmq/zmq.hpp:2709
cx::cend
constexpr auto cend(const C &c) -> decltype(cx::end(c))
Definition: wildcards.hpp:706
detail::value_t::string
@ string
string value
ZMQ_DELETED_FUNCTION
#define ZMQ_DELETED_FUNCTION
Definition: 3rdparty/cppzmq/zmq.hpp:179
magic_enum::detail::n
constexpr auto n() noexcept
Definition: magic_enum.hpp:417
cx::equal
constexpr bool equal(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Definition: wildcards.hpp:1640
cx::begin
constexpr auto begin(const C &c) -> decltype(c.begin())
Definition: wildcards.hpp:661
zmq.hpp
zmq::operator!=
bool operator!=(const detail::socket_base &a, const detail::socket_base &b) ZMQ_NOTHROW
Definition: 3rdparty/cppzmq/zmq.hpp:2117
lexyd::flags
constexpr auto flags(_sym< Table, Token, Tag > flag_rule)
Definition: flags.hpp:83


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Jun 28 2024 02:20:08