GridFormat 0.6.0
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
field.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2// SPDX-License-Identifier: MIT
8#ifndef GRIDFORMAT_COMMON_FIELD_HPP_
9#define GRIDFORMAT_COMMON_FIELD_HPP_
10
11#include <memory>
12#include <cstddef>
13#include <concepts>
14#include <type_traits>
15#include <utility>
16#include <ranges>
17#include <cmath>
18#include <string>
19
20#include <gridformat/common/md_layout.hpp>
21#include <gridformat/common/precision.hpp>
22#include <gridformat/common/serialization.hpp>
23#include <gridformat/common/type_traits.hpp>
24#include <gridformat/common/exceptions.hpp>
25#include <gridformat/common/concepts.hpp>
26#include <gridformat/common/ranges.hpp>
27
28namespace GridFormat {
29
32
38class Field {
39 public:
41 static constexpr struct DisableResize {} no_resize{};
42
43 virtual ~Field() = default;
44
45 Field() = default;
46 Field(Field&&) = default;
47 Field(const Field&) = delete;
48 Field& operator=(Field&&) = default;
49 Field& operator=(const Field&) = delete;
50
52 MDLayout layout() const {
53 return _layout();
54 }
55
57 DynamicPrecision precision() const {
58 return _precision();
59 }
60
62 std::size_t size_in_bytes() const {
63 return layout().number_of_entries()*precision().size_in_bytes();
64 }
65
67 Serialization serialized() const {
68 auto result = _serialized();
69 if (result.size() != size_in_bytes())
70 throw SizeError("Serialized size does not match expected number of bytes");
71 return result;
72 }
73
75 template<typename Visitor>
76 decltype(auto) visit_field_values(Visitor&& visitor) const {
77 return precision().visit([&] <typename T> (const Precision<T>&) {
78 const auto serialization = serialized();
79 return visitor(serialization.template as_span_of<T>());
80 });
81 }
82
86 template<std::ranges::range R> requires(Concepts::Scalar<MDRangeValueType<R>>)
87 decltype(auto) export_to(R&& output_range) const {
88 if constexpr (std::same_as<std::remove_cvref_t<R>, std::string>) {
89 decltype(auto) result = _export_to<true>(std::forward<R>(output_range));
90 if (!result.empty() && result.back() == '\0')
91 result.pop_back();
92 if (result.find('\0') != std::string::npos)
93 throw ValueError("Field holds multiple strings, export it into a MultiString instead");
94 return std::forward<decltype(result)>(result);
95 } else {
96 return _export_to<true>(std::forward<R>(output_range));
97 }
98 }
99
101 template<std::ranges::range R> requires(Concepts::Scalar<MDRangeValueType<R>>)
102 decltype(auto) export_to(R&& output_range, DisableResize) const {
103 return _export_to<false>(std::forward<R>(output_range));
104 }
105
107 template<Concepts::Scalar S>
108 void export_to(S& out) const {
109 const auto my_layout = layout();
110 const auto serialization = serialized();
111 if (my_layout.number_of_entries() != 1)
112 throw TypeError("Field cannot be exported into a scalar");
113 visit_field_values([&] <typename T> (std::span<const T> data) {
114 out = static_cast<S>(data[0]);
115 });
116 }
117
119 template<Concepts::Scalar S>
120 S export_to() const {
121 S out;
122 export_to(out);
123 return out;
124 }
125
127 template<Concepts::ResizableMDRange R>
128 requires(Concepts::StaticallySizedMDRange<std::ranges::range_value_t<R>> or
129 Concepts::Scalar<std::ranges::range_value_t<R>>)
130 R export_to() const {
131 return export_to(R{});
132 }
133
134 private:
135 DynamicPrecision _prec;
136
137 virtual MDLayout _layout() const = 0;
138 virtual DynamicPrecision _precision() const = 0;
139 virtual Serialization _serialized() const = 0;
140
142 template<bool enable_resize, std::ranges::range R> requires(Concepts::Scalar<MDRangeValueType<R>>)
143 decltype(auto) _export_to(R&& output_range) const {
144 const auto my_layout = layout();
145
146 if constexpr (Concepts::ResizableMDRange<R> && enable_resize) {
147 const auto num_scalars = my_layout.number_of_entries();
148 const auto num_sub_scalars = get_md_layout<std::ranges::range_value_t<R>>().number_of_entries();
149 if (num_scalars%num_sub_scalars != 0)
150 throw TypeError(
151 "Cannot export the field into the given range type. "
152 "Number of entries in the field is not divisible by the "
153 "number of entries in the value_type of the provided range."
154 );
155
156 output_range.resize(
157 num_scalars/num_sub_scalars,
158 DefaultValue<std::ranges::range_value_t<R>>::get()
159 );
160 } else {
161 const auto output_range_layout = get_md_layout(output_range);
162 if (output_range_layout.number_of_entries() < my_layout.number_of_entries())
163 throw SizeError(
164 std::string{"Cannot fill the given range. Too few entries. "} +
165 "Number of field entries: '" + std::to_string(my_layout.number_of_entries()) + "'; " +
166 "Number of range entries: '" + std::to_string(output_range_layout.number_of_entries()) + "'"
167 );
168 }
169
170 std::size_t offset = 0;
171 visit_field_values([&] <typename T> (std::span<const T> data) {
172 _export_to(output_range, data, offset);
173 });
174
175 return std::forward<R>(output_range);
176 }
177
178 template<std::ranges::range R, Concepts::Scalar T>
179 void _export_to(R& range,
180 std::span<const T> data,
181 std::size_t& offset) const {
182 if constexpr (mdrange_dimension<R> > 1) {
183 std::ranges::for_each(range, [&] (std::ranges::range auto& sub_range) {
184 _export_to(sub_range, data, offset);
185 });
186 } else {
187 // Note: if the span is over a std::vector<bool>, the use of `std::ranges::copy` or similar broke here.
188 // It seems to work with more recent compilers and/or c++23, though. We use `std::copy` as a workaround.
189 auto converted_data = data | std::views::drop(offset)
190 | std::views::take(std::min(Ranges::size(range), data.size() - offset))
191 | std::views::transform([] (const T& value) {
192 return static_cast<std::ranges::range_value_t<R>>(value);
193 });
194 std::copy(
195 std::ranges::begin(converted_data),
196 std::ranges::end(converted_data),
197 std::ranges::begin(range)
198 );
199 offset += Ranges::size(converted_data);
200 }
201 }
202};
203
205using FieldPtr = std::shared_ptr<const Field>;
206
208template<typename F> requires(
209 std::derived_from<std::remove_cvref_t<F>, Field> and
210 !std::is_lvalue_reference_v<F>)
212 return std::make_shared<std::add_const_t<F>>(std::forward<F>(f));
213}
214
216
217} // namespace GridFormat
218
219#endif // GRIDFORMAT_COMMON_FIELD_HPP_
Abstract interface for fields of values that is used by writers/readers to store fields.
Definition field.hpp:38
std::size_t size_in_bytes() const
Return the size of all field values in serialized form.
Definition field.hpp:62
decltype(auto) export_to(R &&output_range) const
Definition field.hpp:87
S export_to() const
Export the field as a scalar (works only if the field is a scalar field).
Definition field.hpp:120
Serialization serialized() const
Return the field values in serialized form.
Definition field.hpp:67
R export_to() const
Export the field into a resizable range (e.g. std::vector).
Definition field.hpp:130
void export_to(S &out) const
Export the field as a scalar (works only if the field is a scalar field).
Definition field.hpp:108
decltype(auto) visit_field_values(Visitor &&visitor) const
Visit the scalar values of the field in the form of an std::span.
Definition field.hpp:76
decltype(auto) export_to(R &&output_range, DisableResize) const
Export the field values into the provided range without resizing (given range must be large enough).
Definition field.hpp:102
DynamicPrecision precision() const
Return the precision of the scalar field values.
Definition field.hpp:57
MDLayout layout() const
Return the layout of this field.
Definition field.hpp:52
FieldPtr make_field_ptr(F &&f)
Factory function for field pointers.
Definition field.hpp:211
std::shared_ptr< const Field > FieldPtr
Pointer type used by writers/readers for fields.
Definition field.hpp:205