libqi-api  2.8.7.4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
futureadapter.hxx
Go to the documentation of this file.
1 #pragma once
2 /*
3 ** Copyright (C) 2013 Aldebaran Robotics
4 ** See COPYING for the license
5 */
6 
7 #ifndef QI_TYPE_DETAIL_FUTURE_ADAPTER_HXX_
8 #define QI_TYPE_DETAIL_FUTURE_ADAPTER_HXX_
9 
11 
12 namespace qi
13 {
14 namespace detail
15 {
16 
17 static const char* InvalidValueError = "value is invalid";
18 static const char* InvalidFutureError = "function returned an invalid future";
19 
20 template<typename T> void setPromise(qi::Promise<T>& promise, const AnyValue& v)
21 {
22  if (!v.isValid())
23  {
24  promise.setError(InvalidValueError);
25  return;
26  }
27 
28  try
29  {
30  qiLogDebug("qi.adapter") << "converting value";
31  T val = v.to<T>();
32  qiLogDebug("qi.adapter") << "setting promise";
33  promise.setValue(val);
34  qiLogDebug("qi.adapter") << "done";
35  }
36  catch(const std::exception& e)
37  {
38  qiLogError("qi.adapter") << "future to promise forwarding error: " << e.what();
39  promise.setError(e.what());
40  }
41 }
42 
43 template<> inline void setPromise(qi::Promise<void>& promise, const AnyValue&)
44 {
45  promise.setValue(0);
46 }
47 
48 template<> inline void setPromise(qi::Promise<AnyValue>& promise, const AnyValue& val)
49 {
50  promise.setValue(val);
51 }
52 
53 template<> inline void setPromise(qi::Promise<AnyReference>& promise, const AnyValue& val)
54 {
55  promise.setValue(val.clone());
56 }
57 
58 template <typename T>
60  boost::shared_ptr<GenericObject> ao)
61 {
62  if (!val.isValid())
63  {
64  promise.setError(InvalidValueError);
65  return;
66  }
67 
68  QI_ASSERT(ao);
69  qiLogDebug("qi.adapter") << "futureAdapter trigger";
70  TypeOfTemplate<Future>* ft1 = QI_TEMPLATE_TYPE_GET(val.type(), Future);
71  TypeOfTemplate<FutureSync>* ft2 = QI_TEMPLATE_TYPE_GET(val.type(), FutureSync);
72  qiLogDebug("qi.adapter") << "isFuture " << val.type()->infoString() << ' ' << !!ft1 << ' ' << !!ft2;
73  bool isvoid = false;
74  if (ft1)
75  isvoid = ft1->templateArgument()->kind() == TypeKind_Void;
76  else if (ft2)
77  isvoid = ft2->templateArgument()->kind() == TypeKind_Void;
78  GenericObject& gfut = *ao;
79  if (gfut.call<bool>("hasError", 0))
80  {
81  qiLogDebug("qi.adapter") << "futureAdapter: future in error";
82  std::string s = gfut.call<std::string>("error", 0);
83  qiLogDebug("qi.adapter") << "futureAdapter: got error: " << s;
84  promise.setError(s);
85  return;
86  }
87  if (gfut.call<bool>("isCanceled"))
88  {
89  qiLogDebug("qi.adapter") << "futureAdapter: future canceled";
90  promise.setCanceled();
91  return;
92  }
93  qiLogDebug("qi.adapter") << "futureAdapter: future has value";
94  AnyValue v = gfut.call<AnyValue>("value", 0);
95  // For a Future<void>, value() gave us a void*
96  if (isvoid)
97  v = AnyValue(qi::typeOf<void>());
98  qiLogDebug("qi.adapter") << (v.isValid() ? v.type()->infoString() : "<invalid AnyValue>");
99  setPromise(promise, v);
100  qiLogDebug("qi.adapter") << "Promise set";
101 }
102 
103 // return a generic object pointing to the future referenced by val or null if val is not a future
104 // remember that you need a shared_ptr pointing on the genericobject so that it can work (shared_from_this)
105 inline boost::shared_ptr<GenericObject> getGenericFuture(AnyReference val, TypeKind* kind = 0)
106 {
107  if (!val.isValid())
108  {
109  qiLogDebug("qi.adapter") << "getGenericFuture: Invalid value.";
110  return boost::shared_ptr<GenericObject>();
111  }
112 
113  TypeOfTemplate<Future>* ft1 = QI_TEMPLATE_TYPE_GET(val.type(), Future);
114  TypeOfTemplate<FutureSync>* ft2 = QI_TEMPLATE_TYPE_GET(val.type(), FutureSync);
115  ObjectTypeInterface* onext = NULL;
116  qiLogDebug("qi.adapter") << "isFuture " << val.type()->infoString() << ' ' << !!ft1 << ' ' << !!ft2;
117  if (ft1)
118  {
119  if (kind)
120  *kind = ft1->templateArgument()->kind();
121  onext = ft1;
122  }
123  else if (ft2)
124  {
125  if (kind)
126  *kind = ft2->templateArgument()->kind();
127  onext = ft2;
128  }
129  if (!onext)
130  return boost::shared_ptr<GenericObject>();
131  return boost::make_shared<GenericObject>(onext, val.rawValue());
132 }
133 
134 // futureAdapter helper that detects and handles value of kind future
135 // return true if value was a future and was handled
136 // Takes ownership of the value if it returns true.
137 template <typename T>
138 inline bool handleFuture(AnyReference val, Promise<T> promise)
139 {
140  boost::shared_ptr<GenericObject> ao = getGenericFuture(val);
141  if (!ao)
142  return false;
143 
144  // At this point, we are sure that the value holds a Future, so we take its ownership.
145  UniqueAnyReference uval{ val };
146  if (!ao->call<bool>("isValid"))
147  {
148  promise.setError(InvalidFutureError);
149  return true;
150  }
151 
152  // The callback takes ownership of the value. After that, the old reference will be in a invalid
153  // state.
154  // TODO: Remove use of a shared_ptr once we can initialize captures of the lambda (C++14).
155  auto spVal = std::make_shared<UniqueAnyReference>(std::move(uval));
156  boost::function<void()> cb = [=]() mutable {
157  if (!spVal || !(*spVal)->isValid() || !ao)
158  throw std::logic_error{"Future is either invalid or has already been adapted."};
159 
160  // Transfer the value to a local UniqueAnyReference.
161  // Also reset the shared ptr to break the cycle.
162  auto localRef = ka::exchange(spVal, nullptr);
163  futureAdapterGeneric<T>(**localRef, promise, ka::exchange(ao, nullptr));
164  };
165  spVal.reset();
166 
167  // Careful, gfut will die at the end of this block, but it is
168  // stored in call data. So call must finish before we exit this block,
169  // and thus must be synchronous.
170  try
171  {
172  ao->call<void>("_connect", cb);
173  promise.setOnCancel(
175  static_cast<void(GenericObject::*)(const std::string&)>(
176  &GenericObject::call<void>),
177  boost::weak_ptr<GenericObject>(ao),
178  "cancel"));
179  }
180  catch (std::exception& e)
181  {
182  qiLogError("qi.object") << "future connect error " << e.what();
183  promise.setError("internal error: cannot connect returned future");
184  }
185  return true;
186 }
187 
188 template <typename T>
190 {
191  auto val = UniqueAnyReference{ metaFut.value() };
192  if (!val->isValid())
193  throw std::runtime_error(InvalidValueError);
194 
195 
196  AnyValue hold;
197  if (boost::shared_ptr<GenericObject> ao = getGenericFuture(*val))
198  {
199  if (!ao->call<bool>("isValid"))
200  throw std::runtime_error(InvalidFutureError);
201 
202  hold = ao->call<qi::AnyValue>("value", (int)FutureTimeout_Infinite);
203  *val = hold.asReference();
204  }
205 
206  static TypeInterface* targetType;
207  QI_ONCE(targetType = typeOf<T>());
208  try
209  {
210  auto conv = val->convert(targetType);
211  if (!conv->type())
212  throw std::runtime_error(std::string("Unable to convert call result to target type: from ") +
213  val->signature(true).toPrettySignature() + " to " +
214  targetType->signature().toPrettySignature());
215  return std::move(*conv->ptr<T>(false));
216  }
217  catch(const std::exception& e)
218  {
219  throw std::runtime_error(std::string("Return argument conversion error: ") + e.what());
220  }
221 }
222 
223 template <>
225 {
226  auto val = UniqueAnyReference{ metaFut.value() };
227  if (!val->isValid())
228  throw std::runtime_error(InvalidValueError);
229 
230  if (boost::shared_ptr<GenericObject> ao = getGenericFuture(*val))
231  {
232  if (!ao->call<bool>("isValid"))
233  throw std::runtime_error(InvalidFutureError);
234 
235  ao->call<qi::AnyValue>("value", (int)FutureTimeout_Infinite);
236  }
237 }
238 
241 template <typename T>
243 {
244  if (!ref->isValid())
245  {
246  promise.setError(InvalidValueError);
247  return;
248  }
249 
250  static TypeInterface* targetType;
251  QI_ONCE(targetType = typeOf<T>());
252  try
253  {
254  auto conv = ref->convert(targetType);
255  if (!conv->type())
256  promise.setError(std::string("Unable to convert call result to target type: from ")
257  + ref->signature(true).toPrettySignature() + " to " + targetType->signature().toPrettySignature() );
258  else
259  {
260  promise.setValue(*conv->ptr<T>(false));
261  }
262  }
263  catch(const std::exception& e)
264  {
265  promise.setError(std::string("Return argument conversion error: ") + e.what());
266  }
267 }
268 
271 template <>
273 {
274  if (!ref->isValid())
275  {
276  promise.setError(InvalidValueError);
277  return;
278  }
279 
280  promise.setValue(nullptr);
281 }
282 
285 template <>
287 {
288  if (!ref->isValid())
289  {
290  promise.setError(InvalidValueError);
291  return;
292  }
293 
294  try
295  {
296  promise.setValue(ref->clone());
297  }
298  catch(const std::exception& e)
299  {
300  promise.setError(std::string("Return argument conversion error: ") + e.what());
301  }
302 }
303 
304 template <typename T>
305 inline void futureAdapter(const qi::Future<qi::AnyReference>& metaFut, qi::Promise<T> promise)
306 {
307  //error handling
308  if (metaFut.hasError()) {
309  promise.setError(metaFut.error());
310  return;
311  }
312  if (metaFut.isCanceled()) {
313  promise.setCanceled();
314  return;
315  }
316 
317  auto val = metaFut.value();
318  if (handleFuture(val, promise)) // Takes ownership of the value if it returns true.
319  return;
320  setAdaptedResult(promise, UniqueAnyReference{ val });
321 }
322 
323 template <typename T>
324 inline void futureAdapterVal(const qi::Future<qi::AnyValue>& metaFut, qi::Promise<T> promise)
325 {
326  //error handling
327  if (metaFut.hasError()) {
328  promise.setError(metaFut.error());
329  return;
330  }
331  if (metaFut.isCanceled()) {
332  promise.setCanceled();
333  return;
334  }
335  const AnyValue& val = metaFut.value();
336  if (!val.isValid())
337  {
338  promise.setError(InvalidValueError);
339  return;
340  }
341 
342  try
343  {
344  promise.setValue(val.to<T>());
345  }
346  catch (const std::exception& e)
347  {
348  promise.setError(std::string("Return argument conversion error: ") + e.what());
349  }
350 }
351 
352 template <>
354 {
355  if (metaFut.hasError())
356  promise.setError(metaFut.error());
357  else if (metaFut.isCanceled())
358  promise.setCanceled();
359  else
360  promise.setValue(metaFut.value());
361 }
362 
363 template <>
364 inline void futureAdapterVal(const qi::Future<qi::AnyValue>& metaFut, qi::Promise<void> promise)
365 {
366  if (metaFut.hasError())
367  promise.setError(metaFut.error());
368  else if (metaFut.isCanceled())
369  promise.setCanceled();
370  else
371  promise.setValue(0);
372 }
373 
374 }
375 }
376 
377 #endif
std::string toPrettySignature() const
std::enable_if< std::is_function< RF >::value, boost::function< RF > >::type bindSilent(AF &&fun, Arg0 &&arg0, Args &&...args)
Definition: trackable.hxx:300
void futureAdapter(const Future< FT > &f, Promise< PT > p, CONV converter)
Definition: future.hxx:475
void setPromise(qi::Promise< T > &promise, const AnyValue &v)
UniqueAnyReference convert(TypeInterface *targetType) const
#define qiLogDebug(...)
Definition: log.hpp:76
void setError(const std::string &msg)
Definition: future_fwd.hpp:862
void setAdaptedResult< void >(Promise< void > &promise, UniqueAnyReference ref)
#define QI_ASSERT(expr__)
Definition: assert.hpp:27
void futureAdapterGeneric(AnyReference val, qi::Promise< T > promise, boost::shared_ptr< GenericObject > ao)
T extractFuture(const qi::Future< qi::AnyReference > &metaFut)
bool isCanceled() const
Definition: future_fwd.hpp:339
qi::Signature signature(void *storage=nullptr, bool resolveDynamic=false)
void setAdaptedResult< AnyReference >(Promise< AnyReference > &promise, UniqueAnyReference ref)
void setAdaptedResult(Promise< T > &promise, UniqueAnyReference ref)
R call(const std::string &methodName, Args &&...args)
#define qiLogError(...)
Log in error mode.
Definition: log.hpp:120
T to() const
Convert to anything or throw trying.
qi::Signature signature(bool resolveDynamic=false) const
TypeInterface * type() const
void futureAdapterVal(const qi::Future< qi::AnyValue > &metaFut, qi::Promise< T > promise)
#define QI_TEMPLATE_TYPE_GET(typeInst, templateName)
#define QI_ONCE(code)
Execute code once, parallel calls are blocked until code finishes.
Definition: atomic.hpp:420
boost::shared_ptr< GenericObject > getGenericFuture(AnyReference val, TypeKind *kind=0)
AnyReference asReference() const
Definition: anyvalue.hpp:87
const char * infoString()
void hold(T data)
void setCanceled()
Definition: future_fwd.hpp:869
void setOnCancel(boost::function< void(qi::Promise< T > &)> cancelCallback)
Definition: future_fwd.hpp:897
AnyReference clone() const
const std::string & error(int msecs=FutureTimeout_Infinite) const
Definition: future_fwd.hpp:367
const ValueType & value(int msecs=FutureTimeout_Infinite) const
Return the value associated to a Future.
Definition: future_fwd.hpp:249
TypeKind
Definition: fwd.hpp:54
void setValue(const ValueType &value)
Definition: future_fwd.hpp:855
bool handleFuture(AnyReference val, Promise< T > promise)
void extractFuture< void >(const qi::Future< qi::AnyReference > &metaFut)
bool hasError(int msecs=FutureTimeout_Infinite) const
Definition: future_fwd.hpp:348