OSVR-Core  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
CSV.h
Go to the documentation of this file.
1 
11 // Copyright 2015 Sensics, Inc.
12 //
13 // Licensed under the Apache License, Version 2.0 (the "License");
14 // you may not use this file except in compliance with the License.
15 // You may obtain a copy of the License at
16 //
17 // http://www.apache.org/licenses/LICENSE-2.0
18 //
19 // Unless required by applicable law or agreed to in writing, software
20 // distributed under the License is distributed on an "AS IS" BASIS,
21 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 // See the License for the specific language governing permissions and
23 // limitations under the License.
24 
25 #ifndef INCLUDED_CSV_h_GUID_30FA2504_8CCF_4B87_71C7_5974F968BC19
26 #define INCLUDED_CSV_h_GUID_30FA2504_8CCF_4B87_71C7_5974F968BC19
27 
28 // Internal Includes
29 // - none
30 
31 // Library/third-party includes
32 // - none
33 
34 // Standard includes
35 #include <cassert>
36 #include <iomanip>
37 #include <limits>
38 #include <sstream>
39 #include <string>
40 #include <type_traits>
41 #include <unordered_map>
42 #include <vector>
43 
44 namespace osvr {
45 namespace util {
46  template <typename Derived> class CSVBase;
47  namespace detail {
48  template <typename T> class CellBase {
49  protected:
50  explicit CellBase(T const &data) : data_(data) {}
51 
52  public:
53  T const &getData() const { return data_; }
54 
55  private:
56  T const &data_;
57  };
62  template <typename T, typename HeaderType = std::string> class Cell;
63  template <typename T> class Cell<T, std::string> : public CellBase<T> {
64  public:
65  using Base = CellBase<T>;
66  Cell(std::string const &header, T const &data)
67  : Base(data), header_(header) {}
68 
69  Cell(const char *header, T const &data)
70  : Base(data), header_(header) {}
71  Cell(std::string &&header, T const &data)
72  : Base(data), header_(std::move(header)) {}
73 
74  std::string const &getHeader() const { return header_; }
75 
76  private:
77  std::string header_;
78  };
79 
80  template <typename T, typename Dummy = void> struct CellStreamPrep {
81  static void prepareStream(std::ostream &) {
83  }
84  };
85 
86  template <typename T>
87  struct CellStreamPrep<T, typename std::enable_if<
88  std::is_floating_point<T>::value>::type> {
89  static void prepareStream(std::ostream &os) {
90  // Floating point values: please set precision to maximum.
91  os << std::setprecision(std::numeric_limits<T>::digits10 + 1);
92  }
93  };
94 
96  template <typename T>
97  inline void prepareStream(std::ostream &os, T const &cell) {
98  using DataValueConstType =
99  typename std::remove_reference<decltype(cell.getData())>::type;
100  using DataValueType =
101  typename std::remove_const<DataValueConstType>::type;
102  using PrepType = CellStreamPrep<DataValueType>;
103  PrepType::prepareStream(os);
104  }
105 
112  template <typename Derived> class CSVRowProxy {
113  public:
115  friend class CSVBase<Derived>;
116  using CSV = Derived;
117  using type = CSVRowProxy<Derived>;
118 
119  private:
121  explicit CSVRowProxy(BaseCSVType &csv) : csv_(csv) {}
122 
123  public:
125  CSVRowProxy(CSVRowProxy const &) = delete;
127  CSVRowProxy &operator=(CSVRowProxy const &) = delete;
130  : csv_(other.csv_), preparedRow_(other.preparedRow_),
131  active_(other.active_) {
134  other.active_ = false;
135  }
136 
142  if (active_ && preparedRow_) {
143  csv_.finalizeLatestRow();
144  }
145  }
146 
151  template <typename... CellArgs>
152  void add(Cell<CellArgs...> const &c) {
153  commonPreAdd();
154  std::ostringstream os;
155  prepareStream(os, c);
156  os << c.getData();
157  commonPostAdd(c.getHeader(), os.str());
158  }
159 
160  private:
163  void commonPreAdd() {
164  if (!preparedRow_) {
165  csv_.prepareForRow();
166  preparedRow_ = true;
167  }
168  }
171  void commonPostAdd(std::string const &header,
172  std::string const &value) {
173  csv_.dataForLatestRow(header, value);
174  }
175 
176  BaseCSVType &csv_;
177  bool preparedRow_ = false;
178  bool active_ = true;
179  };
180 
181  namespace csv {
182  static const char COMMA[] = ",";
183  static const char DOUBLEQUOTE[] = "\"";
184  static const char DOUBLEQUOTE_COMMA[] = "\",";
185  static const char NEWLINE[] = "\n";
186  } // namespace csv
187 
190  public:
191  using DataRow = std::vector<std::string>;
192  using column_id = std::size_t;
193  column_id getColumn(std::string const &heading) {
194  auto it = columnsMap_.find(heading);
195  // If we don't find it, this is where it will be.
196  column_id ret = columns_.size();
197  if (end(columnsMap_) != it) {
198  // OK, found it somewhere already.
199  ret = it->second;
200  return ret;
201  }
202  // didn't find it, add it.
203  columns_.push_back(heading);
204  columnsMap_.insert(std::make_pair(heading, ret));
205  return ret;
206  }
207  column_id numColumns() const { return columns_.size(); }
208 
209  protected:
210  void outputHeaders(std::ostream &os) const {
211  for (auto &colName : columns_) {
212  os << csv::DOUBLEQUOTE << colName << csv::DOUBLEQUOTE_COMMA;
213  }
214  os << csv::NEWLINE;
215  }
216 
217  private:
218  std::vector<std::string> columns_;
219  std::unordered_map<std::string, column_id> columnsMap_;
220  };
221 
223  template <typename Derived, typename... CellArgs>
224  inline CSVRowProxy<Derived> &&
225  operator<<(CSVRowProxy<Derived> &&row, Cell<CellArgs...> const &cell) {
226  row.add(cell);
227  return std::move(row);
228  }
229 
231  template <typename Derived, typename... CellArgs>
232  inline CSVRowProxy<Derived> &operator<<(CSVRowProxy<Derived> &row,
233  Cell<CellArgs...> const &cell) {
234  row.add(cell);
235  return row;
236  }
237  } // namespace detail
238 
247  template <typename Derived> class CSVBase : public detail::CSVCommonBase {
248  public:
249  friend class detail::CSVRowProxy<Derived>;
250  using RowProxy = detail::CSVRowProxy<Derived>;
251 
255  RowProxy row() { return RowProxy(*this); }
256 
261  std::size_t numDataRows() const { return data_.size(); }
262 
266  std::size_t numRows() const { return rows_; }
267 
268  protected:
270  void prepareForRow() { latestRow().clear(); }
272  void dataForLatestRow(std::string const &heading,
273  std::string const &data) {
274  auto col = getColumn(heading);
275  ensureLatestRowCanHoldColId(col);
276  latestRow()[col] = data;
277  }
282  rows_++;
283  derived().finalizeLatestRow();
284  }
285 
288  DataRow &latestRow() { return latestRow_; }
289 
292  void outputRow(std::ostream &os, DataRow const &row) const {
293  for (auto &cell : row) {
294  os << cell << detail::csv::COMMA;
295  }
296  os << detail::csv::NEWLINE;
297  }
298 
300  void outputData(std::ostream &os) const {
301  for (auto &row : data_) {
302  outputRow(os, row);
303  }
304  }
305 
308  data_.emplace_back(std::move(latestRow()));
309  }
310 
311  private:
313  void ensureLatestRowCanHoldColId(column_id id) {
314  if (id >= latestRow().size()) {
315  latestRow().resize(id + 1);
316  }
317  }
318 
320  Derived const &derived() const {
321  return *static_cast<Derived const *>(this);
322  }
324  Derived &derived() { return *static_cast<Derived *>(this); }
325  std::size_t rows_ = 0;
326  std::vector<DataRow> data_;
327  DataRow latestRow_;
328  };
329 
333  class CSV : public CSVBase<CSV> {
334  using Base = CSVBase<CSV>;
335  friend class CSVBase<CSV>;
336 
337  public:
340  void output(std::ostream &os) const {
341  Base::outputHeaders(os);
342  Base::outputData(os);
343  }
344 
345  private:
347  void finalizeLatestRow() { Base::moveLatestRowToData(); }
348  };
349 
358  class StreamCSV : public CSVBase<StreamCSV> {
359  using Base = CSVBase<StreamCSV>;
360  friend class CSVBase<StreamCSV>;
361 
362  public:
366  explicit StreamCSV(std::ostream &os) : m_stream(os) {}
367 
371  void startOutput() {
372  Base::outputHeaders(m_stream);
373  outputData(m_stream);
374  m_streaming = true;
375  }
376 
377  private:
380  std::ostream &m_stream;
381 
385  bool m_streaming = false;
386 
389  void finalizeLatestRow() {
390  if (m_streaming) {
392  Base::outputRow(m_stream, Base::latestRow());
393  } else {
395  }
396  }
397  };
398 
400  template <typename T>
401  inline detail::Cell<T> cell(const char *header, T const &data) {
402  return detail::Cell<T>{header, data};
403  }
404 
406  template <typename T>
407  inline detail::Cell<T> cell(std::string const &header, T const &data) {
408  return detail::Cell<T>{header, data};
409  }
410 
411 } // namespace util
412 } // namespace osvr
413 
414 #endif // INCLUDED_CSV_h_GUID_30FA2504_8CCF_4B87_71C7_5974F968BC19
CSVRowProxy & operator=(CSVRowProxy const &)=delete
non-assignable
void moveLatestRowToData()
utility function for use in derived finalizeLatestRow()
Definition: CSV.h:307
void finalizeLatestRow()
Definition: CSV.h:281
CSVRowProxy(CSVRowProxy &&other)
move constructible
Definition: CSV.h:129
The main namespace for all C++ elements of the framework, internal and external.
Definition: ClientKit.h:31
STL namespace.
void output(std::ostream &os) const
Definition: CSV.h:340
detail::size< coerce_list< Ts...>> size
Get the size of a list (number of elements.)
Definition: Size.h:56
StreamCSV(std::ostream &os)
Definition: CSV.h:366
void outputData(std::ostream &os) const
Called by derived classes to output stored data rows.
Definition: CSV.h:300
void outputRow(std::ostream &os, DataRow const &row) const
Definition: CSV.h:292
void prepareForRow()
Called by CSVRowProxy life cycle, on row creation.
Definition: CSV.h:270
void dataForLatestRow(std::string const &heading, std::string const &data)
Called by CSVRowProxy life cycle, on cell addition.
Definition: CSV.h:272
std::size_t numRows() const
Definition: CSV.h:266
void startOutput()
Definition: CSV.h:371
std::size_t numDataRows() const
Definition: CSV.h:261
void add(Cell< CellArgs...> const &c)
Definition: CSV.h:152
RowProxy row()
Definition: CSV.h:255
DataRow & latestRow()
Definition: CSV.h:288
Truly shared base class for all CSV implementations.
Definition: CSV.h:189
detail::Cell< T > cell(const char *header, T const &data)
Helper free function to make a CSV cell.
Definition: CSV.h:401