libqi-api  release-2.5.3-2016-11-18
 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 
10 #include <boost/scope_exit.hpp>
11 
13 
14 namespace qi
15 {
16 namespace detail
17 {
18 
19 static const char* InvalidFutureError = "function returned an invalid future";
20 
21 template<typename T> void setPromise(qi::Promise<T>& promise, AnyValue& v)
22 {
23  try
24  {
25  qiLogDebug("qi.adapter") << "converting value";
26  T val = v.to<T>();
27  qiLogDebug("qi.adapter") << "setting promise";
28  promise.setValue(val);
29  qiLogDebug("qi.adapter") << "done";
30  }
31  catch(const std::exception& e)
32  {
33  qiLogError("qi.adapter") << "future to promise forwarding error: " << e.what();
34  promise.setError(e.what());
35  }
36 }
37 
38 template<> inline void setPromise(qi::Promise<void>& promise, AnyValue&)
39 {
40  promise.setValue(0);
41 }
42 
43 template<> inline void setPromise(qi::Promise<AnyValue>& promise, AnyValue& val)
44 {
45  promise.setValue(val);
46 }
47 
48 template <typename T>
50  boost::shared_ptr<GenericObject>& ao)
51 {
52  QI_ASSERT(ao);
53  qiLogDebug("qi.adapter") << "futureAdapter trigger";
54  TypeOfTemplate<Future>* ft1 = QI_TEMPLATE_TYPE_GET(val.type(), Future);
55  TypeOfTemplate<FutureSync>* ft2 = QI_TEMPLATE_TYPE_GET(val.type(), FutureSync);
56  qiLogDebug("qi.adapter") << "isFuture " << val.type()->infoString() << ' ' << !!ft1 << ' ' << !!ft2;
57  bool isvoid = false;
58  if (ft1)
59  isvoid = ft1->templateArgument()->kind() == TypeKind_Void;
60  else if (ft2)
61  isvoid = ft2->templateArgument()->kind() == TypeKind_Void;
62  GenericObject& gfut = *ao;
63  // reset the shared_ptr to break the cycle
64  BOOST_SCOPE_EXIT_TPL(&ao, &val) {
65  ao.reset();
66  val.destroy();
67  } BOOST_SCOPE_EXIT_END
68  if (gfut.call<bool>("hasError", 0))
69  {
70  qiLogDebug("qi.adapter") << "futureAdapter: future in error";
71  std::string s = gfut.call<std::string>("error", 0);
72  qiLogDebug("qi.adapter") << "futureAdapter: got error: " << s;
73  promise.setError(s);
74  return;
75  }
76  if (gfut.call<bool>("isCanceled"))
77  {
78  qiLogDebug("qi.adapter") << "futureAdapter: future canceled";
79  promise.setCanceled();
80  return;
81  }
82  qiLogDebug("qi.adapter") << "futureAdapter: future has value";
83  AnyValue v = gfut.call<AnyValue>("value", 0);
84  // For a Future<void>, value() gave us a void*
85  if (isvoid)
86  v = AnyValue(qi::typeOf<void>());
87  qiLogDebug("qi.adapter") << v.type()->infoString();
88  setPromise(promise, v);
89  qiLogDebug("qi.adapter") << "Promise set";
90 }
91 
92 // return a generic object pointing to the future referenced by val or null if val is not a future
93 // remember that you need a shared_ptr pointing on the genericobject so that it can work (shared_from_this)
94 inline boost::shared_ptr<GenericObject> getGenericFuture(AnyReference val, TypeKind* kind = 0)
95 {
96  TypeOfTemplate<Future>* ft1 = QI_TEMPLATE_TYPE_GET(val.type(), Future);
97  TypeOfTemplate<FutureSync>* ft2 = QI_TEMPLATE_TYPE_GET(val.type(), FutureSync);
98  ObjectTypeInterface* onext = NULL;
99  qiLogDebug("qi.adapter") << "isFuture " << val.type()->infoString() << ' ' << !!ft1 << ' ' << !!ft2;
100  if (ft1)
101  {
102  if (kind)
103  *kind = ft1->templateArgument()->kind();
104  onext = ft1;
105  }
106  else if (ft2)
107  {
108  if (kind)
109  *kind = ft2->templateArgument()->kind();
110  onext = ft2;
111  }
112  if (!onext)
113  return boost::shared_ptr<GenericObject>();
114  return boost::make_shared<GenericObject>(onext, val.rawValue());
115 }
116 
117 // futureAdapter helper that detects and handles value of kind future
118 // return true if value was a future and was handled
119 template <typename T>
120 inline bool handleFuture(AnyReference val, Promise<T> promise)
121 {
122  boost::shared_ptr<GenericObject> ao = getGenericFuture(val);
123  if (!ao)
124  return false;
125 
126  if (!ao->call<bool>("isValid"))
127  {
128  promise.setError(InvalidFutureError);
129  return true;
130  }
131 
132  boost::function<void()> cb =
133  boost::bind(futureAdapterGeneric<T>, val, promise, ao);
134  // Careful, gfut will die at the end of this block, but it is
135  // stored in call data. So call must finish before we exit this block,
136  // and thus must be synchronous.
137  try
138  {
139  ao->call<void>("_connect", cb);
140  promise.setOnCancel(
142  static_cast<void(GenericObject::*)(const std::string&)>(
143  &GenericObject::call<void>),
144  boost::weak_ptr<GenericObject>(ao),
145  "cancel"));
146  }
147  catch (std::exception& e)
148  {
149  qiLogError("qi.object") << "future connect error " << e.what();
150  promise.setError("internal error: cannot connect returned future");
151  }
152  return true;
153 }
154 
156 {
160  {
161  val.destroy();
162  }
163 };
164 
165 template <typename T>
167 {
168  AnyReference val = metaFut.value();
169  AutoRefDestroy destroy(val);
170 
171  AnyValue hold;
172  if (boost::shared_ptr<GenericObject> ao = getGenericFuture(val))
173  {
174  if (!ao->call<bool>("isValid"))
175  throw std::runtime_error(InvalidFutureError);
176 
177  hold = ao->call<qi::AnyValue>("value", (int)FutureTimeout_Infinite);
178  val = hold.asReference();
179  }
180 
181  static TypeInterface* targetType;
182  QI_ONCE(targetType = typeOf<T>());
183  try
184  {
185  std::pair<AnyReference, bool> conv = val.convert(targetType);
186  if (!conv.first.type())
187  throw std::runtime_error(std::string("Unable to convert call result to target type: from ")
188  + val.signature(true).toPrettySignature() + " to " + targetType->signature().toPrettySignature());
189  else
190  {
191  if (conv.second)
192  {
193  AutoRefDestroy destroy(conv.first);
194  return std::move(*conv.first.ptr<T>(false));
195  }
196  else
197  return std::move(*conv.first.ptr<T>(false));
198  }
199  }
200  catch(const std::exception& e)
201  {
202  throw std::runtime_error(std::string("Return argument conversion error: ") + e.what());
203  }
204 }
205 
206 template <>
208 {
209  AnyReference val = metaFut.value();
210  AutoRefDestroy destroy(val);
211 
212  if (boost::shared_ptr<GenericObject> ao = getGenericFuture(val))
213  {
214  if (!ao->call<bool>("isValid"))
215  throw std::runtime_error(InvalidFutureError);
216 
217  ao->call<qi::AnyValue>("value", (int)FutureTimeout_Infinite);
218  }
219 }
220 
221 template <typename T>
222 inline void futureAdapter(const qi::Future<qi::AnyReference>& metaFut, qi::Promise<T> promise)
223 {
224  qiLogDebug("qi.object") << "futureAdapter " << qi::typeOf<T>()->infoString()<< ' ' << metaFut.hasError();
225  //error handling
226  if (metaFut.hasError()) {
227  promise.setError(metaFut.error());
228  return;
229  }
230  if (metaFut.isCanceled()) {
231  promise.setCanceled();
232  return;
233  }
234 
235  AnyReference val = metaFut.value();
236  if (handleFuture(val, promise))
237  return;
238 
239  static TypeInterface* targetType;
240  QI_ONCE(targetType = typeOf<T>());
241  try
242  {
243  std::pair<AnyReference, bool> conv = val.convert(targetType);
244  if (!conv.first.type())
245  promise.setError(std::string("Unable to convert call result to target type: from ")
246  + val.signature(true).toPrettySignature() + " to " + targetType->signature().toPrettySignature() );
247  else
248  {
249  promise.setValue(*conv.first.ptr<T>(false));
250  }
251  if (conv.second)
252  conv.first.destroy();
253  }
254  catch(const std::exception& e)
255  {
256  promise.setError(std::string("Return argument conversion error: ") + e.what());
257  }
258  val.destroy();
259 }
260 
261 template <>
263 {
264  qiLogDebug("qi.object") << "futureAdapter void " << metaFut.hasError();
265  //error handling
266  if (metaFut.hasError()) {
267  promise.setError(metaFut.error());
268  return;
269  }
270  if (metaFut.isCanceled()) {
271  promise.setCanceled();
272  return;
273  }
274  AnyReference val = metaFut.value();
275  if (handleFuture(val, promise))
276  return;
277 
278  promise.setValue(0);
279  val.destroy();
280 }
281 
282 template <typename T>
283 inline void futureAdapterVal(const qi::Future<qi::AnyValue>& metaFut, qi::Promise<T> promise)
284 {
285  //error handling
286  if (metaFut.hasError()) {
287  promise.setError(metaFut.error());
288  return;
289  }
290  if (metaFut.isCanceled()) {
291  promise.setCanceled();
292  return;
293  }
294  const AnyValue& val = metaFut.value();
295  try
296  {
297  promise.setValue(val.to<T>());
298  }
299  catch (const std::exception& e)
300  {
301  promise.setError(std::string("Return argument conversion error: ") + e.what());
302  }
303 }
304 
305 template <>
307 {
308  if (metaFut.hasError())
309  promise.setError(metaFut.error());
310  else if (metaFut.isCanceled())
311  promise.setCanceled();
312  else
313  promise.setValue(metaFut.value());
314 }
315 
316 template <>
317 inline void futureAdapterVal(const qi::Future<qi::AnyValue>& metaFut, qi::Promise<void> promise)
318 {
319  if (metaFut.hasError())
320  promise.setError(metaFut.error());
321  else if (metaFut.isCanceled())
322  promise.setCanceled();
323  else
324  promise.setValue(0);
325 }
326 
327 }
328 }
329 
330 #endif
std::pair< AnyReference, bool > convert(TypeInterface *targetType) const
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:319
AutoRefDestroy(AnyReference ref)
void destroy()
Stop and flush the logging system.
void futureAdapter(const Future< FT > &f, Promise< PT > p, CONV converter)
Definition: future.hxx:610
#define qiLogDebug(...)
Definition: log.hpp:68
void setError(const std::string &msg)
Definition: future_fwd.hpp:767
#define QI_ASSERT(expr__)
Definition: assert.hpp:27
T extractFuture(const qi::Future< qi::AnyReference > &metaFut)
bool isCanceled() const
Definition: future_fwd.hpp:269
void futureAdapterGeneric(AnyReference val, qi::Promise< T > promise, boost::shared_ptr< GenericObject > &ao)
void setPromise(qi::Promise< T > &promise, AnyValue &v)
void destroy()
Deletes storage.
R call(const std::string &methodName, Args &&...args)
#define qiLogError(...)
Log in error mode.
Definition: log.hpp:112
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:317
boost::shared_ptr< GenericObject > getGenericFuture(AnyReference val, TypeKind *kind=0)
AnyReference asReference() const
Definition: anyvalue.hpp:83
const char * infoString()
void hold(T data)
void setCanceled()
Definition: future_fwd.hpp:774
void setOnCancel(boost::function< void(qi::Promise< T > &)> cancelCallback)
Definition: future_fwd.hpp:802
const std::string & error(int msecs=FutureTimeout_Infinite) const
Definition: future_fwd.hpp:297
const ValueType & value(int msecs=FutureTimeout_Infinite) const
Return the value associated to a Future.
Definition: future_fwd.hpp:214
void futureAdapter< void >(const qi::Future< qi::AnyReference > &metaFut, qi::Promise< void > promise)
TypeKind
Definition: fwd.hpp:53
void setValue(const ValueType &value)
Definition: future_fwd.hpp:760
bool handleFuture(AnyReference val, Promise< T > promise)
std::enable_if< std::is_function< RF >::value, boost::function< RF > >::type bind(AF &&fun, Arg0 &&arg0, Args &&...args)
Definition: trackable.hxx:327
void extractFuture< void >(const qi::Future< qi::AnyReference > &metaFut)
qi::Signature signature(void *storage=0, bool resolveDynamic=false)
bool hasError(int msecs=FutureTimeout_Infinite) const
Definition: future_fwd.hpp:278