libqi-api  release-2.5.3-2016-11-18
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
signalspy.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #ifndef _QI_SIGNALHELPER_HPP_
4 #define _QI_SIGNALHELPER_HPP_
5 
6 #include <qi/actor.hpp>
7 #include <qi/anyobject.hpp>
8 #include <qi/clock.hpp>
9 #include <qi/signal.hpp>
10 
11 namespace qi
12 {
21 class SignalSpy: public qi::Actor
22 {
23 public:
25  template<typename... Args>
26  SignalSpy(SignalF<void(Args...)>& signal)
27  : _records()
28  {
29  signal.connect(stranded([this](const Args&... args)
30  {
31  this->recordCallback(args...);
32  }));
33  }
34 
36  SignalSpy(qi::AnyObject& object, const std::string& signalOrPropertyName)
37  : _records()
38  {
39  object.connect(
40  signalOrPropertyName,
42  stranded([this](qi::AnyReferenceVector anything)
43  {
44  return this->recordAnyCallback(anything);
45  })));
46  }
47 
48  // Non-copyable
49  SignalSpy(const SignalSpy&) = delete;
50  SignalSpy& operator=(const SignalSpy&) = delete;
51 
53  {
54  joinTasks();
55  }
56 
58  struct Record
59  {
61  std::vector<qi::AnyValue> args;
62 
64  template<typename T>
65  const T& arg(int index) const
66  {
67  return args[index].asReference().as<T>();
68  }
69  };
70 
72  std::vector<Record> allRecords() const
73  {
74  return async([this]
75  { return _records;
76  }).value();
77  }
78 
80  Record record(size_t index) const
81  {
82  qiLogDebug("qi.signalspy") << "Getting record #" << index << " "
83  << (strand()->isInThisContext() ? "from strand" : "from outside");
84 
85  return async([this, index]
86  {
87  qiLogDebug("qi.signalspy") << "Getting record #" << index;
88  return _records[index];
89  }).value();
90  }
91 
94  {
95  return async([this]
96  {
97  assert(!_records.empty()); return _records.back();
98  }).value();
99  }
100 
102  size_t recordCount() const
103  {
104  qiLogDebug("qi.signalspy") << "Getting record count "
105  << (strand()->isInThisContext() ? "from strand" : "from outside");
106  return async([this]
107  {
108  qiLogDebug("qi.signalspy") << "Getting record count";
109  return _records.size();
110  }).value();
111  }
112 
113  QI_API_DEPRECATED_MSG(Use 'recordCount' instead)
114  unsigned int getCounter() const
115  {
116  return async([&]{ return static_cast<unsigned int>(_records.size()); }).value();
117  }
118 
120  qi::FutureSync<bool> waitUntil(unsigned int nofRecords, const qi::Duration& timeout) const
121  {
122  qi::Promise<bool> waiting;
123  async([this, waiting, nofRecords, timeout]() mutable
124  {
125  if(nofRecords <= _records.size())
126  {
127  waiting.setValue(true);
128  return;
129  }
130 
131  qi::SignalLink recordedSubscription;
132 
133  // track timeout
134  auto timingOut = asyncDelay([this, waiting, &recordedSubscription]() mutable
135  {
136  waiting.setValue(false);
137  recorded.disconnect(recordedSubscription);
138  }, timeout);
139 
140  // be called after signal emissions are recorded
141  recordedSubscription = recorded.connect(stranded(
142  [this, waiting, &recordedSubscription, timingOut, nofRecords]() mutable
143  {
144  if (nofRecords <= _records.size())
145  {
146  waiting.setValue(true);
147  timingOut.cancel();
148  recorded.disconnect(recordedSubscription);
149  } // no need for scheduling in the strand because it is a direct connection
151  });
152  return waiting.future();
153  }
154 
155 private:
157  std::vector<Record> _records;
158 
160  mutable qi::Signal<void> recorded;
161 
163  template <typename... Args>
164  void recordCallback(const Args&... args)
165  {
166  assert(strand()->isInThisContext());
167  _records.emplace_back(Record{{qi::AnyValue::from<Args>(args)...}});
168  recorded();
169  }
170 
172  AnyReference recordAnyCallback(const qi::AnyReferenceVector& args)
173  {
174  assert(strand()->isInThisContext());
175  Record record;
176  for (const auto& arg: args)
177  record.args.emplace_back(arg.to<qi::AnyValue>());
178  _records.emplace_back(std::move(record));
179  recorded();
180  return AnyReference();
181  }
182 };
183 }
184 
185 #endif
SignalSubscriber & setCallType(MetaCallType ct)
Definition: signal.hxx:148
bool isInThisContext() override
unsigned int getCounter() const
Definition: signalspy.hpp:114
auto stranded(Args &&...args) const -> decltype(_strand.schedulerFor(std::forward< Args >(args)...))
Definition: actor.hpp:46
#define qiLogDebug(...)
Definition: log.hpp:68
A tool to track signal emissions, specialized for testing. A signal spy can acknowledge every signal ...
Definition: signalspy.hpp:21
qi::Strand * strand() const
Definition: actor.hpp:40
NanoSeconds Duration
Definition: clock.hpp:32
Future< T > future() const
Get a future linked to this promise. Can be called multiple times.
Definition: future_fwd.hpp:786
Record lastRecord() const
Direct access to last record.
Definition: signalspy.hpp:93
auto async(Args &&...args) const -> decltype(_strand.async(std::forward< Args >(args)...))
Definition: actor.hpp:53
SignalSubscriber & connect(...)
Definition: signal.hxx:19
bool disconnect(const SignalLink &link)
std::vector< AnyReference > AnyReferenceVector
Force a synchronous call.
Definition: typeobject.hpp:25
#define QI_API_DEPRECATED_MSG(msg__)
Compiler flags to mark a function as deprecated. It will generate a compiler warning.
Definition: macro.hpp:53
void joinTasks()
Definition: actor.hpp:73
SignalSpy(SignalF< void(Args...)> &signal)
Constructor taking a signal instance.
Definition: signalspy.hpp:26
std::vector< qi::AnyValue > args
Signal arguments are stored here, in a type-erased way for compatibility.
Definition: signalspy.hpp:61
SignalSpy & operator=(const SignalSpy &)=delete
Record record(size_t index) const
Direct access to a record, by order of arrival.
Definition: signalspy.hpp:80
static AnyFunction fromDynamicFunction(DynamicFunction f)
SignalSpy(qi::AnyObject &object, const std::string &signalOrPropertyName)
Constructor taking a type-erased signal.
Definition: signalspy.hpp:36
A record data, corresponding to one signal emission.
Definition: signalspy.hpp:58
size_t recordCount() const
The number of records.
Definition: signalspy.hpp:102
void setValue(const ValueType &value)
Definition: future_fwd.hpp:760
qi::uint64_t SignalLink
Definition: signal.hpp:35
qi::FutureSync< bool > waitUntil(unsigned int nofRecords, const qi::Duration &timeout) const
Waits for the given number of records to be reached, before the given timeout.
Definition: signalspy.hpp:120
std::vector< Record > allRecords() const
Retrieve all the records in one shot.
Definition: signalspy.hpp:72
const T & arg(int index) const
Use this to access an argument in the type you expect it.
Definition: signalspy.hpp:65
auto asyncDelay(Args &&...args) const -> decltype(_strand.asyncDelay(std::forward< Args >(args)...))
Definition: actor.hpp:60