56#define DETRAY_DFE_NAMEDTUPLE(name, ...) \
57 using Tuple = decltype(::std::make_tuple(__VA_ARGS__)); \
58 static ::std::array<::std::string, ::std::tuple_size<Tuple>::value> \
60 return ::detray::io::csv::dfe::unstringify< \
61 ::std::tuple_size<Tuple>::value>((#__VA_ARGS__)); \
63 template <typename... U> \
64 name& operator=(const ::std::tuple<U...>& other) { \
65 ::std::tie(__VA_ARGS__) = other; \
68 template <typename... U> \
69 name& operator=(::std::tuple<U...>&& other) { \
70 ::std::tie(__VA_ARGS__) = ::std::forward<std::tuple<U...>>(other); \
73 operator Tuple() const { \
74 return ::std::make_tuple(__VA_ARGS__); \
76 Tuple tuple() const { \
77 return ::std::make_tuple(__VA_ARGS__); \
79 template <std::size_t I> \
80 constexpr ::std::tuple_element_t<I, Tuple>& get() { \
81 return ::std::get<I>(std::tie(__VA_ARGS__)); \
83 template <::std::size_t I> \
84 constexpr const ::std::tuple_element_t<I, Tuple>& get() const { \
85 return ::std::get<I>(std::tie(__VA_ARGS__)); \
87 template <::std::size_t I> \
88 friend constexpr ::std::tuple_element_t<I, Tuple>& get(name& nt) { \
89 return nt.template get<I>(); \
91 template <::std::size_t I> \
92 friend constexpr const ::std::tuple_element_t<I, Tuple>& get( \
94 return nt.template get<I>(); \
96 friend inline ::std::ostream& operator<<(::std::ostream& os, \
98 return ::detray::io::csv::dfe::print_tuple( \
99 os, nt.names(), nt.tuple(), \
100 ::std::make_index_sequence<::std::tuple_size<Tuple>::value>{}); \
103namespace detray::io::csv::dfe {
105template <
typename R,
typename T>
106bool rangeContainsValue(
const R& range,
const T& value) {
107 return std::ranges::find(range, value) != std::ranges::end(range);
110template <std::
size_t N>
111constexpr std::array<std::string, N> unstringify(
const char* str) {
112 assert(str &&
"Input string must be non-null");
114 std::array<std::string, N> out;
116 for (std::size_t idx = 0; idx < N; ++idx) {
117 while ((*str !=
'\0') && (*str ==
' ')) {
120 const char* sep = str;
121 while ((*sep !=
'\0') && (*sep !=
',')) {
124 out[idx].assign(str,
static_cast<std::size_t
>(sep - str));
133template <
typename Names,
typename Values, std::size_t... I>
134inline std::ostream& print_tuple(std::ostream& os,
const Names& n,
136 std::index_sequence<I...> ) {
138 using Vacuum =
int[];
140 (os << ((0 < I) ?
" " :
"") <<
get<I>(n) <<
"=" <<
get<I>(v), 0)...};
144template <
char Delimiter>
147 DsvWriter() =
delete;
148 DsvWriter(
const DsvWriter&) =
delete;
149 DsvWriter(DsvWriter&&) noexcept = default;
150 ~DsvWriter() = default;
151 DsvWriter& operator=(const DsvWriter&) = delete;
152 DsvWriter& operator=(DsvWriter&&) noexcept = default;
154 DsvWriter(const std::vector<std::
string>& columns, const std::
string& path,
155 int precision = std::numeric_limits<
double>::max_digits10);
157 template <typename Arg0, typename... Args>
158 void append(Arg0&& arg0, Args&&... args);
161 std::ofstream m_file;
162 std::
size_t m_num_columns;
164 template <typename T>
165 unsigned write(T&& x, std::ostream& os);
167 template <typename T, typename Allocator>
168 static
unsigned write(const std::vector<T, Allocator>& xs, std::ostream& os);
171template <
char Delimiter>
174 DsvReader() =
delete;
175 DsvReader(
const DsvReader&) =
delete;
176 DsvReader(DsvReader&&) noexcept = default;
177 ~DsvReader() = default;
178 DsvReader& operator=(const DsvReader&) = delete;
179 DsvReader& operator=(DsvReader&&) noexcept = default;
181 explicit DsvReader(const std::
string& path);
183 bool read(std::vector<std::
string>& columns);
185 std::
size_t num_lines()
const {
return m_num_lines; }
188 std::ifstream m_file;
190 std::size_t m_num_lines = 0;
193template <
char Delimiter,
typename NamedTuple>
194class NamedTupleDsvWriter {
196 NamedTupleDsvWriter() =
delete;
197 NamedTupleDsvWriter(
const NamedTupleDsvWriter&) =
delete;
198 NamedTupleDsvWriter(NamedTupleDsvWriter&&) noexcept = default;
199 ~NamedTupleDsvWriter() = default;
200 NamedTupleDsvWriter& operator=(const NamedTupleDsvWriter&) = delete;
201 NamedTupleDsvWriter& operator=(NamedTupleDsvWriter&&) noexcept = default;
203 explicit NamedTupleDsvWriter(
204 const std::
string& path,
205 int precision = std::numeric_limits<
double>::max_digits10)
206 : m_writer(colum_names(), path, precision) {}
208 void append(
const NamedTuple& record) {
209 append_impl(record, std::make_index_sequence<
210 std::tuple_size_v<typename NamedTuple::Tuple>>{});
214 DsvWriter<Delimiter> m_writer;
216 static std::vector<std::string> colum_names() {
217 const auto& from_record = NamedTuple::names();
218 return {from_record.begin(), from_record.end()};
220 template <std::size_t... I>
221 void append_impl(
const NamedTuple& values,
222 std::index_sequence<I...> ) {
224 m_writer.append(get<I>(values)...);
229static void parse(
const std::string& str, T& value) {
230 std::istringstream is(str);
234template <
char Delimiter,
typename NamedTuple>
235class NamedTupleDsvReader {
237 NamedTupleDsvReader() =
delete;
238 NamedTupleDsvReader(
const NamedTupleDsvReader&) =
delete;
239 NamedTupleDsvReader(NamedTupleDsvReader&&) noexcept = default;
240 ~NamedTupleDsvReader() = default;
241 NamedTupleDsvReader& operator=(const NamedTupleDsvReader&) = delete;
242 NamedTupleDsvReader& operator=(NamedTupleDsvReader&&) noexcept = default;
244 explicit NamedTupleDsvReader(
245 const std::
string& path,
246 const std::vector<std::
string>& optional_columns = {},
247 bool verify_header =
true);
248 bool read(NamedTuple& record);
250 template <
typename T>
251 bool read(NamedTuple& record, std::vector<T>& extra);
253 std::size_t num_extra_columns()
const {
return m_extra_columns.size(); }
254 std::size_t num_records()
const {
return m_reader.num_lines() - 1u; }
257 using Tuple =
typename NamedTuple::Tuple;
259 DsvReader<Delimiter> m_reader;
260 std::vector<std::string> m_columns;
261 std::size_t m_num_columns = SIZE_MAX;
262 std::array<std::size_t, std::tuple_size<Tuple>::value> m_tuple_column_map;
263 std::vector<std::size_t> m_extra_columns;
265 void use_default_columns();
266 void parse_header(
const std::vector<std::string>& optional_columns);
267 template <std::size_t... I>
268 void parse_record(NamedTuple& record,
269 std::index_sequence<I...> )
const {
270 using Vacuum =
int[];
271 (void)Vacuum{(parse_element<I>(record), 0)...};
273 template <std::
size_t I>
274 void parse_element(NamedTuple& record)
const {
276 if (m_tuple_column_map[I] != SIZE_MAX) {
277 parse(m_columns[m_tuple_column_map[I]], get<I>(record));
282template <
char Delimiter>
283inline DsvWriter<Delimiter>::DsvWriter(
const std::vector<std::string>& columns,
284 const std::string& path,
int precision)
286 std::ios_base::binary | std::ios_base::out | std::ios_base::trunc),
287 m_num_columns(columns.size()) {
288 if (!m_file.is_open() || m_file.fail()) {
289 throw std::runtime_error(
"Could not open file '" + path +
"'");
291 m_file.precision(precision);
292 if (m_num_columns == 0) {
293 throw std::invalid_argument(
"No columns were specified");
298template <
char Delimiter>
299template <
typename Arg0,
typename... Args>
300inline void DsvWriter<Delimiter>::append(Arg0&& arg0, Args&&... args) {
301 std::stringstream line;
302 unsigned written_columns[] = {
303 write(std::forward<Arg0>(arg0), line),
304 (line << std::setprecision(std::numeric_limits<double>::max_digits10)
306 write(std::forward<Args>(args), line))...,
309 unsigned total_columns = 0;
310 for (
auto nc : written_columns) {
313 if (total_columns < m_num_columns) {
314 throw std::invalid_argument(
"Not enough columns");
316 if (m_num_columns < total_columns) {
317 throw std::invalid_argument(
"Too many columns");
319 m_file << line.rdbuf();
320 if (!m_file.good()) {
321 throw std::runtime_error(
"Could not write data to file");
325template <
char Delimiter>
327inline unsigned DsvWriter<Delimiter>::write(T&& x, std::ostream& os) {
328 os << std::forward<T>(x);
332template <
char Delimiter>
333template <
typename T,
typename Allocator>
334inline unsigned DsvWriter<Delimiter>::write(
const std::vector<T, Allocator>& xs,
337 for (
const auto& x : xs) {
347template <
char Delimiter>
348inline DsvReader<Delimiter>::DsvReader(
const std::string& path)
349 : m_file(path, std::ios_base::binary | std::ios_base::in) {
350 if (!m_file.is_open() || m_file.fail()) {
351 throw std::runtime_error(
"Could not open file '" + path +
"'");
355template <
char Delimiter>
356inline bool DsvReader<Delimiter>::read(std::vector<std::string>& columns) {
357 std::getline(m_file, m_line);
362 throw std::runtime_error(std::string(
"Could not read line ") +
363 std::to_string(m_num_lines));
368 for (std::string::size_type pos = 0; pos < m_line.size();) {
369 auto del = m_line.find_first_of(Delimiter, pos);
370 if (del == std::string::npos) {
371 columns.emplace_back(m_line, pos);
374 columns.emplace_back(m_line, pos, del - pos);
381template <
char Delimiter,
typename NamedTuple>
382inline NamedTupleDsvReader<Delimiter, NamedTuple>::NamedTupleDsvReader(
383 const std::string& path,
const std::vector<std::string>& optional_columns,
386 if ((!optional_columns.empty()) && (!verify_header)) {
387 throw std::runtime_error(
388 "Optional columns can not be used without header verification");
390 if (!m_reader.read(m_columns)) {
391 throw std::runtime_error(
"Could not read header from '" + path +
"'");
394 parse_header(optional_columns);
396 use_default_columns();
400template <
char Delimiter,
typename NamedTuple>
401inline bool NamedTupleDsvReader<Delimiter, NamedTuple>::read(
402 NamedTuple& record) {
403 if (!m_reader.read(m_columns)) {
406 if (m_columns.size() < m_num_columns) {
407 throw std::runtime_error(
"Too few columns in line " +
408 std::to_string(m_reader.num_lines()));
410 if (m_num_columns < m_columns.size()) {
411 throw std::runtime_error(
"Too many columns in line " +
412 std::to_string(m_reader.num_lines()));
414 parse_record(record, std::make_index_sequence<std::tuple_size_v<Tuple>>{});
418template <
char Delimiter,
typename NamedTuple>
420inline bool NamedTupleDsvReader<Delimiter, NamedTuple>::read(
421 NamedTuple& record, std::vector<T>& extra) {
425 extra.resize(m_extra_columns.size());
426 for (std::size_t i = 0; i < m_extra_columns.size(); ++i) {
427 parse(m_columns[m_extra_columns[i]], extra[i]);
432template <
char Delimiter,
typename NamedTuple>
433inline void NamedTupleDsvReader<Delimiter, NamedTuple>::use_default_columns() {
434 m_num_columns = std::tuple_size_v<Tuple>;
435 for (std::size_t i = 0; i < m_tuple_column_map.size(); ++i) {
436 m_tuple_column_map[i] = i;
438 m_extra_columns.clear();
441template <
char Delimiter,
typename NamedTuple>
442inline void NamedTupleDsvReader<Delimiter, NamedTuple>::parse_header(
443 const std::vector<std::string>& optional_columns) {
444 const auto& names = NamedTuple::names();
446 m_num_columns = m_columns.size();
448 for (
const auto& name : names) {
453 throw std::runtime_error(
"Missing header column '" + name +
"'");
457 m_tuple_column_map.fill(SIZE_MAX);
459 m_extra_columns.clear();
460 for (std::size_t i = 0; i < m_columns.size(); ++i) {
461 auto it = std::ranges::find(names, m_columns[i]);
462 if (it != names.end()) {
463 m_tuple_column_map[
static_cast<std::size_t
>(
464 std::distance(names.begin(), it))] = i;
466 m_extra_columns.push_back(i);
471using CsvWriter = dfe::DsvWriter<
','>;
473using TsvWriter = dfe::DsvWriter<
'\t'>;
476using NamedTupleCsvWriter = dfe::NamedTupleDsvWriter<
',',
T>;
479using NamedTupleCsvReader = dfe::NamedTupleDsvReader<
',',
T>;
482using NamedTupleTsvWriter = dfe::NamedTupleDsvWriter<
'\t',
T>;
485using NamedTupleTsvReader = dfe::NamedTupleDsvReader<
'\t',
T>;
constexpr double T
Magnetic field, native unit (eV*s)/(e*m²).
Definition Units.hpp:168
bool rangeContainsValue(const R &range, const T &value)
This can be replaced with C++23 to use the std::ranges::contains method.
Definition Helpers.hpp:227
auto read(const std::string &intersection_file_name, const std::string &track_param_file_name, std::vector< std::vector< intersection_record< detector_t >, allocator_t > > &intersection_traces)
Read the intersection_record from file.
Definition detector_scanner.hpp:275
const auto & get(const detray::tuple< Ts... > &t) noexcept
Get a tuple element by index from a const tuple.
Definition tuple.hpp:152