BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bslfmt_formatterspecificationnumericvalue.h
Go to the documentation of this file.
1/// @file bslfmt_formatterspecificationnumericvalue.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslfmt_formatterspecificationnumericvalue.h -*-C++-*-
8
9#ifndef INCLUDED_BSLFMT_FORMATTERSPECIFICATIONNUMERICVALUE
10#define INCLUDED_BSLFMT_FORMATTERSPECIFICATIONNUMERICVALUE
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bslfmt_formatterspecificationnumericvalue bslfmt_formatterspecificationnumericvalue
16/// @brief Integer value for use within `bsl::format` specification parsers
17/// @addtogroup bsl
18/// @{
19/// @addtogroup bslfmt
20/// @{
21/// @addtogroup bslfmt_formatterspecificationnumericvalue
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bslfmt_formatterspecificationnumericvalue-purpose"> Purpose</a>
26/// * <a href="#bslfmt_formatterspecificationnumericvalue-classes"> Classes </a>
27/// * <a href="#bslfmt_formatterspecificationnumericvalue-description"> Description </a>
28///
29/// # Purpose {#bslfmt_formatterspecificationnumericvalue-purpose}
30/// Integer value for use within `bsl::format` specification parsers
31///
32/// # Classes {#bslfmt_formatterspecificationnumericvalue-classes}
33///
34/// - FormatterSpecificationNumericValue: Category and its optional integer value
35///
36/// @see bslfmt_format.h
37///
38/// # Description {#bslfmt_formatterspecificationnumericvalue-description}
39/// This component provides a a value semantic type to enable
40/// `bslfmt` formatters to return an optional integer value, typically
41/// representing a width or a precision, and a category that determines the
42/// meaning of the integer value (direct value or argument ID), or represents a
43/// category without an integer value (default value or next argument).
44///
45/// This component is for use within `bslfmt` only.
46/// @}
47/** @} */
48/** @} */
49
50/** @addtogroup bsl
51 * @{
52 */
53/** @addtogroup bslfmt
54 * @{
55 */
56/** @addtogroup bslfmt_formatterspecificationnumericvalue
57 * @{
58 */
59
60#include <bslscm_version.h>
61
62#include <bslfmt_formaterror.h>
63
65
66#include <bslmf_assert.h>
67#include <bslmf_isintegral.h>
68#include <bslmf_enableif.h>
69
71#include <bsls_exceptionutil.h>
72#include <bsls_keyword.h>
74
75#include <bslstl_iterator.h>
76#include <bslstl_monostate.h>
77
78#include <locale> // for 'std::ctype', 'locale'
79#include <string> // for 'std::char_traits'
80
81#include <limits.h> // `INT_MAX`
82#include <stdio.h> // for 'snprintf'
83
84#if defined(BSLS_LIBRARYFEATURES_HAS_CPP20_FORMAT)
85# include <format> // for 'std::visit_format_arg'
86#endif
87
88
89namespace bslfmt {
90
91 // =========================================
92 // struct FormatterSpecificationNumericValue
93 // =========================================
94
95/// Type holding a category plus an optional integral value. This is a value
96/// semantic type primarily used to represent width and precision of a format
97/// specification.
98///
99/// See @ref bslfmt_formatterspecificationnumericvalue
101 public:
102 // TYPES
103 enum Category {
104 e_DEFAULT, // Not specified value and category (in the format)
105 e_VALUE, // Contained integer is the value to use
106 e_NEXT_ARG, // Dynamic nested argument
107 e_ARG_ID // Nested argument whose id is the contained integer
108 };
109
110 private:
111 // DATA
112 Category d_category; // category
113 int d_value; // optionally specified integral value
114
115 // FRIENDS
116
118
119 public:
120 // CREATORS
121
122 /// Create a `FormatterSpecificationNumericValue` object with `e_DEFAULT`
123 /// `category` and 0 `value`.
125
126 /// Create a `FormatterSpecificationNumericValue` object with the
127 /// specified `value` and `category` attributes.
130 int value);
131
132 // MANIPULATORS
133
134 /// Parse the string in the random-access-iterator range specified by
135 /// `start` and `end` to extract either a hard-coded integer or a nested
136 /// argument specification. If the specified `needInitialDot` is true the
137 /// string is only parsed if `*start == '.'` and a @ref format_error exception
138 /// is thrown if the string following the initial dot is empty. If an
139 /// error occurs throw an exception of type @ref format_error , otherwise
140 /// update the output `start` iterator to point to the first unparsed
141 /// character of the string and store the parsed result into this object.
142 template <class t_ITER>
144 t_ITER end,
145 bool needInitialDot);
146
147 /// If this object holds a non-dynamic nested value (i.e., `d_type` is
148 /// `e_ARG_ID`) update its value using the arguments stored in the
149 /// specified `context` and update the type to `e_VALUE`. If this object
150 /// holds a dynamic nested value (i.e., `d_type` is `e_NEXT_ARG`) throw an
151 /// exception of type @ref format_error . Otherwise do nothing.
152 template <typename t_FORMAT_CONTEXT>
153 void postprocess(const t_FORMAT_CONTEXT& context);
154
155 // ACCESSORS
156
157 /// Return `true` if this object is equal to the specified `other` object,
158 /// otherwise return `false`. Two `FormatterSpecificationNumericValue`
159 /// objects are considered equal if their `category` and `value` attributes
160 /// are both equal.
162 bool operator==(const FormatterSpecificationNumericValue& other) const;
163
164 /// Return the `category` attribute of this object.
166
167 /// Return the `value` attribute of this object.
168 ///
169 /// \pre The behavior is undefined if `category` is either `e_DEFAULT` or `e_NEXT_ARG_ID`.
171};
172
173 // ===================================================
174 // class FormatterSpecificationNumericValue_ArgVisitor
175 // ===================================================
176
177/// Component-private type to enable extraction of values held by format
178/// contexts during the postprocessing stage of determining the format
179/// specification (typically performed by the `FormatSpecificationParser`).
180/// This type exists so that `FormatSpecificationParser` can update
181/// FormatterSpecificationNumericValue to the value contained by a Standard
182/// @ref basic_format_arg using the Standard @ref visit_format_arg function.
183///
184/// See @ref bslfmt_formatterspecificationnumericvalue
186 private:
187 // DATA
188
189 /// The referenced `FormatterSpecificationNumericValue` to be updated
191
192 public:
193 // CREATORS
194
195 /// Create an instance of `FormatterSpecificationNumericValue_ArgVisitor`
196 /// that refers to the object to which the specified `valuePtr` points.
199
200 // ACCESSORS
201
202 /// Throw an exception of type @ref format_error . This indicates visitation
203 /// of an argument with no value, which would indicate a logic error.
204 ///
205 /// \note Note that this would only be called when extracting a value from a
206 /// @ref basic_format_arg that holds no value, which would normally indicate
207 /// an incorrect argument type.
208 void operator()(bsl::monostate) const;
209
210 /// Throw an exception of type @ref format_error . This indicates visitation
211 /// of a `bsl::format` argument of boolean type, usually resulting from a
212 /// user error such as calling `bsl::format("{:{}}", value, b)` where `b`
213 /// is `bool`.
214 void operator()(bool) const;
215
216 /// Update the `FormatterSpecificationNumericValue` referenced by this
217 /// visitor by setting its `category` attribute to `e_VALUE` and its
218 /// `value` to the specified `value`.
219 template <class t_TYPE>
221 operator()(t_TYPE value) const;
222
223 /// Throw an exception of type @ref format_error . This indicates visitation
224 /// of a `bsl::format` argument of non-integral type, usually resulting
225 /// from a user error such as calling `bsl::format("{:{}}", value, f)`
226 /// where `f` has a floating point type.
227 template <class t_TYPE>
229 operator()(t_TYPE value) const;
230};
231
232// ============================================================================
233// INLINE DEFINITIONS
234// ============================================================================
235
236 // -----------------------------------------
237 // struct FormatterSpecificationNumericValue
238 // -----------------------------------------
239
240// CREATORS
241inline
244: d_category(e_DEFAULT)
245, d_value(0)
246{
247}
248inline
251 Category category,
252 int value)
253: d_category(category)
254, d_value(value)
255{
256}
257
258// MANIPULATORS
259template <class t_ITER>
261 t_ITER *start,
262 t_ITER end,
263 bool needInitialDot)
264{
265 // Handle empty string or empty specification.
266 if (*start == end || **start == '}') {
267 d_category = e_DEFAULT;
268 return; // RETURN
269 }
270
271 t_ITER current = *start;
272
273 if (needInitialDot) {
274 // No dot therefore no precision: early successful exit
275 if (*current != '.') {
276 return; // RETURN
277 }
278 ++current;
279
280 // Found a dot but nothing afterwards: it is an invalid precision spec.
281 if (current == end) {
282 BSLS_THROW(bsl::format_error(
283 "Invalid Precision (nothing after '.')")); // THROW
284 }
285 }
286
287 bool isArgId = false;
288
289 if (*current == '{') {
290 current++;
291 if (current == end) {
292 BSLS_THROW(bsl::format_error("Nested arg id closing '}' "
293 "is missing")); // THROW
294 }
295
296 // Early exit for a non-numbered replacement field
297 if (*current == '}') {
298 d_category = e_NEXT_ARG;
299 d_value = 0;
300 *start = current + 1;
301 return; // RETURN
302 }
303 isArgId = true;
304 }
305
306 int digitCount = 0;
307 int value = 0;
308
309 long long accumulator = 0;
310 while (*current >= '0' && *current <= '9') {
311 accumulator = (accumulator * 10) + static_cast<int>(*current - '0');
312 if (accumulator > INT_MAX) {
313 BSLS_THROW(bsl::format_error("Too large integer value")); // THROW
314 }
315 ++digitCount;
316 ++current;
317 if (current == end) {
318 break; // BREAK
319 }
320 }
321 value = static_cast<int>(accumulator);
322
323 // No digits
324 if (digitCount == 0) {
325 // If we have either specified the "precision dot" or if we know we
326 // have a numbered replacement field then digits are non-optional.
327 if (isArgId) {
328 BSLS_THROW(bsl::format_error(
329 "Nested argument id is not numeric")); // THROW
330 }
331 if (needInitialDot) {
332 BSLS_THROW(bsl::format_error(
333 "Invalid precision (no digits following '.')")); // THROW
334 }
335 d_category = e_DEFAULT;
336 }
337 // At least one digit
338 else {
339 // As we do not allow + or - the value must be non-negative.
340 d_value = value;
341
342 if (isArgId) {
343 // Relative argument references must have a closing brace.
344 if (current == end || *current != '}') {
345 BSLS_THROW(bsl::format_error(
346 "Nested argument id '}' is missing")); // THROW
347 }
348 ++current;
349 }
350
351 d_category = isArgId ? e_ARG_ID : e_VALUE;
352 }
353
354 *start = current;
355}
356
357template <typename t_FORMAT_CONTEXT>
358inline
360 const t_FORMAT_CONTEXT& context)
361{
362 // Non-nested argument id: value does not change
365 return; // RETURN
366 }
367
368 // Parser parsing converts dynamic nested widths and precisions
369 // (`e_NEXT_ARG`) into non-dynamic ones (`e_ARG_ID`). As a result, if we
370 // encounter a dynamic nested width at this stage it indicates a logic
371 // error.
373 BSLS_THROW(bsl::format_error(
374 "INTERNAL ERROR: Unconverted dynamic nested argument")); // THROW
375 }
376
378 {
379#if defined(BSLS_LIBRARYFEATURES_HAS_CPP20_FORMAT)
380 // Depending on the type of t_FORMAT_CONTEXT we may need to use
381 // @ref visit_format_arg from `bslfmt` or from `std`.
382 using namespace std;
383#endif
384 visit_format_arg(visitor, context.arg(d_value));
385 }
386}
387
388// ACCESSORS
389inline
392 const FormatterSpecificationNumericValue& other) const
393{
394 return d_category == other.d_category && d_value == other.d_value;
395}
396
397inline
400{
401 if (d_category == e_DEFAULT) {
402 BSLS_THROW(bsl::format_error("INTERNAL ERROR: Access to unspecified "
403 "nested value in format spec.")); // THROW
404 }
405 if (d_category == e_NEXT_ARG) {
407 bsl::format_error("INTERNAL ERROR: Failed to identify arg-id for "
408 "nested value in format spec.")); // THROW
409 }
410 return d_value;
411}
412
413inline
417{
418 return d_category;
419}
420
421 // ----------------------------------------------------
422 // struct FormatterSpecificationNumericValue_ArgVisitor
423 // ----------------------------------------------------
424
425inline
432
433inline
435 bsl::monostate) const
436{
437 BSLS_THROW(bsl::format_error("Nested argument id out of range"));
438}
439
440inline
442{
443 BSLS_THROW(bsl::format_error("Nested value argument must be integral"));
444}
445
446template <class t_TYPE>
449{
450 if (x < 0 || x > INT_MAX) {
452 bsl::format_error("Nested value argument out of range")); // THROW
453 }
454 d_value_p->d_value = static_cast<int>(x);
455 d_value_p->d_category = FormatterSpecificationNumericValue::e_VALUE;
456}
457
458template <class t_TYPE>
461{
462 BSLS_THROW(bsl::format_error("Nested value argument must be integral"));
463}
464
465} // close package namespace
466
467
468#endif // INCLUDED_BSLFMT_FORMATTERSPECIFICATIONNUMERICVALUE
469
470// ----------------------------------------------------------------------------
471// Copyright 2024 Bloomberg Finance L.P.
472//
473// Licensed under the Apache License, Version 2.0 (the "License");
474// you may not use this file except in compliance with the License.
475// You may obtain a copy of the License at
476//
477// http://www.apache.org/licenses/LICENSE-2.0
478//
479// Unless required by applicable law or agreed to in writing, software
480// distributed under the License is distributed on an "AS IS" BASIS,
481// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
482// See the License for the specific language governing permissions and
483// limitations under the License.
484// ----------------------------- END-OF-FILE ----------------------------------
485
486/** @} */
487/** @} */
488/** @} */
Definition bslfmt_formatterspecificationnumericvalue.h:185
FormatterSpecificationNumericValue_ArgVisitor(FormatterSpecificationNumericValue *valuePtr)
Definition bslfmt_formatterspecificationnumericvalue.h:427
bsl::enable_if<!bsl::is_integral< t_TYPE >::value >::type operator()(t_TYPE value) const
void operator()(bsl::monostate) const
Definition bslfmt_formatterspecificationnumericvalue.h:434
#define BSLS_THROW(X)
Definition bsls_exceptionutil.h:374
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_KEYWORD_CONSTEXPR_CPP20
Definition bsls_keyword.h:645
Definition bslfmt_enablestreamedformatter.h:130
bsl::invoke_result< t_VISITOR &, bsl::monostate & >::type visit_format_arg(t_VISITOR &visitor, basic_format_arg< t_CONTEXT > arg)
Definition bslfmt_format_arg.h:907
Definition bdldfp_decimal.h:5549
Definition bslmf_enableif.h:530
Definition bslstl_monostate.h:77
Definition bslfmt_formatterspecificationnumericvalue.h:100
BSLS_KEYWORD_CONSTEXPR_CPP20 FormatterSpecificationNumericValue()
Definition bslfmt_formatterspecificationnumericvalue.h:243
Category
Definition bslfmt_formatterspecificationnumericvalue.h:103
@ e_VALUE
Definition bslfmt_formatterspecificationnumericvalue.h:105
@ e_ARG_ID
Definition bslfmt_formatterspecificationnumericvalue.h:107
@ e_NEXT_ARG
Definition bslfmt_formatterspecificationnumericvalue.h:106
@ e_DEFAULT
Definition bslfmt_formatterspecificationnumericvalue.h:104
void postprocess(const t_FORMAT_CONTEXT &context)
Definition bslfmt_formatterspecificationnumericvalue.h:359
BSLS_KEYWORD_CONSTEXPR_CPP20 bool operator==(const FormatterSpecificationNumericValue &other) const
Definition bslfmt_formatterspecificationnumericvalue.h:391
void BSLS_KEYWORD_CONSTEXPR_CPP20 parse(t_ITER *start, t_ITER end, bool needInitialDot)
BSLS_KEYWORD_CONSTEXPR_CPP20 int value() const
Definition bslfmt_formatterspecificationnumericvalue.h:399
BSLS_KEYWORD_CONSTEXPR_CPP20 Category category() const
Return the category attribute of this object.
Definition bslfmt_formatterspecificationnumericvalue.h:416