signal-caster.h 6.41 KB
Newer Older
Thomas Moulard's avatar
Thomas Moulard committed
1
2
3
4
5
// -*- c++-mode -*-
// Copyright 2010 François Bleibel Thomas Moulard, Olivier Stasse
//

#ifndef DYNAMIC_GRAPH_SIGNAL_CASTER_HH
Bergé's avatar
Bergé committed
6
7
8
9
#define DYNAMIC_GRAPH_SIGNAL_CASTER_HH
#include <map>
#include <typeinfo>
#include <vector>
Thomas Moulard's avatar
Thomas Moulard committed
10

Bergé's avatar
Bergé committed
11
12
13
14
15
16
#include <boost/any.hpp>
#include <boost/format.hpp>
#include <boost/function/function1.hpp>
#include <boost/function/function2.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tuple/tuple.hpp>
Thomas Moulard's avatar
Thomas Moulard committed
17

Bergé's avatar
Bergé committed
18
#include "dynamic-graph/exception-signal.h"
19
#include <dynamic-graph/dynamic-graph-api.h>
Joseph Mirabel's avatar
Joseph Mirabel committed
20
#include <dynamic-graph/linear-algebra.h>
Joseph Mirabel's avatar
Joseph Mirabel committed
21
#include <dynamic-graph/eigen-io.h>
Thomas Moulard's avatar
Thomas Moulard committed
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
namespace dynamicgraph {
/// This singleton class allows serialization of a number of objects into
/// (disp) and from (cast) std i/o streams.
///
/// The transformation is done at run-time, i.e. SignalCaster
/// doesn't know about the type of objects it casts to.
///
/// It also allows registering of user-defined casts. A cast is
/// identified by the compiler. The mapping from a type to a
/// serialization function is dynamic, hence it is more complex than
/// a typical template-based compile-time resolve. So disp, cast and
/// trace are costly functions and should be used as such.
class DYNAMIC_GRAPH_DLLAPI SignalCaster {
public:
  virtual ~SignalCaster();
  /// Destroy the unique instance.
  static void destroy();
  /// Typedef of displayer functions that take an encapsulated 'any'
  /// object and displays, cast, or trace it on an output stream
  /// (serialization).
  typedef boost::function1<boost::any, std::istringstream &> caster_type;
Thomas Moulard's avatar
Thomas Moulard committed
44

45
46
47
48
49
  /// Get a reference to the unique object of the class.
  static SignalCaster *getInstance(void);
  /// Casts an object using a registered cast function.
  boost::any cast(const std::type_info &, std::istringstream &iss);
  /// Registers a cast.
50
  void registerCast(const std::type_info &type,
51
                    caster_type caster);
52
53
54
55
56
57
  /// Unregister a cast.
  void unregisterCast(const std::type_info &type);
  /// Checks if there is a displayer registered with type_name.
  bool existsCast(const std::type_info &type) const;
  /// Return the list of type names registered.
  std::vector<std::string> listTypenames() const;
Thomas Moulard's avatar
Thomas Moulard committed
58

59
60
private:
  /// Container for the three cast functions.
61
  typedef boost::tuple<caster_type>
62
      cast_functions_type;
63

64
65
  /// \brief Retrieve cast structure from its name.
  cast_functions_type &getCast(const std::string &type_name);
Thomas Moulard's avatar
Thomas Moulard committed
66

67
68
69
  /// This map associates the typename of objects and the corresponding
  /// using boost::function with 'compatible' syntax
  std::map<std::string, cast_functions_type> functions_;
70

71
  std::map<std::string, const std::type_info *> type_info_;
Thomas Moulard's avatar
Thomas Moulard committed
72

73
74
75
76
77
private:
  explicit SignalCaster();
  /// Pointer to the unique instance of the class.
  static SignalCaster *instance_;
};
Thomas Moulard's avatar
Thomas Moulard committed
78

79
80
81
82
83
84
85
86
/// The SignalCast registerer class. Can be used to automatically
/// register a cast when instanced somewhere in a cpp file. Pass the
/// typeid () of the type you want to register a cast to as the first
/// argument. The code is provided here so the class does not need
/// to be exported.
class DYNAMIC_GRAPH_DLLAPI SignalCastRegisterer {
public:
  inline SignalCastRegisterer(const std::type_info &type,
87
                              SignalCaster::caster_type caster) {
88
    SignalCaster::getInstance()->registerCast(type, caster);
Thomas Moulard's avatar
Thomas Moulard committed
89
  }
90
};
Thomas Moulard's avatar
Thomas Moulard committed
91

Joseph Mirabel's avatar
Joseph Mirabel committed
92
93
94
95
96
97
98
99
100
101
102
103
/// Template class used to serialize a signal value.
template <typename T> struct signal_disp {
inline static void run (const T &value, std::ostream &os) { os << value << '\n'; }
};

/// Template specialization of signal_disp for Eigen objects
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
struct signal_disp<Eigen::Matrix< _Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols > > {
inline static void run(const Eigen::Matrix< _Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols > &value, std::ostream &os) {
  static const Eigen::IOFormat row_format (Eigen::StreamPrecision,
      Eigen::DontAlignCols, " ", " ", "", "", "", "");
  os << value.format(row_format);
104
}
Joseph Mirabel's avatar
Joseph Mirabel committed
105
106
107
108
109
110
111
};

/// Template specialization of signal_disp for std::string.
/// Do not print '\n' at the end.
template <> struct signal_disp<std::string> {
inline static void run (const std::string &value, std::ostream &os) { os << value; }
};
Thomas Moulard's avatar
Thomas Moulard committed
112

Joseph Mirabel's avatar
Joseph Mirabel committed
113
114
115
116
117
118
119
120
121
122
/// Template class used to deserialize a signal value (reverse of signal_disp).
template <typename T> struct signal_cast {
inline static T run (std::istringstream &iss) {
  T inst;
  iss >> inst;
  if (iss.fail()) {
    throw ExceptionSignal(ExceptionSignal::GENERIC,
        "failed to serialize " + iss.str());
  }
  return inst;
123
}
Joseph Mirabel's avatar
Joseph Mirabel committed
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
};

/// Template specialization of signal_cast for std::string.
template <> struct signal_cast<std::string> {
inline static std::string run (std::istringstream &iss) { return iss.str(); }
};

/// Template specialization of signal_cast for double
/// to workaround the limitations of the stream based approach.
///
/// When dealing with double: displaying a double on a stream
/// is *NOT* the opposite of reading a double from a stream.
///
/// In practice, it means that there is no way to read
/// a NaN, +inf, -inf from a stream!
///
/// To workaround this problem, parse special values manually
/// (the strings used are the one produces by displaying special
/// values on a stream).
template <> struct signal_cast<double> {
inline static double run (std::istringstream &iss) {
  std::string tmp (iss.str());

  if (tmp == "nan")
    return std::numeric_limits<double>::quiet_NaN();
  else if (tmp == "inf" || tmp == "+inf")
    return std::numeric_limits<double>::infinity();
  else if (tmp == "-inf")
    return -1. * std::numeric_limits<double>::infinity();

  try {
    return boost::lexical_cast<double>(tmp);
  } catch (boost::bad_lexical_cast &) {
    boost::format fmt("failed to serialize %s (to double)");
    fmt % tmp;
    throw ExceptionSignal(ExceptionSignal::GENERIC, fmt.str());
  }
}
};
Bergé's avatar
Bergé committed
163

Joseph Mirabel's avatar
Joseph Mirabel committed
164
165
166
167
168
169
170
171
172
173
174
/// Template class used to display a signal value.
template <typename T> struct signal_trace {
inline static void run(const T &value, std::ostream &os) { os << value << '\n'; }
};

/// Template specialization of signal_trace for Eigen objects
template <typename Derived> struct signal_trace<Eigen::DenseBase<Derived> > {
inline static void run(const Eigen::DenseBase<Derived> &value, std::ostream &os) {
  static const Eigen::IOFormat row_format (Eigen::StreamPrecision,
      Eigen::DontAlignCols, ", ", ", ", "", "", "", "\n");
  os << value.format(row_format);
175
}
Joseph Mirabel's avatar
Joseph Mirabel committed
176
177
};

178
} // end of namespace dynamicgraph.
Thomas Moulard's avatar
Thomas Moulard committed
179

180
#endif //! DYNAMIC_GRAPH_SIGNAL_CASTER_HH