Lector v1.0.0
C++ library for parsing command line arguments.
print.hpp
1// Copyright © 2026, Alexandre Coderre-Chabot.
2
3// This file is part of Lector (https://github.com/acodcha/lector), a C++ library for parsing
4// command line arguments. Lector is licensed under the MIT License (https://mit-license.org).
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
7// associated documentation files (the "Software"), to deal in the Software without restriction,
8// including without limitation the rights to use, copy, modify, merge, publish, distribute,
9// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11// - The above copyright notice and this permission notice shall be included in all copies or
12// substantial portions of the Software.
13// - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
14// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM
17// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18
19#ifndef LECTOR_PRINT_HPP
20#define LECTOR_PRINT_HPP
21
22#include <array>
23#include <cmath>
24#include <cstdint>
25#include <filesystem>
26#include <iomanip>
27#include <limits>
28#include <sstream>
29#include <string>
30#include <string_view>
31#include <type_traits>
32
33/// @brief The Lector library's namespace.
34namespace lector {
35
36/// @brief The canonical printed name for a value of a specified type.
37/// @tparam Type The type of the value.
38template <typename Type>
39using Name = ::std::pair<Type, ::std::string_view>;
40
41/// @brief Map of canonical printed names to their corresponding values of a specified type.
42/// @tparam Type The type of the values.
43template <typename Type>
44inline constexpr ::std::array<::lector::Name<Type>, 0UL> Names{};
45
46/// @brief Prints a value of a specified enumeration type as a string of text. The enumeration type
47/// must define a specialization of the lector::Names constant for its type.
48/// @tparam EnumerationType The enumeration type.
49/// @param[in] value The enumeration value to print.
50/// @return The value as a printed string of text.
51template <typename EnumerationType>
52[[nodiscard]] constexpr ::std::string_view print_enumeration(const EnumerationType value) {
53 static_assert(std::is_enum_v<EnumerationType>);
54 for (const auto& [enumeration_value, name] : ::lector::Names<EnumerationType>) {
55 if (enumeration_value == value) {
56 return name;
57 }
58 }
59 return ::std::string_view{};
60}
61
62/// @brief Prints a value of a specific type as a string of text.
63/// @tparam Type The type of the value to print.
64/// @param[in] value The value to print.
65/// @return The value as a printed string of text.
66template <typename Type>
67[[nodiscard]] ::std::string print(const Type& value);
68
69/// @brief Prints a boolean value as a string of text.
70/// @param[in] value The boolean value to print.
71/// @return The boolean value as a printed string of text.
72template <>
73[[nodiscard]] inline ::std::string print(const bool& value) {
74 if (value) {
75 return "true";
76 }
77 return "false";
78}
79
80/// @brief Prints an 8-bit natural number as a string of text.
81/// @param[in] value The 8-bit natural number to print.
82/// @return The 8-bit natural number as a printed string of text.
83template <>
84[[nodiscard]] inline ::std::string print(const ::std::uint8_t& value) {
85 return ::std::to_string(value);
86}
87
88/// @brief Prints a 16-bit natural number as a string of text.
89/// @param[in] value The 16-bit natural number to print.
90/// @return The 16-bit natural number as a printed string of text.
91template <>
92[[nodiscard]] inline ::std::string print(const ::std::uint16_t& value) {
93 return ::std::to_string(value);
94}
95
96/// @brief Prints a 32-bit natural number as a string of text.
97/// @param[in] value The 32-bit natural number to print.
98/// @return The 32-bit natural number as a printed string of text.
99template <>
100[[nodiscard]] inline ::std::string print(const ::std::uint32_t& value) {
101 return ::std::to_string(value);
102}
103
104/// @brief Prints a 64-bit natural number as a string of text.
105/// @param[in] value The 64-bit natural number to print.
106/// @return The 64-bit natural number as a printed string of text.
107template <>
108[[nodiscard]] inline ::std::string print(const ::std::uint64_t& value) {
109 return ::std::to_string(value);
110}
111
112/// @brief Prints an 8-bit integer number as a string of text.
113/// @param[in] value The 8-bit integer number to print.
114/// @return The 8-bit integer number as a printed string of text.
115template <>
116[[nodiscard]] inline ::std::string print(const ::std::int8_t& value) {
117 return ::std::to_string(value);
118}
119
120/// @brief Prints a 16-bit integer number as a string of text.
121/// @param[in] value The 16-bit integer number to print.
122/// @return The 16-bit integer number as a printed string of text.
123template <>
124[[nodiscard]] inline ::std::string print(const ::std::int16_t& value) {
125 return ::std::to_string(value);
126}
127
128/// @brief Prints a 32-bit integer number as a string of text.
129/// @param[in] value The 32-bit integer number to print.
130/// @return The 32-bit integer number as a printed string of text.
131template <>
132[[nodiscard]] inline ::std::string print(const ::std::int32_t& value) {
133 return ::std::to_string(value);
134}
135
136/// @brief Prints a 64-bit integer number as a string of text.
137/// @param[in] value The 64-bit integer number to print.
138/// @return The 64-bit integer number as a printed string of text.
139template <>
140[[nodiscard]] inline ::std::string print(const ::std::int64_t& value) {
141 return ::std::to_string(value);
142}
143
144/// @brief Prints a single-precision floating-point number as a string of text.
145/// @param[in] value The single-precision floating-point number to print.
146/// @return The single-precision floating-point number as a printed string of text.
147template <>
148[[nodiscard]] inline ::std::string print(const float& value) {
149 if (value == 0.0F) {
150 return "0";
151 }
152 if (!::std::isfinite(value)) {
153 if (::std::isnan(value)) {
154 return "nan";
155 }
156 if (value < 0.0F) {
157 return "-inf";
158 }
159 return "inf";
160 }
161 const float absolute{::std::abs(value)};
162 constexpr int digits{::std::numeric_limits<float>::max_digits10};
163 ::std::ostringstream stream;
164 if (absolute < 0.001F || absolute >= 10000.0F) {
165 stream << ::std::scientific << ::std::setprecision(digits) << value;
166 } else {
167 stream << ::std::fixed;
168 if (absolute < 0.01F) {
169 stream << ::std::setprecision(digits + 3);
170 } else if (absolute < 0.1F) {
171 stream << ::std::setprecision(digits + 2);
172 } else if (absolute < 1.0F) {
173 stream << ::std::setprecision(digits + 1);
174 } else if (absolute < 10.0F) {
175 stream << ::std::setprecision(digits);
176 } else if (absolute < 100.0F) {
177 stream << ::std::setprecision(digits - 1);
178 } else if (absolute < 1000.0F) {
179 stream << ::std::setprecision(digits - 2);
180 } else {
181 stream << ::std::setprecision(digits - 3);
182 }
183 stream << value;
184 }
185 return stream.str();
186}
187
188/// @brief Prints a double-precision floating-point number as a string of text.
189/// @param[in] value The double-precision floating-point number to print.
190/// @return The double-precision floating-point number as a printed string of text.
191template <>
192[[nodiscard]] inline ::std::string print(const double& value) {
193 if (value == 0.0) {
194 return "0";
195 }
196 if (!::std::isfinite(value)) {
197 if (::std::isnan(value)) {
198 return "nan";
199 }
200 if (value < 0.0) {
201 return "-inf";
202 }
203 return "inf";
204 }
205 const double absolute{::std::abs(value)};
206 constexpr int digits{::std::numeric_limits<double>::max_digits10};
207 ::std::ostringstream stream;
208 if (absolute < 0.001 || absolute >= 10000.0) {
209 stream << ::std::scientific << ::std::setprecision(digits) << value;
210 } else {
211 stream << ::std::fixed;
212 if (absolute < 0.01) {
213 stream << ::std::setprecision(digits + 3);
214 } else if (absolute < 0.1) {
215 stream << ::std::setprecision(digits + 2);
216 } else if (absolute < 1.0) {
217 stream << ::std::setprecision(digits + 1);
218 } else if (absolute < 10.0) {
219 stream << ::std::setprecision(digits);
220 } else if (absolute < 100.0) {
221 stream << ::std::setprecision(digits - 1);
222 } else if (absolute < 1000.0) {
223 stream << ::std::setprecision(digits - 2);
224 } else {
225 stream << ::std::setprecision(digits - 3);
226 }
227 stream << value;
228 }
229 return stream.str();
230}
231
232/// @brief Prints an extended-precision floating-point number as a string of text.
233/// @param[in] value The extended-precision floating-point number to print.
234/// @return The extended-precision floating-point number as a printed string of text.
235template <>
236[[nodiscard]] inline ::std::string print(const long double& value) {
237 if (value == 0.0L) {
238 return "0";
239 }
240 if (!::std::isfinite(value)) {
241 if (::std::isnan(value)) {
242 return "nan";
243 }
244 if (value < 0.0L) {
245 return "-inf";
246 }
247 return "inf";
248 }
249 const long double absolute{::std::abs(value)};
250 constexpr int digits{::std::numeric_limits<long double>::max_digits10};
251 ::std::ostringstream stream;
252 if (absolute < 0.001F || absolute >= 10000.0F) {
253 stream << ::std::scientific << ::std::setprecision(digits) << value;
254 } else {
255 stream << ::std::fixed;
256 if (absolute < 0.01L) {
257 stream << ::std::setprecision(digits + 3);
258 } else if (absolute < 0.1L) {
259 stream << ::std::setprecision(digits + 2);
260 } else if (absolute < 1.0L) {
261 stream << ::std::setprecision(digits + 1);
262 } else if (absolute < 10.0L) {
263 stream << ::std::setprecision(digits);
264 } else if (absolute < 100.0L) {
265 stream << ::std::setprecision(digits - 1);
266 } else if (absolute < 1000.0L) {
267 stream << ::std::setprecision(digits - 2);
268 } else {
269 stream << ::std::setprecision(digits - 3);
270 }
271 stream << value;
272 }
273 return stream.str();
274}
275
276/// @brief Returns a string of text as-is.
277/// @param[in] value The string of text.
278/// @return The string of text.
279template <>
280[[nodiscard]] inline ::std::string print(const ::std::string& value) {
281 return value;
282}
283
284/// @brief Prints a string view as a string of text.
285/// @param[in] value The string view to print.
286/// @return The string view as a printed string of text.
287template <>
288[[nodiscard]] inline ::std::string print(const ::std::string_view& value) {
289 return ::std::string{value};
290}
291
292/// @brief Prints a filesystem path as a string of text.
293/// @param[in] value The filesystem path to print.
294/// @return The filesystem path as a printed string of text.
295template <>
296[[nodiscard]] inline ::std::string print(const ::std::filesystem::path& value) {
297 return value.string();
298}
299
300/// @brief Prints a value of a specific type as a string of text. If the type is an enumeration
301/// type, it must define a specialization of the lector::Names constant for its type; otherwise, the
302/// type must be streamable with the << output stream operator.
303/// @tparam Type The type of the value to print.
304/// @param[in] value The value to print.
305/// @return The value as a printed string of text.
306template <typename Type>
307[[nodiscard]] inline ::std::string print(const Type& value) {
308 if constexpr (::std::is_enum_v<Type>) {
309 return ::std::string{::lector::print_enumeration<Type>(value)};
310 } else {
311 ::std::ostringstream stream;
312 stream << value;
313 return stream.str();
314 }
315}
316
317} // namespace lector
318
319#endif // LECTOR_PRINT_HPP
The Lector library's namespace.
Definition arguments.hpp:45
constexpr ::std::array<::lector::Name< Type >, 0UL > Names
Map of canonical printed names to their corresponding values of a specified type.
Definition print.hpp:44
::std::pair< Type, ::std::string_view > Name
The canonical printed name for a value of a specified type.
Definition print.hpp:39
constexpr ::std::string_view print_enumeration(const EnumerationType value)
Prints a value of a specified enumeration type as a string of text. The enumeration type must define ...
Definition print.hpp:52
::std::string print(const Type &value)
Prints a value of a specific type as a string of text.
Definition print.hpp:307