CppWAMP
C++11 client library for the WAMP protocol
corounpacker.hpp
Go to the documentation of this file.
1 /*------------------------------------------------------------------------------
2  Copyright Butterfly Energy Systems 2014-2015, 2022.
3  Distributed under the Boost Software License, Version 1.0.
4  http://www.boost.org/LICENSE_1_0.txt
5 ------------------------------------------------------------------------------*/
6 
7 #ifndef CPPWAMP_COROUNPACKER_HPP
8 #define CPPWAMP_COROUNPACKER_HPP
9 
10 //------------------------------------------------------------------------------
14 //------------------------------------------------------------------------------
15 
16 #include <boost/asio/spawn.hpp>
17 #include "config.hpp"
18 #include "unpacker.hpp"
19 #include "internal/callee.hpp"
20 
21 namespace wamp
22 {
23 
24 //------------------------------------------------------------------------------
37 //------------------------------------------------------------------------------
38 template <typename TSlot, typename... TArgs>
40 {
41 public:
43  using Slot = TSlot;
44 
46  explicit CoroEventUnpacker(Slot slot);
47 
52  void operator()(Event event) const;
53 
54 private:
55  using Yield = boost::asio::yield_context;
56 
57  template <int ...S>
58  void invoke(Event&& event, internal::IntegerSequence<S...>) const;
59 
60  Slot slot_;
61 };
62 
63 //------------------------------------------------------------------------------
76 //------------------------------------------------------------------------------
77 template <typename... TArgs, typename TSlot>
78 CoroEventUnpacker<DecayedSlot<TSlot>, TArgs...> unpackedCoroEvent(TSlot&& slot);
79 
80 
81 //------------------------------------------------------------------------------
95 //------------------------------------------------------------------------------
96 template <typename TSlot, typename... TArgs>
98 {
99 public:
101  using Slot = TSlot;
102 
104  explicit SimpleCoroEventUnpacker(Slot slot);
105 
110  void operator()(Event event) const;
111 
112 private:
113  using Yield = boost::asio::yield_context;
114 
115  template <int ...S>
116  void invoke(Event&& event, internal::IntegerSequence<S...>) const;
117 
118  Slot slot_;
119 };
120 
121 //------------------------------------------------------------------------------
124 //------------------------------------------------------------------------------
125 template <typename TSlot, typename... TArgs>
126 using BasicCoroEventUnpacker CPPWAMP_DEPRECATED
127  = SimpleCoroEventUnpacker<TSlot, TArgs...>;
128 
129 //------------------------------------------------------------------------------
141 //------------------------------------------------------------------------------
142 template <typename... TArgs, typename TSlot>
144 simpleCoroEvent(TSlot&& slot);
145 
146 //------------------------------------------------------------------------------
148 //------------------------------------------------------------------------------
149 template <typename... TArgs, typename TSlot>
151 basicCoroEvent(TSlot&& slot);
152 
153 
154 //------------------------------------------------------------------------------
167 //------------------------------------------------------------------------------
168 template <typename TSlot, typename... TArgs>
170 {
171 public:
173  using Slot = TSlot;
174 
176  explicit CoroInvocationUnpacker(Slot slot);
177 
182  Outcome operator()(Invocation inv) const;
183 
184 private:
185  using Yield = boost::asio::yield_context;
186 
187  template <int ...S>
188  void invoke(Invocation&& inv, internal::IntegerSequence<S...>) const;
189 
190  Slot slot_;
191 };
192 
193 //------------------------------------------------------------------------------
204 //------------------------------------------------------------------------------
205 template <typename... TArgs, typename TSlot>
207 unpackedCoroRpc(TSlot&& slot);
208 
209 
210 //------------------------------------------------------------------------------
227 //------------------------------------------------------------------------------
228 template <typename TSlot, typename TResult, typename... TArgs>
230 {
231 public:
233  using Slot = TSlot;
234 
236  using ResultType = TResult;
237 
239  explicit SimpleCoroInvocationUnpacker(Slot slot);
240 
245  Outcome operator()(Invocation inv) const;
246 
247 private:
248  using Yield = boost::asio::yield_context;
249 
250  template <int ...S>
251  void invoke(TrueType, Invocation&& inv,
252  internal::IntegerSequence<S...>) const;
253 
254  template <int ...S>
255  void invoke(FalseType, Invocation&& inv,
256  internal::IntegerSequence<S...>) const;
257 
258  Slot slot_;
259 };
260 
261 //------------------------------------------------------------------------------
264 //------------------------------------------------------------------------------
265 template <typename TSlot, typename TResult, typename... TArgs>
267  SimpleCoroInvocationUnpacker<TSlot, TResult, TArgs...>;
268 
269 //------------------------------------------------------------------------------
282 //------------------------------------------------------------------------------
283 template <typename TResult, typename... TArgs, typename TSlot>
285 simpleCoroRpc(TSlot&& slot);
286 
287 //------------------------------------------------------------------------------
289 //------------------------------------------------------------------------------
290 template <typename TResult, typename... TArgs, typename TSlot>
292 basicCoroRpc(TSlot&& slot);
293 
294 //******************************************************************************
295 // Internal helper types
296 //******************************************************************************
297 
298 namespace internal
299 {
300 
301 //------------------------------------------------------------------------------
302 // This is caught internally by Client while dispatching RPCs and is never
303 // propagated through the API.
304 //------------------------------------------------------------------------------
305 struct UnpackCoroError : public Error
306 {
307  UnpackCoroError() : Error("wamp.error.invalid_argument") {}
308 };
309 
310 } // namespace internal
311 
312 
313 //******************************************************************************
314 // CoroEventUnpacker implementation
315 //******************************************************************************
316 
317 //------------------------------------------------------------------------------
318 template <typename S, typename... A>
320  : slot_(std::move(slot))
321 {}
322 
323 //------------------------------------------------------------------------------
324 template <typename S, typename... A>
326 {
327  if (event.args().size() < sizeof...(A))
328  {
329  std::ostringstream oss;
330  oss << "Expected " << sizeof...(A)
331  << " args, but only got " << event.args().size();
332  throw internal::UnpackCoroError().withArgs(oss.str());
333  }
334 
335  // Use the integer parameter pack technique shown in
336  // http://stackoverflow.com/a/7858971/245265
337  using Seq = typename internal::GenIntegerSequence<sizeof...(A)>::type;
338  invoke(std::move(event), Seq());
339 }
340 
341 //------------------------------------------------------------------------------
342 template <typename S, typename... A>
343 template <int ...Seq>
345  internal::IntegerSequence<Seq...>) const
346 {
347  struct Spawned
348  {
349  Slot slot;
350  Event event;
351 
352  void operator()(Yield yield)
353  {
354  std::tuple<ValueTypeOf<A>...> args;
355  try
356  {
357  event.convertToTuple(args);
358  slot(std::move(event), std::get<Seq>(std::move(args))...,
359  yield);
360  }
361  catch (const error::BadType&)
362  {
363  /* Do nothing. This is to prevent the publisher crashing
364  subscribers when it passes Variant objects having
365  incorrect schema. */
366  }
367  }
368  };
369 
370  auto executor =
371  boost::asio::get_associated_executor(slot_, event.executor());
372  boost::asio::spawn(executor, Spawned{slot_, std::move(event)});
373 }
374 
375 //------------------------------------------------------------------------------
376 template <typename... TArgs, typename TSlot>
377 CoroEventUnpacker<DecayedSlot<TSlot>, TArgs...> unpackedCoroEvent(TSlot&& slot)
378 {
379  return CoroEventUnpacker<DecayedSlot<TSlot>, TArgs...>(
380  std::forward<TSlot>(slot));
381 }
382 
383 
384 //******************************************************************************
385 // SimpleCoroEventUnpacker implementation
386 //******************************************************************************
387 
388 //------------------------------------------------------------------------------
389 template <typename S, typename... A>
391  : slot_(std::move(slot))
392 {}
393 
394 //------------------------------------------------------------------------------
395 template <typename S, typename... A>
397 {
398  if (event.args().size() < sizeof...(A))
399  {
400  std::ostringstream oss;
401  oss << "Expected " << sizeof...(A)
402  << " args, but only got " << event.args().size();
403  throw internal::UnpackCoroError().withArgs(oss.str());
404  }
405 
406  // Use the integer parameter pack technique shown in
407  // http://stackoverflow.com/a/7858971/245265
408  using Seq = typename internal::GenIntegerSequence<sizeof...(A)>::type;
409  invoke(std::move(event), Seq());
410 }
411 
412 //------------------------------------------------------------------------------
413 template <typename S, typename... A>
414 template <int ...Seq>
415 void
417  internal::IntegerSequence<Seq...>) const
418 {
419  struct Spawned
420  {
421  Slot slot;
422  Event event;
423 
424  void operator()(Yield yield)
425  {
426  std::tuple<ValueTypeOf<A>...> args;
427  try
428  {
429  event.convertToTuple(args);
430  slot(std::get<Seq>(std::move(args))..., yield);
431  }
432  catch (const error::BadType&)
433  {
434  /* Do nothing. This is to prevent the publisher crashing
435  subscribers when it passes Variant objects having
436  incorrect schema. */
437  }
438  }
439  };
440 
441  auto executor =
442  boost::asio::get_associated_executor(slot_, event.executor());
443  boost::asio::spawn(executor, Spawned{slot_, std::move(event)});
444 }
445 
446 //------------------------------------------------------------------------------
447 template <typename... TArgs, typename TSlot>
448 SimpleCoroEventUnpacker<DecayedSlot<TSlot>, TArgs...>
449 simpleCoroEvent(TSlot&& slot)
450 {
452  std::forward<TSlot>(slot));
453 }
454 
455 //------------------------------------------------------------------------------
456 template <typename... TArgs, typename TSlot>
458 basicCoroEvent(TSlot&& slot)
459 {
461  std::forward<TSlot>(slot));
462 }
463 
464 
465 //******************************************************************************
466 // CoroInvocationUnpacker implementation
467 //******************************************************************************
468 
469 //------------------------------------------------------------------------------
470 template <typename S, typename... A>
472  : slot_(std::move(slot))
473 {}
474 
475 //------------------------------------------------------------------------------
476 template <typename S, typename... A>
478 {
479  if (inv.args().size() < sizeof...(A))
480  {
481  std::ostringstream oss;
482  oss << "Expected " << sizeof...(A)
483  << " args, but only got " << inv.args().size();
484  throw internal::UnpackCoroError().withArgs(oss.str());
485  }
486 
487  // Use the integer parameter pack technique shown in
488  // http://stackoverflow.com/a/7858971/245265
489  using Seq = typename internal::GenIntegerSequence<sizeof...(A)>::type;
490  invoke(std::move(inv), Seq());
491 
492  return deferment;
493 }
494 
495 //------------------------------------------------------------------------------
496 template <typename S, typename... A>
497 template <int ...Seq>
498 void
500  internal::IntegerSequence<Seq...>) const
501 {
502  class Spawned
503  {
504  public:
505  Spawned(const Slot& slot, Invocation&& inv)
506  : slot_(slot),
507  calleePtr_(inv.callee_),
508  reqId_(inv.requestId()),
509  inv_(std::move(inv))
510  {}
511 
512  void operator()(Yield yield)
513  {
514  try
515  {
516  std::tuple<ValueTypeOf<A>...> args;
517  inv_.convertToTuple(args);
518  Outcome outcome = slot_(std::move(inv_),
519  std::get<Seq>(std::move(args))...,
520  yield);
521 
522  switch (outcome.type())
523  {
525  // Do nothing
526  break;
527 
529  safeYield(std::move(outcome).asResult());
530  break;
531 
533  safeYield(std::move(outcome).asError());
534  break;
535 
536  default:
537  assert(false && "Unexpected wamp::Outcome::Type enumerator");
538  }
539 
540  }
541  catch (Error e)
542  {
543  safeYield(std::move(e));
544  }
545  catch (const error::BadType& e)
546  {
547  // Forward Variant conversion exceptions as ERROR messages.
548  safeYield(Error(e));
549  }
550  }
551 
552  private:
553  void safeYield(Result&& result)
554  {
555  auto callee = calleePtr_.lock();
556  if (callee)
557  callee->safeYield(reqId_, std::move(result));
558  }
559 
560  void safeYield(Error&& error)
561  {
562  auto callee = calleePtr_.lock();
563  if (callee)
564  callee->safeYield(reqId_, std::move(error));
565  }
566 
567  Slot slot_;
568  std::weak_ptr<internal::Callee> calleePtr_;
569  RequestId reqId_;
570  Invocation inv_;
571  };
572 
573  auto executor =
574  boost::asio::get_associated_executor(slot_, inv.executor());
575  boost::asio::spawn(executor, Spawned{slot_, std::move(inv)});
576 }
577 
578 //------------------------------------------------------------------------------
579 template <typename... TArgs, typename TSlot>
580 CoroInvocationUnpacker<DecayedSlot<TSlot>, TArgs...>
581 unpackedCoroRpc(TSlot&& slot)
582 {
584  std::forward<TSlot>(slot) );
585 }
586 
587 //******************************************************************************
588 // SimpleCoroInvocationUnpacker implementation
589 //******************************************************************************
590 
591 //------------------------------------------------------------------------------
592 template <typename S, typename R, typename... A>
594  : slot_(std::move(slot))
595 {}
596 
597 //------------------------------------------------------------------------------
598 template <typename S, typename R, typename... A>
599 Outcome
601 {
602  if (inv.args().size() < sizeof...(A))
603  {
604  std::ostringstream oss;
605  oss << "Expected " << sizeof...(A)
606  << " args, but only got " << inv.args().size();
607  throw internal::UnpackCoroError().withArgs(oss.str());
608  }
609 
610  // Use the integer parameter pack technique shown in
611  // http://stackoverflow.com/a/7858971/245265
612  using Seq = typename internal::GenIntegerSequence<sizeof...(A)>::type;
614  invoke(IsVoidResult{}, std::move(inv), Seq());
615 
616  return deferment;
617 }
618 
619 //------------------------------------------------------------------------------
620 template <typename S, typename R, typename... A>
621 template <int ...Seq>
623  TrueType, Invocation&& inv, internal::IntegerSequence<Seq...>) const
624 {
625  struct Spawned
626  {
627  Slot slot;
628  Invocation inv;
629 
630  void operator()(Yield yield)
631  {
632  try
633  {
634  std::tuple<ValueTypeOf<A>...> args;
635  inv.convertToTuple(args);
636  slot(std::get<Seq>(std::move(args))..., yield);
637  inv.yield();
638  }
639  catch (const Error& e)
640  {
641  inv.yield(e);
642  }
643  catch (const error::BadType& e)
644  {
645  // Forward Variant conversion exceptions as ERROR messages.
646  inv.yield(Error(e));
647  }
648  }
649  };
650 
651  auto executor =
652  boost::asio::get_associated_executor(slot_, inv.executor());
653  boost::asio::spawn(executor, Spawned{slot_, std::move(inv)});
654 }
655 
656 //------------------------------------------------------------------------------
657 template <typename S, typename R, typename... A>
658 template <int ...Seq>
659 void SimpleCoroInvocationUnpacker<S,R,A...>::invoke(
660  FalseType, Invocation&& inv, internal::IntegerSequence<Seq...>) const
661 {
662  struct Spawned
663  {
664  Slot slot;
665  Invocation inv;
666 
667  void operator()(Yield yield)
668  {
669  try
670  {
671  std::tuple<ValueTypeOf<A>...> args;
672  inv.convertToTuple(args);
673  ResultType result = slot(std::get<Seq>(std::move(args))...,
674  yield);
675  inv.yield(Result().withArgs(std::move(result)));
676  }
677  catch (const Error& e)
678  {
679  inv.yield(e);
680  }
681  catch (const error::BadType& e)
682  {
683  // Forward Variant conversion exceptions as ERROR messages.
684  inv.yield(Error(e));
685  }
686  }
687  };
688 
689  auto executor = inv.executor();
690  boost::asio::spawn(executor, Spawned{slot_, std::move(inv)});
691 }
692 
693 //------------------------------------------------------------------------------
694 template <typename TResult, typename... TArgs, typename TSlot>
695 SimpleCoroInvocationUnpacker<DecayedSlot<TSlot>, TResult, TArgs...>
696 simpleCoroRpc(TSlot&& slot)
697 {
698  return SimpleCoroInvocationUnpacker<DecayedSlot<TSlot>, TResult, TArgs...>(
699  std::forward<TSlot>(slot) );
700 }
701 
702 //------------------------------------------------------------------------------
703 template <typename TResult, typename... TArgs, typename TSlot>
705 basicCoroRpc(TSlot&& slot)
706 {
707  return SimpleCoroInvocationUnpacker<DecayedSlot<TSlot>, TResult, TArgs...>(
708  std::forward<TSlot>(slot) );
709 }
710 
711 } // namespace wamp
712 
713 #endif // CPPWAMP_COROUNPACKER_HPP
wamp::SimpleCoroInvocationUnpacker::Slot
TSlot Slot
The function type to be wrapped.
Definition: corounpacker.hpp:233
wamp::CoroEventUnpacker
Wrapper around an event coroutine slot which automatically unpacks positional payload arguments.
Definition: corounpacker.hpp:39
wamp::RequestId
int64_t RequestId
Ephemeral ID associated with a WAMP request.
Definition: wampdefs.hpp:23
wamp::FalseType
BoolConstant< false > FalseType
Equivalent to std::false_type provided in C++17.
Definition: traits.hpp:122
wamp::CoroEventUnpacker::operator()
void operator()(Event event) const
Spawns a new coroutine and executes the stored event slot.
Definition: corounpacker.hpp:325
wamp::TrueType
BoolConstant< true > TrueType
Equivalent to std::true_type provided in C++17.
Definition: traits.hpp:117
wamp::SimpleCoroEventUnpacker::operator()
void operator()(Event event) const
Spawns a new coroutine and executes the stored event slot.
Definition: corounpacker.hpp:396
wamp::CoroEventUnpacker::Slot
TSlot Slot
The function type to be wrapped.
Definition: corounpacker.hpp:43
wamp::Invocation::yield
void yield(Result result=Result()) const
Manually sends a YIELD result back to the callee.
Definition: peerdata.ipp:939
wamp::SimpleCoroInvocationUnpacker::SimpleCoroInvocationUnpacker
SimpleCoroInvocationUnpacker(Slot slot)
Constructor taking a callable target.
Definition: corounpacker.hpp:593
wamp::SimpleCoroEventUnpacker::SimpleCoroEventUnpacker
SimpleCoroEventUnpacker(Slot slot)
Constructor taking a callable target.
Definition: corounpacker.hpp:390
wamp::Outcome::Type::result
@ result
Contains a wamp::Result to be yielded back to the caller.
wamp::Outcome::Type::deferred
@ deferred
A YIELD has been, or will be, sent manually.
wamp::Error
Provides the reason URI, options, and payload arguments contained within WAMP ERROR messages.
Definition: peerdata.hpp:292
wamp::CoroEventUnpacker::CoroEventUnpacker
CoroEventUnpacker(Slot slot)
Constructor taking a callable target.
Definition: corounpacker.hpp:319
wamp::SimpleCoroInvocationUnpacker
Wrapper around a call slot which automatically unpacks positional payload arguments.
Definition: corounpacker.hpp:229
wamp::SimpleCoroInvocationUnpacker::ResultType
TResult ResultType
The static result type returned by the slot.
Definition: corounpacker.hpp:236
wamp::basicCoroRpc
SimpleCoroInvocationUnpacker< DecayedSlot< TSlot >, TResult, TArgs... > basicCoroRpc(TSlot &&slot)
Definition: corounpacker.hpp:705
wamp::CoroInvocationUnpacker
Wrapper around a call coroutine slot which automatically unpacks positional payload arguments.
Definition: corounpacker.hpp:169
wamp::SimpleCoroInvocationUnpacker::operator()
Outcome operator()(Invocation inv) const
Spawns a new coroutine and executes the stored call slot.
Definition: corounpacker.hpp:600
wamp::deferment
constexpr Deferment deferment
Convenient value of the wamp::Deferment tag type that can be passed to the wamp::Outcome constructor.
Definition: peerdata.hpp:703
wamp
Definition: anyhandler.hpp:36
unpacker.hpp
Contains utilities for unpacking positional arguments passed to event slots and call slots.
wamp::CoroInvocationUnpacker::operator()
Outcome operator()(Invocation inv) const
Spawns a new coroutine and executes the stored call slot.
Definition: corounpacker.hpp:477
wamp::basicCoroEvent
SimpleCoroEventUnpacker< DecayedSlot< TSlot >, TArgs... > basicCoroEvent(TSlot &&slot)
Definition: corounpacker.hpp:458
wamp::Outcome::Type::error
@ error
Contains a wamp::Error to be yielded back to the caller.
wamp::SimpleCoroEventUnpacker
Wrapper around an event slot which automatically unpacks positional payload arguments.
Definition: corounpacker.hpp:97
wamp::CoroInvocationUnpacker::Slot
TSlot Slot
The function type to be wrapped.
Definition: corounpacker.hpp:173
wamp::Outcome
Contains the outcome of an RPC invocation.
Definition: peerdata.hpp:709
wamp::SimpleCoroEventUnpacker::Slot
TSlot Slot
The function type to be wrapped.
Definition: corounpacker.hpp:101
wamp::CoroInvocationUnpacker::CoroInvocationUnpacker
CoroInvocationUnpacker(Slot slot)
Constructor taking a callable target.
Definition: corounpacker.hpp:471
wamp::Invocation
Contains payload arguments and other options within WAMP INVOCATION messages.
Definition: peerdata.hpp:799
wamp::Event
Provides the subscription/publication ids, options, and payload contained within WAMP EVENT messages.
Definition: peerdata.hpp:427
wamp::Invocation::executor
AnyIoExecutor executor() const
Obtains the executor used to execute user-provided handlers.
Definition: peerdata.ipp:932
wamp::BoolConstant
std::integral_constant< bool, B > BoolConstant
Equivalent to std::bool_constant provided in C++17.
Definition: traits.hpp:112