30namespace data_table_detail
44template <std::
signed_
integral T>
inline constexpr bool half_integer<basic_half_int<T>> =
true;
49 std::same_as<T, signed char> || std::same_as<T, unsigned char> || std::same_as<T, short> ||
50 std::same_as<T, unsigned short> || std::same_as<T, int> || std::same_as<T, unsigned int> || std::same_as<T, long> ||
51 std::same_as<T, unsigned long> || std::same_as<T, long long> || std::same_as<T, unsigned long long>;
55template <>
inline constexpr bool runtime_real<mpreal> =
true;
60concept supported_real = std::same_as<T, float> || std::same_as<T, double> || std::same_as<T, long double>
61#if UNI20_HAS_FLOAT128 && UNI20_FLOAT128_PROVIDER_MPLAPACK && defined(MPLAPACK_BINARY128_MODE) && \
62 (MPLAPACK_BINARY128_MODE == MPLAPACK_BINARY128_MODE_FLOAT128)
63 || std::same_as<T, uni20::float128>
67template <
typename To,
typename From>
consteval bool accepts()
69 using F = std::remove_cvref_t<From>;
72 if constexpr (std::same_as<F, std::nullopt_t>)
77 return accepts<value_type<To>, From>();
79 else if constexpr (std::same_as<To, std::string>)
80 return std::constructible_from<std::string, From> && !std::same_as<F, std::nullptr_t>;
81 else if constexpr (std::same_as<To, bool>)
82 return std::same_as<F, bool>;
85 else if constexpr (half_integer<To>)
93template <
typename To,
typename From>
94 requires(accepts<To, From>())
97 using F = std::remove_cvref_t<From>;
100 if constexpr (std::same_as<F, std::nullopt_t>)
104 if (!input)
return std::nullopt;
105 return To{convert<value_type<To>>(*std::forward<From>(input))};
108 return To{convert<value_type<To>>(std::forward<From>(input))};
112 if (!std::in_range<To>(input))
throw std::overflow_error(
"data table integer conversion out of range");
113 return static_cast<To
>(input);
115 else if constexpr (half_integer<To>)
117 if constexpr (half_integer<F>)
121 using Storage =
typename To::value_type;
125 throw std::overflow_error(
"data table half-integer conversion out of range");
126 return To(
static_cast<Storage
>(input));
129 else if constexpr (std::same_as<To, std::string>)
131 if constexpr (std::is_pointer_v<F>)
132 if (input ==
nullptr)
throw std::invalid_argument(
"null data table string");
133 return std::string(std::forward<From>(input));
135 else if constexpr (runtime_real<F>)
137 if (!input.initialized())
throw std::invalid_argument(
"unset data table value; use optional for missing data");
138 return static_cast<To
>(std::forward<From>(input));
141 return static_cast<To
>(std::forward<From>(input));
146void write_field(std::ostream& out, std::string_view field,
char delimiter,
bool quote_empty =
false);
163 if constexpr (runtime_real<T>)
169#if UNI20_HAS_FLOAT128 && UNI20_FLOAT128_PROVIDER_MPLAPACK && defined(MPLAPACK_BINARY128_MODE) && \
170 (MPLAPACK_BINARY128_MODE == MPLAPACK_BINARY128_MODE_FLOAT128)
171 if constexpr (std::same_as<T, uni20::float128>)
179 static_assert(std::floating_point<T>,
"data table export needs a lossless formatter for this scalar provider");
190 std::same_as<T, std::remove_cvref_t<T>> &&
193 std::same_as<data_table_detail::value_type<T>,
bool> ||
194 data_table_detail::half_integer<data_table_detail::value_type<T>> || std::same_as<T, std::string>);
212 if constexpr (std::same_as<T, std::string> || std::same_as<data_table_detail::value_type<T>,
bool>)
220 label_ = std::move(
text);
225 unit_ = std::move(
text);
230 description_ = std::move(
text);
276 requires(data_table_detail::half_integer<data_table_detail::value_type<T>> ||
277 data_table_detail::runtime_real<data_table_detail::value_type<T>>)
283 [[nodiscard]] std::string
const&
identifier() const noexcept {
return identifier_; }
284 [[nodiscard]] std::string
const&
label() const noexcept {
return label_; }
285 [[nodiscard]] std::string
const&
unit() const noexcept {
return unit_; }
286 [[nodiscard]] std::string
const&
description() const noexcept {
return description_; }
296 if constexpr (data_table_detail::runtime_real<data_table_detail::value_type<T>>) display_.
fractions =
false;
299 std::string identifier_;
302 std::string description_;
303 data_column_display display_;
310 std::map<std::string, data_column_display>
display = {};
386 :
std::runtime_error(
"required data table output failed"), report_(
std::move(
report))
406 static_assert(
sizeof...(Ts) > 0,
"data tables require at least one column");
417 [](
auto const&... column) {
418 std::array names{std::string_view(column.identifier())...};
419 for (std::size_t i = 0; i < names.size(); ++i)
420 for (std::size_t j = 0; j < i; ++j)
421 if (names[i] == names[j])
throw std::invalid_argument(
"duplicate data table column identifier");
433 template <
typename... Us>
434 requires(
sizeof...(Ts) ==
sizeof...(Us) && (data_table_detail::accepts<Ts, Us>() && ...))
437 mutation_guard guard(busy_);
438 if (finished_)
throw std::logic_error(
"cannot append to a finished data table");
439 auto row = this->
make_row(std::forward<Us>(values)...);
440 auto report = this->new_report();
444 for (
auto& sink : sinks_)
446 return this->checked_report(std::move(report));
450 template <
typename... Us>
451 requires(
sizeof...(Ts) ==
sizeof...(Us) && (data_table_detail::accepts<Ts, Us>() && ...))
454 return row_type{data_table_detail::convert<Ts>(std::forward<Us>(values))...};
462 mutation_guard guard(busy_);
464 throw std::logic_error(
"data table history unavailable; request future_only attachment explicitly");
465 auto owned = std::make_unique<sink_model<std::remove_cvref_t<Sink>>>(std::forward<Sink>(output));
470 auto& sink = sinks_.back();
472 sink.output->begin(title_, columns_, options_.
metadata, {options.replay == sink_replay::all ? 0 : size_});
475 for (
auto const&
row : rows_)
480 if (finished_) this->
close(sink, *summary_, report);
481 return {id, this->checked_report(std::move(report))};
487 mutation_guard guard(busy_);
488 auto report = this->new_report();
490 return this->checked_report(std::move(report));
498 mutation_guard guard(busy_);
501 if (!
summary.empty() &&
summary != *summary_)
throw std::logic_error(
"data table summary already finalized");
502 return this->checked_report(final_report_);
504 auto report = this->new_report();
507 for (
auto& sink : sinks_)
508 this->
close(sink, *summary_, report);
509 final_report_ = std::move(report);
510 return this->checked_report(final_report_);
513 [[nodiscard]] std::string
const&
title() const noexcept {
return title_; }
516 [[nodiscard]] std::optional<table_metadata>
const&
summary() const noexcept {
return summary_; }
517 [[nodiscard]]
bool finished() const noexcept {
return finished_; }
519 [[nodiscard]] std::size_t
size() const noexcept {
return size_; }
520 [[nodiscard]] std::size_t
retained_size() const noexcept {
return rows_.size(); }
522 [[nodiscard]] std::span<row_type const>
rows()
const
524 if (options_.
retain ==
retention::none)
throw std::logic_error(
"data table does not retain history");
530 struct mutation_guard
533 explicit mutation_guard(
bool& flag) : busy(flag)
535 if (busy)
throw std::logic_error(
"data table mutation is not reentrant");
538 ~mutation_guard() { busy =
false; }
540 struct sink_interface
542 virtual ~sink_interface() =
default;
547 template <
typename Sink>
struct sink_model final : sink_interface
550 explicit sink_model(Sink
value) : output(
std::move(
value)) {}
552 data_sink_start start)
override
564 std::unique_ptr<sink_interface> output;
566 data_delivery_report new_report()
const
568 data_delivery_report report;
569 report.failures.reserve(sinks_.size());
572 static data_delivery_report checked_report(data_delivery_report report)
574 if (std::any_of(report.failures.begin(), report.failures.end(), [](
auto const& f) { return f.required; }))
575 throw data_delivery_error(std::move(report));
578 template <
typename Function>
579 void attempt(sink_record& sink,
data_sink_operation operation, data_delivery_report& report, Function&& function)
589 report.failures.push_back({
sink.id,
sink.required, operation, std::current_exception()});
602 sink_record
const& find_sink(data_sink_id
id)
const
604 if (
id.
value >= sinks_.size())
throw std::invalid_argument(
"unknown data table sink");
605 return sinks_[
id.value];
607 sink_record& find_sink(data_sink_id
id) {
return const_cast<sink_record&
>(std::as_const(*this).find_sink(
id)); }
611 data_table_options options_;
612 std::vector<row_type> rows_;
613 std::vector<sink_record> sinks_;
614 std::size_t size_ = 0;
616 bool finished_ =
false;
617 std::optional<table_metadata> summary_;
618 data_delivery_report final_report_;
624 return data_table<Ts...>(std::move(title), std::move(columns)...);
628template <DataTableValue... Ts>
631 return data_table<Ts...>(std::move(title), std::move(options), std::move(columns)...);
634namespace data_table_detail
643 else if constexpr (std::same_as<T, std::string>)
645 else if constexpr (std::same_as<T, bool>)
646 return value ?
"true" :
"false";
647 else if constexpr (half_integer<T>)
650 return fmt::format(
"{}",
value);
654 if constexpr (runtime_real<T>)
667template <
typename Schema,
typename Row,
typename Function>
670 [&]<std::size_t... I>(std::index_sequence<I...>) {
671 (function(std::get<I>(schema), std::get<I>(
row)), ...);
672 }(std::make_index_sequence<std::tuple_size_v<Schema>>{});
680template <
typename Schema,
typename Function>
681void visit_column(Schema
const& schema, std::size_t index, Function&& function)
683 [&]<std::size_t... I>(std::index_sequence<I...>) {
684 ((I == index ? (void)function(std::get<I>(schema)) : (void)0), ...);
685 }(std::make_index_sequence<std::tuple_size_v<Schema>>{});
691 std::vector<std::string> names;
692 std::apply([&](
auto const&... column) { (names.push_back(column.identifier()), ...); }, schema);
693 auto add = [&](std::string
const& name) {
694 auto found = std::find(names.begin(), names.end(), name);
695 if (found == names.end())
throw std::invalid_argument(
"unknown data table output column: " + name);
696 auto index =
static_cast<std::size_t
>(found - names.begin());
698 throw std::invalid_argument(
"duplicate data table output column: " + name);
699 result.
indices.push_back(index);
701 if (
auto override = options.
display.find(name);
override != options.
display.end())
702 result.
display.back() =
override->second;
704 for (
auto const& name : options.
columns.empty() ? names : options.
columns)
706 for (
auto const& [name, format] : options.
display)
708 auto found = std::find(names.begin(), names.end(), name);
709 if (found == names.end() ||
710 std::find(result.
indices.begin(), result.
indices.end(), found - names.begin()) == result.
indices.end())
711 throw std::invalid_argument(
"display override requires a selected data table column: " + name);
712 auto const& numeric = format.numeric;
718template <
typename Schema,
typename Row,
typename Function>
721 for (std::size_t
p = 0;
p < selection.
indices.size(); ++
p)
722 [&]<std::size_t... I>(std::index_sequence<I...>) {
723 ((I == selection.
indices[
p] ? (void)function(std::get<I>(schema), std::get<I>(
row), selection.
display[
p])
726 }(std::make_index_sequence<std::tuple_size_v<Schema>>{});
731template <DataTableValue... Ts>
734 auto rows = table.rows();
735 report_table result(table.title());
737 for (std::size_t
p = 0;
p < selection.indices.size(); ++
p)
739 result.column(column.label(), selection.display[p].alignment);
741 for (
auto const&
row : rows)
743 std::vector<std::string> cells;
744 cells.reserve(
sizeof...(Ts));
746 [&](
auto const&,
auto const&
value,
auto const&
display) {
747 cells.push_back(data_table_detail::cell_text(value, display, false));
749 result.row(std::move(cells));
768namespace data_table_detail
775 if (!
value)
return "";
782template <
typename Schema>
788 for (
auto index : selection.
indices)
790 if (!std::exchange(first,
false)) out.put(delimiter);
797template <
typename Schema,
typename Row>
803 if (!std::exchange(first,
false)) out.put(delimiter);
804 using T = std::remove_cvref_t<
decltype(
value)>;
816 auto rows = table.rows();
819 for (
auto const&
row : rows)
828template <DataTableValue... Ts>
836template <DataTableValue... Ts>
Typed column schema with owned metadata and deferred display formatting.
Definition data_table.hpp:206
data_column(std::string identifier)
Definition data_table.hpp:209
data_column & label(std::string text)
Definition data_table.hpp:218
data_column & fractional(bool enabled=true)
Select exact fractions for half-integers or exact mpreal values.
Definition data_table.hpp:275
data_column & round_trip()
Recover finite real values from their text, including the sign of zero.
Definition data_table.hpp:266
data_column & fixed(int digits)
Use fractional decimal digits; -1 selects native max_digits10 or approximate MPFR round-trip digits.
Definition data_table.hpp:245
T value_type
Definition data_table.hpp:208
data_column & missing(std::string text)
Definition data_table.hpp:238
data_column & description(std::string text)
Definition data_table.hpp:228
std::string const & label() const noexcept
Definition data_table.hpp:284
std::string const & unit() const noexcept
Definition data_table.hpp:285
data_column & alignment(table_alignment value)
Definition data_table.hpp:233
data_column_display const & display() const noexcept
Definition data_table.hpp:287
std::string const & description() const noexcept
Definition data_table.hpp:286
std::string const & identifier() const noexcept
Definition data_table.hpp:283
data_column & general(int digits=-1)
Use significant digits; -1 selects native max_digits10 or approximate MPFR round-trip digits.
Definition data_table.hpp:257
data_column & scientific(int digits)
Use fractional mantissa digits; -1 selects native max_digits10 or approximate MPFR round-trip digits.
Definition data_table.hpp:251
data_column & unit(std::string text)
Definition data_table.hpp:223
Required output failed after all eligible sinks were attempted; retained rows are not rolled back.
Definition data_table.hpp:383
data_delivery_report const & report() const noexcept
Definition data_table.hpp:388
data_delivery_error(data_delivery_report report)
Definition data_table.hpp:385
Definition data_table.hpp:150
classic_numeric_locale(classic_numeric_locale const &)=delete
classic_numeric_locale & operator=(classic_numeric_locale const &)=delete
classic_numeric_locale()
Definition data_table.cpp:58
~classic_numeric_locale()
Definition data_table.cpp:66
Typed result table with synchronous sinks and optional retained history.
Definition data_table.hpp:404
retention retention_policy() const noexcept
Definition data_table.hpp:518
data_delivery_report finish_sink(data_sink_id id, table_metadata const &summary={})
Finalize and unsubscribe one sink without ending the table or discarding rows.
Definition data_table.hpp:485
data_table & operator=(data_table const &)=delete
data_table(std::string title, data_column< Ts >... columns)
Definition data_table.hpp:410
bool finished() const noexcept
Definition data_table.hpp:517
static row_type make_row(Us &&... values)
Construct a checked, owning row independently of storage or output.
Definition data_table.hpp:452
table_metadata const & metadata() const noexcept
Definition data_table.hpp:515
std::size_t retained_size() const noexcept
Definition data_table.hpp:520
std::size_t size() const noexcept
Definition data_table.hpp:519
std::string const & title() const noexcept
Definition data_table.hpp:513
std::span< row_type const > rows() const
Require complete retained history before inspecting or exporting rows.
Definition data_table.hpp:522
data_sink_state sink_state(data_sink_id id) const
Definition data_table.hpp:527
data_table & operator=(data_table &&)=delete
data_attachment attach(Sink &&output, data_sink_options options={})
Own a sink, begin its output and replay history before subscribing to future rows.
Definition data_table.hpp:460
data_table(data_table &&)=default
data_delivery_report append(Us &&... values)
Validate once, retain when enabled, then deliver to every active sink in attachment order.
Definition data_table.hpp:435
data_delivery_report finish(table_metadata summary={})
End row insertion and finalize all active outputs, retaining the summary for later replay.
Definition data_table.hpp:496
data_table(std::string title, data_table_options options, data_column< Ts >... columns)
Definition data_table.hpp:413
data_table(data_table const &)=delete
std::optional< table_metadata > const & summary() const noexcept
Definition data_table.hpp:516
schema_type const & columns() const noexcept
Definition data_table.hpp:514
std::tuple< data_column< Ts >... > schema_type
Definition data_table.hpp:408
std::tuple< Ts... > row_type
Definition data_table.hpp:407
A small report table builder for command-line examples and diagnostics.
Definition presentation.hpp:479
Concept for real scalar types (float, double, etc.).
Definition scalar_concepts.hpp:69
Types retained directly by data tables, without numerical narrowing or text conversion.
Definition data_table.hpp:189
Definition data_table.hpp:48
Definition data_table.hpp:60
locale_t value
Definition data_table.cpp:49
presentation::styled_text text
Definition display.cpp:141
std::string format_real(T value, scalar_format_options const &options={})
Format a Uni20 real scalar.
Definition scalar_io.hpp:306
real_format_notation
Selects how real scalar values are rendered as text.
Definition scalar_io.hpp:29
Defines basic_half_int<T>, a signed integer type with half-integer resolution.
Definition display.cpp:16
std::function< void(event const &)> sink
Callable that receives display events for final routing and rendering.
Definition display.hpp:126
void validate_numeric_precision(real_format_notation notation, int digits)
Definition data_table.cpp:9
constexpr bool runtime_real
Definition data_table.hpp:53
constexpr bool half_integer
Definition data_table.hpp:43
void visit_selected(Schema const &schema, Row const &row, resolved_projection const &selection, Function &&function)
Definition data_table.hpp:719
void write_delimited(std::ostream &out, data_table< Ts... > const &table, char delimiter, delimited_options const &options)
Definition data_table.hpp:813
consteval bool accepts()
Definition data_table.hpp:67
To convert(From &&input)
Definition data_table.hpp:95
std::string export_text(T const &value, data_column_display const &display, delimited_options const &options)
Definition data_table.hpp:771
void check_output(std::ostream &out)
Definition data_table.cpp:40
resolved_projection resolve_projection(Schema const &schema, table_projection const &options)
Definition data_table.hpp:688
void write_delimited_row(std::ostream &out, Schema const &schema, Row const &row, resolved_projection const &selection, char delimiter, delimited_options const &options)
Definition data_table.hpp:798
void visit_column(Schema const &schema, std::size_t index, Function &&function)
Definition data_table.hpp:681
void validate_identifier(std::string_view identifier)
Definition data_table.cpp:16
std::string cell_text(T const &value, data_column_display const &display, bool machine)
Definition data_table.hpp:636
std::string real_text(T value, scalar_format_options options)
Definition data_table.hpp:161
void visit_cells(Schema const &schema, Row const &row, Function &&function)
Definition data_table.hpp:668
void write_delimited_header(std::ostream &out, Schema const &schema, resolved_projection const &selection, char delimiter)
Definition data_table.hpp:783
typename optional_traits< T >::value_type value_type
Definition data_table.hpp:42
void write_field(std::ostream &out, std::string_view field, char delimiter, bool quote_empty)
Definition data_table.cpp:27
Definition data_table.cpp:8
void write_tsv(std::ostream &out, data_table< Ts... > const &table, delimited_options options={})
Write tab-delimited CSV using CSV quoting, not whitespace-token splitting.
Definition data_table.hpp:837
data_export_precision
Precision for delimited numerical output; display mode deliberately rounds real values.
Definition data_table.hpp:756
table_alignment
Selects table cell alignment in report-style output.
Definition presentation.hpp:89
std::map< std::string, std::string > table_metadata
Application metadata is owned text, independent of numerical rows.
Definition data_table.hpp:314
retention
Whether accepted rows remain available for snapshots and replay.
Definition data_table.hpp:318
data_sink_state
Definition data_table.hpp:355
auto make_data_table(std::string title, data_column< Ts >... columns)
Deduce column types with default retained history.
Definition data_table.hpp:622
void write_csv(std::ostream &out, data_table< Ts... > const &table, delimited_options options={})
Write a CSV snapshot with stable headers, quoted strings and empty missing numeric fields.
Definition data_table.hpp:829
sink_replay
Full replay is the default. Future-only attachment explicitly opts out of history.
Definition data_table.hpp:332
report_table to_report_table(data_table< Ts... > const &table, table_projection const &projection={})
Format a snapshot using column display settings and the existing rich table model.
Definition data_table.hpp:732
data_sink_operation
Definition data_table.hpp:361
auto to_string_fraction(basic_half_int< T > value) -> std::string
Format a half-integer as an integer or n/2.
Definition half_int.hpp:464
int add(int a, int b)
Definition dummy.cpp:6
auto to_string(basic_half_int< T > value) -> std::string
Format a half-integer as an integer or decimal .5.
Definition half_int.hpp:477
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16
Attachment identity and any optional begin/replay/finalization failure.
Definition data_table.hpp:396
data_delivery_report report
Definition data_table.hpp:398
data_sink_id id
Definition data_table.hpp:397
Column display settings. These never change stored values or default export precision.
Definition data_table.hpp:198
std::string missing
Definition data_table.hpp:201
table_alignment alignment
Definition data_table.hpp:200
bool fractions
Definition data_table.hpp:202
scalar_format_options numeric
Definition data_table.hpp:199
Newly encountered failures. Callers using optional sinks must inspect this result.
Definition data_table.hpp:378
std::vector< data_sink_failure > failures
Definition data_table.hpp:379
Original exception and attachment identity for one disabled sink.
Definition data_table.hpp:369
bool required
Definition data_table.hpp:371
data_sink_operation operation
Definition data_table.hpp:372
std::exception_ptr exception
Definition data_table.hpp:373
data_sink_id sink
Definition data_table.hpp:370
A table-local attachment identifier, stable when its table is moved.
Definition data_table.hpp:350
std::size_t value
Definition data_table.hpp:351
bool operator==(data_sink_id const &) const =default
Definition data_table.hpp:337
bool required
Definition data_table.hpp:338
sink_replay replay
Definition data_table.hpp:339
The index of the first row this attachment can emit (zero for full replay).
Definition data_table.hpp:344
std::size_t first_row
Definition data_table.hpp:345
T value_type
Definition data_table.hpp:40
Definition data_table.hpp:33
T value_type
Definition data_table.hpp:35
static constexpr bool optional
Definition data_table.hpp:34
Definition data_table.hpp:675
std::vector< data_column_display > display
Definition data_table.hpp:677
std::vector< std::size_t > indices
Definition data_table.hpp:676
Construction-time table policy and initial application metadata.
Definition data_table.hpp:325
retention retain
Definition data_table.hpp:326
table_metadata metadata
Definition data_table.hpp:327
Delimited output policy, independent of terminal styles and width.
Definition data_table.hpp:763
table_projection projection
Definition data_table.hpp:765
data_export_precision precision
Definition data_table.hpp:764
Ordered output columns and independent display overrides. Empty columns selects the full schema.
Definition data_table.hpp:308
std::map< std::string, data_column_display > display
Definition data_table.hpp:310
std::vector< std::string > columns
Definition data_table.hpp:309