Intel® RealSense™ Cross Platform API
Intel Realsense Cross-platform API
api.h
Go to the documentation of this file.
1 // License: Apache 2.0. See LICENSE file in root directory.
2 // Copyright(c) 2017 Intel Corporation. All Rights Reserved.
3 
4 
5 #pragma once
6 #include "context.h"
7 #include "core/extension.h"
8 #include "device.h"
9 
10 #include <type_traits>
11 #include <iostream>
12 
14 {
15  std::vector<uint8_t> buffer;
16 };
17 
18 struct rs2_error
19 {
20  std::string message;
21  const char* function;
22  std::string args;
24 };
25 
27 {
29  :_notification(notification) {}
30 
32 };
33 
34 struct rs2_device
35 {
36  std::shared_ptr<librealsense::context> ctx;
37  std::shared_ptr<librealsense::device_info> info;
38  std::shared_ptr<librealsense::device_interface> device;
39 };
40 
41 namespace librealsense
42 {
43  // Facilities for streaming function arguments
44 
45  // First, we define a generic parameter streaming
46  // Assumes T is streamable, reasonable for C API parameters
47  template<class T, bool S>
48  struct arg_streamer
49  {
50  void stream_arg(std::ostream & out, const T& val, bool last)
51  {
52  out << ':' << val << (last ? "" : ", ");
53  }
54  };
55 
56  // Next we define type trait for testing if *t for some T* is streamable
57  template<typename T>
59  {
60  template <typename S>
61  static auto test(const S* t) -> decltype(std::cout << **t);
62  static auto test(...)->std::false_type;
63  public:
65  };
66 
67  // Using above trait, we can now specialize our streamer
68  // for streamable pointers:
69  template<class T>
70  struct arg_streamer<T*, true>
71  {
72  void stream_arg(std::ostream & out, T* val, bool last)
73  {
74  out << ':'; // If pointer not null, stream its content
75  if (val) out << *val;
76  else out << "nullptr";
77  out << (last ? "" : ", ");
78  }
79  };
80 
81  // .. and for not streamable pointers
82  template<class T>
83  struct arg_streamer<T*, false>
84  {
85  void stream_arg(std::ostream & out, T* val, bool last)
86  {
87  out << ':'; // If pointer is not null, stream the pointer
88  if (val) out << (int*)val; // Go through (int*) to avoid dumping the content of char*
89  else out << "nullptr";
90  out << (last ? "" : ", ");
91  }
92  };
93 
94  // This facility allows for translation of exceptions to rs2_error structs at the API boundary
95  template<class T> void stream_args(std::ostream & out, const char * names, const T & last)
96  {
97  out << names;
99  s.stream_arg(out, last, true);
100  }
101  template<class T, class... U> void stream_args(std::ostream & out, const char * names, const T & first, const U &... rest)
102  {
103  while (*names && *names != ',') out << *names++;
105  s.stream_arg(out, first, false);
106  while (*names && (*names == ',' || isspace(*names))) ++names;
107  stream_args(out, names, rest...);
108  }
109 
110  static void translate_exception(const char * name, std::string args, rs2_error ** error)
111  {
112  try { throw; }
113  catch (const librealsense_exception& e) { if (error) *error = new rs2_error{ e.what(), name, move(args), e.get_exception_type() }; }
114  catch (const std::exception& e) { if (error) *error = new rs2_error{ e.what(), name, move(args) }; }
115  catch (...) { if (error) *error = new rs2_error{ "unknown error", name, move(args) }; }
116  }
117 
118 #ifdef TRACE_API
119  // API objects repository holds all live objects
120  // created from API calls and not released yet.
121  // This is useful for two tasks:
122  // 1. More easily follow API calls in logs
123  // 2. See a snapshot of all not deallocated objects
124  class api_objects
125  {
126  public:
127  // Define singleton
128  static api_objects& instance() {
129  static api_objects instance;
130  return instance;
131  }
132 
133  // Place new object into the registry and give it a name
134  // according to the class type and the index of the instance
135  // to be presented in the log instead of the object memory address
136  std::string register_new_object(const std::string& type, const std::string& address)
137  {
138  std::lock_guard<std::mutex> lock(_m);
139  return internal_register(type, address);
140  }
141 
142  // Given a list of parameters in form of "param:val,param:val"
143  // This function will replace all val that have alternative names in this repository
144  // with their names
145  // the function is used by the log with param = function input param name from the prototype,
146  // and val = the address of the param.
147  // the function will change the val field from memory address to instance class and index name
148  std::string augment_params(std::string p)
149  {
150  std::lock_guard<std::mutex> lock(_m);
151  std::string acc = "";
152  std::string res = "";
153  std::string param = "";
154  p += ",";
155  bool is_value = false;
156  for (auto i = 0; i < p.size(); i++)
157  {
158  if (p[i] == ':')
159  {
160  param = acc;
161  acc = "";
162  is_value = true;
163  }
164  else if (is_value)
165  {
166  if (p[i] == ',')
167  {
168  auto it = _names.find(acc);
169  if (it != _names.end()) acc = it->second;
170  else
171  {
172  // Heuristic: Assume things that are the same length
173  // as pointers are in-fact pointers
174  std::stringstream ss; ss << (int*)0;
175  if (acc.size() == ss.str().size())
176  {
177  acc = internal_register(param, acc);
178  }
179  }
180  res += param + ":" + acc;
181  if (i != p.size() - 1) res += ",";
182  acc = "";
183  is_value = false;
184  }
185  else acc += p[i];
186  }
187  else acc += p[i];
188  }
189  return res;
190  }
191 
192  // Mark that a certain object is de-allocated
193  void remove_object(const std::string& name)
194  {
195  std::lock_guard<std::mutex> lock(_m);
196  auto it = _names.find(name);
197  if (it != _names.end())
198  _names.erase(it);
199  }
200  private:
201  std::string internal_register(const std::string& type, const std::string& address)
202  {
203  auto it = _counters.find(type);
204  if (it == _counters.end()) _counters[type] = 0;
205  _counters[type]++;
206  std::stringstream ss;
207  ss << type << _counters[type];
208  _names[address] = ss.str();
209  return _names[address];
210  }
211 
212  std::mutex _m;
213  std::map<std::string, std::string> _names;
214  std::map<std::string, int> _counters;
215  };
216 
217  // This class wraps every API call and reports on it once its done
218  class api_logger
219  {
220  public:
221  api_logger(std::string function)
222  : _function(std::move(function)), _result(""), _param_str(""), _type(""),
223  _params([]() { return std::string{}; })
224  {
225  _start = std::chrono::high_resolution_clock::now();
226  LOG_DEBUG("/* Begin " << _function << " */");
227 
228  // Define the returning "type" as the last word in function name
229  std::size_t found = _function.find_last_of("_");
230  _type = _function.substr(found + 1);
231 
232  check_if_deleter("rs2_delete");
233  check_if_deleter("rs2_release");
234  }
235 
236  // if the calling function is going to release an API object - remove it from the tracing list
237  void check_if_deleter(const char* prefix)
238  {
239  auto deleter_pos = _function.find(prefix);
240  if (deleter_pos == 0)
241  {
242  _is_deleter = true;
243  }
244  }
245 
246  void set_return_value(std::string result)
247  {
248  _result = std::move(result);
249  }
250 
251  void set_params(std::function<std::string()> params)
252  {
253  _params = std::move(params);
254  }
255 
256  std::string get_params() { return _param_str = _params(); }
257 
258  void report_error() { _error = true; }
259  void report_pointer_return_type() { _returns_pointer = true; }
260 
261  ~api_logger()
262  {
263  auto d = std::chrono::high_resolution_clock::now() - _start;
264  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(d).count();
265  std::stringstream ss;
266  std::string prefix = "";
267  if (_param_str == "") _param_str = _params();
268 
269  auto&& objs = api_objects::instance();
270  if (_returns_pointer)
271  {
272  prefix = objs.register_new_object(_type, _result) + " = ";
273  _result = "";
274  }
275  if (_is_deleter) objs.remove_object(_result);
276  if (_param_str != "") _param_str = objs.augment_params(_param_str);
277 
278  ss << prefix << _function << "(" << _param_str << ")";
279  if (_result != "") ss << " returned " << _result;
280  ss << ";";
281  if (ms > 0) ss << " /* Took " << ms << "ms */";
282  if (_error) LOG_ERROR(ss.str());
283  else LOG_INFO(ss.str());
284  }
285  private:
286  std::string _function, _result, _type, _param_str;
287  std::function<std::string()> _params;
288  bool _returns_pointer = false;
289  bool _is_deleter = false;
290  bool _error = false;
291  std::chrono::high_resolution_clock::time_point _start;
292  };
293 
294  // This dummy helper function lets us fetch return type from lambda
295  // this is used for result_printer, to be able to be used
296  // similarly for functions with and without return parameter
297  template<typename F, typename R>
298  R fetch_return_type(F& f, R(F::*mf)() const);
299 
300  // Result printer class will capture lambda result value
301  // and pass it as string to the api logger
302  template<class T>
303  class result_printer
304  {
305  public:
306  result_printer(api_logger* logger) : _logger(logger) {}
307 
308  template<class F>
309  T invoke(F func)
310  {
311  _res = func(); // Invoke lambda and save result for later
312  // I assume this will not have any side-effects, since it is applied
313  // to types that are about to be passed from C API
314  return _res;
315  }
316 
317  ~result_printer()
318  {
319  std::stringstream ss; ss << _res;
320  _logger->set_return_value(ss.str());
321  }
322  private:
323  T _res;
324  api_logger* _logger;
325  };
326 
327  // Result printer class for T* should not dump memory content
328  template<class T>
329  class result_printer<T*>
330  {
331  public:
332  result_printer(api_logger* logger) : _logger(logger) {}
333 
334  template<class F>
335  T* invoke(F func)
336  {
337  _res = func(); // Invoke lambda and save result for later
338  // I assume this will not have any side-effects, since it is applied
339  // to types that are about to be passed from C API
340  return _res;
341  }
342 
343  ~result_printer()
344  {
345  std::stringstream ss; ss << (int*)_res; // Force case to int* to avoid char* and such
346  // being dumped to log
347  _logger->report_pointer_return_type();
348  _logger->set_return_value(ss.str());
349  }
350  private:
351  T* _res;
352  api_logger* _logger;
353  };
354 
355  // To work-around the fact that void can't be "stringified"
356  // we define an alternative printer just for void returning lambdas
357  template<>
358  class result_printer<void>
359  {
360  public:
361  result_printer(api_logger* logger) {}
362  template<class F>
363  void invoke(F func) { func(); }
364  };
365 
366 // Begin API macro defines new API logger and redirects function body into a lambda
367 // The extra brace ensures api_logger will die after everything else
368 #define BEGIN_API_CALL { api_logger __api_logger(__FUNCTION__); {\
369 auto func = [&](){
370 
371 // The various return macros finish the lambda and invoke it using the type printer
372 // practically capturing function return value
373 // In addition, error flag and function parameters are captured into the API logger
374 #define NOEXCEPT_RETURN(R, ...) };\
375 result_printer<decltype(fetch_return_type(func, &decltype(func)::operator()))> __p(&__api_logger);\
376 __api_logger.set_params([&](){ std::ostringstream ss; librealsense::stream_args(ss, #__VA_ARGS__, __VA_ARGS__); return ss.str(); });\
377 try {\
378 return __p.invoke(func);\
379 } catch(...) {\
380 rs2_error* e; librealsense::translate_exception(__FUNCTION__, __api_logger.get_params(), &e);\
381 LOG_WARNING(rs2_get_error_message(e)); rs2_free_error(e); __api_logger.report_error(); return R; } } }
382 
383 #define HANDLE_EXCEPTIONS_AND_RETURN(R, ...) };\
384 result_printer<decltype(fetch_return_type(func, &decltype(func)::operator()))> __p(&__api_logger);\
385 __api_logger.set_params([&](){ std::ostringstream ss; librealsense::stream_args(ss, #__VA_ARGS__, __VA_ARGS__); return ss.str(); });\
386 try {\
387 return __p.invoke(func);\
388 } catch(...) {\
389 librealsense::translate_exception(__FUNCTION__, __api_logger.get_params(), error); __api_logger.report_error(); return R; } } }
390 
391 #define NOARGS_HANDLE_EXCEPTIONS_AND_RETURN(R, ...) };\
392 result_printer<decltype(fetch_return_type(func, &decltype(func)::operator()))> __p(&__api_logger);\
393 try {\
394 return __p.invoke(func);\
395 } catch(...) { librealsense::translate_exception(__FUNCTION__, "", error); __api_logger.report_error(); return R; } } }
396 
397 #else // No API tracing:
398 
399 #define BEGIN_API_CALL try
400 #define NOEXCEPT_RETURN(R, ...) catch(...) { std::ostringstream ss; librealsense::stream_args(ss, #__VA_ARGS__, __VA_ARGS__); rs2_error* e; librealsense::translate_exception(__FUNCTION__, ss.str(), &e); LOG_WARNING(rs2_get_error_message(e)); rs2_free_error(e); return R; }
401 #define HANDLE_EXCEPTIONS_AND_RETURN(R, ...) catch(...) { std::ostringstream ss; librealsense::stream_args(ss, #__VA_ARGS__, __VA_ARGS__); librealsense::translate_exception(__FUNCTION__, ss.str(), error); return R; }
402 #define NOARGS_HANDLE_EXCEPTIONS_AND_RETURN(R) catch(...) { librealsense::translate_exception(__FUNCTION__, "", error); return R; }
403 
404 #endif
405 
406 
407  #define VALIDATE_NOT_NULL(ARG) if(!(ARG)) throw std::runtime_error("null pointer passed for argument \"" #ARG "\"");
408  #define VALIDATE_ENUM(ARG) if(!librealsense::is_valid(ARG)) { std::ostringstream ss; ss << "invalid enum value for argument \"" #ARG "\""; throw librealsense::invalid_value_exception(ss.str()); }
409  #define VALIDATE_RANGE(ARG, MIN, MAX) if((ARG) < (MIN) || (ARG) > (MAX)) { std::ostringstream ss; ss << "out of range value for argument \"" #ARG "\""; throw librealsense::invalid_value_exception(ss.str()); }
410  #define VALIDATE_LE(ARG, MAX) if((ARG) > (MAX)) { std::ostringstream ss; ss << "out of range value for argument \"" #ARG "\""; throw std::runtime_error(ss.str()); }
411  #define VALIDATE_INTERFACE_NO_THROW(X, T) \
412  ([&]() -> T* { \
413  T* p = dynamic_cast<T*>(&(*X)); \
414  if (p == nullptr) \
415  { \
416  auto ext = dynamic_cast<librealsense::extendable_interface*>(&(*X)); \
417  if (ext == nullptr) return nullptr; \
418  else \
419  { \
420  if(!ext->extend_to(TypeToExtension<T>::value, (void**)&p)) \
421  return nullptr; \
422  return p; \
423  } \
424  } \
425  return p; \
426  })()
427 
428  #define VALIDATE_INTERFACE(X,T) \
429  ([&]() -> T* { \
430  T* p = VALIDATE_INTERFACE_NO_THROW(X,T); \
431  if(p == nullptr) \
432  throw std::runtime_error("Object does not support \"" #T "\" interface! " ); \
433  return p; \
434  })()
435 }
rs2_exception_type
Exception types are the different categories of errors that RealSense API might return.
Definition: rs_types.h:28
Definition: backend.h:374
Definition: backend.h:378
const librealsense::notification * _notification
Definition: api.h:31
const char * what() const noexcept override
Definition: types.h:148
std::shared_ptr< librealsense::device_info > info
Definition: api.h:37
Definition: api.h:48
Definition: api.h:18
std::string args
Definition: api.h:22
rs2_exception_type exception_type
Definition: api.h:23
void stream_arg(std::ostream &out, T *val, bool last)
Definition: api.h:85
std::shared_ptr< librealsense::device_interface > device
Definition: api.h:38
Definition: algo.h:16
#define LOG_DEBUG(...)
Definition: types.h:108
void stream_arg(std::ostream &out, const T &val, bool last)
Definition: api.h:50
Definition: api.h:34
void stream_args(std::ostream &out, const char *names, const T &first, const U &... rest)
Definition: api.h:101
rs2_notification(const librealsense::notification *notification)
Definition: api.h:28
#define LOG_INFO(...)
Definition: types.h:109
std::shared_ptr< librealsense::context > ctx
Definition: api.h:36
rs2_exception_type get_exception_type() const noexcept
Definition: types.h:143
void stream_arg(std::ostream &out, T *val, bool last)
Definition: api.h:72
Definition: types.h:896
Definition: api.h:26
Definition: api.h:58
std::string message
Definition: api.h:20
std::vector< uint8_t > buffer
Definition: api.h:15
Definition: api.h:13
#define LOG_ERROR(...)
Definition: types.h:111