OSVR-Core
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 {
394  Base::moveLatestRowToData();
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
std::size_t numDataRows() const
Gets the number of rows in the internal data storage.
Definition: CSV.h:261
std::size_t numRows() const
Gets the total number of rows that have been streamed/added to this CSV object, whether or not they&#39;r...
Definition: CSV.h:266
Definition: RunLoopManager.h:42
CSVRowProxy(CSVRowProxy &&other)
move constructible
Definition: CSV.h:129
void prepareStream(std::ostream &os, T const &cell)
Invokes the trait to prepare a stream for a data type.
Definition: CSV.h:97
The main namespace for all C++ elements of the framework, internal and external.
Definition: namespace_osvr.dox:3
Definition: TypeSafeIdHash.h:44
Returned by calls to .row() on a CSV object (you&#39;ll never need instantiate manually), and only really interacted with using the operator<< and cell() calls.
Definition: CSV.h:112
void dataForLatestRow(std::string const &heading, std::string const &data)
Called by CSVRowProxy life cycle, on cell addition.
Definition: CSV.h:272
RowProxy row()
Main call for the CSV object: returns a proxy object that you can redirect "cells" into...
Definition: CSV.h:255
void finalizeLatestRow()
Called by CSVRowProxy life cycle, on destruction of active row proxy.
Definition: CSV.h:281
detail::size< coerce_list< Ts... >> size
Get the size of a list (number of elements.)
Definition: Size.h:56
static void prepareStream(std::ostream &)
Definition: CSV.h:81
StreamCSV(std::ostream &os)
Constructor.
Definition: CSV.h:366
Utility class used in conjunction with osvr::util::CSV, to store a single table cell&#39;s column header ...
Definition: CSV.h:62
Definition: newuoa.h:1888
Definition: CSV.h:48
void output(std::ostream &os) const
Outputs all the stored rows and columns, with the union of all headers in the first row...
Definition: CSV.h:340
void outputRow(std::ostream &os, DataRow const &row) const
Called by outputData() and by derived classes to format individual rows.
Definition: CSV.h:292
(Base) Class for easily outputting CSV files.
Definition: CSV.h:46
void startOutput()
Outputs all the stored rows and columns, with the union of all headers in the first row...
Definition: CSV.h:371
The "traditional" CSV class: get all your data set up ahead of time.
Definition: CSV.h:333
void add(Cell< CellArgs... > const &c)
Main function, used by the operator<< to add your cell to the row in progress.
Definition: CSV.h:152
void prepareForRow()
Called by CSVRowProxy life cycle, on row creation.
Definition: CSV.h:270
~CSVRowProxy()
Destructor - finalizes the row in the CSV object if we&#39;ve got some data and haven&#39;t been moved-from...
Definition: CSV.h:139
DataRow & latestRow()
Called internally and potentially by derived classes for access to the "latest row" temporary storage...
Definition: CSV.h:288
void moveLatestRowToData()
utility function for use in derived finalizeLatestRow()
Definition: CSV.h:307
Truly shared base class for all CSV implementations.
Definition: CSV.h:189
void outputData(std::ostream &os) const
Called by derived classes to output stored data rows.
Definition: CSV.h:300
detail::Cell< T > cell(const char *header, T const &data)
Helper free function to make a CSV cell.
Definition: CSV.h:401
A CSV object taking a reference to an ostream that should remain valid throughout the entire lifetime...
Definition: CSV.h:358