1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /**
18 *
19 * This file provides a generic interface for converting objects to and from
20 * string-like types (std::string, fbstring, StringPiece), as well as
21 * range-checked conversions between numeric and enum types. The mechanisms are
22 * extensible, so that user-specified types can add folly::to support.
23 *
24 *******************************************************************************
25 * TYPE -> STRING CONVERSIONS
26 *******************************************************************************
27 * You can call the to<std::string> or to<fbstring>. These are variadic
28 * functions that convert their arguments to strings, and concatenate them to
29 * form a result. So, for example,
30 *
31 * auto str = to<std::string>(123, "456", 789);
32 *
33 * Sets str to "123456789".
34 *
35 * In addition to just concatenating the arguments, related functions can
36 * delimit them with some string: toDelim<std::string>(",", "123", 456, "789")
37 * will return the string "123,456,789".
38 *
39 * toAppend does not return a string; instead, it takes a pointer to a string as
40 * its last argument, and appends the result of the concatenation into it:
41 * std::string str = "123";
42 * toAppend(456, "789", &str); // Now str is "123456789".
43 *
44 * The toAppendFit function acts like toAppend, but it precalculates the size
45 * required to perform the append operation, and reserves that space in the
46 * output string before actually inserting its arguments. This can sometimes
47 * save on string expansion, but beware: appending to the same string many times
48 * with toAppendFit is likely a pessimization, since it will resize the string
49 * once per append.
50 *
51 * The combination of the append and delim variants also exist: toAppendDelim
52 * and toAppendDelimFit are defined, with the obvious semantics.
53 *
54 *******************************************************************************
55 * STRING -> TYPE CONVERSIONS
56 *******************************************************************************
57 * Going in the other direction, and parsing a string into a C++ type, is also
58 * supported:
59 * to<int>("123"); // Returns 123.
60 *
61 * Out of range (e.g. to<std::uint8_t>("1000")), or invalidly formatted (e.g.
62 * to<int>("four")) inputs will throw. If throw-on-error is undesirable (for
63 * instance: you're dealing with untrusted input, and want to protect yourself
64 * from users sending you down a very slow exception-throwing path), you can use
65 * tryTo<T>, which will return an Expected<T, ConversionCode>.
66 *
67 * There are overloads of to() and tryTo() that take a StringPiece*. These parse
68 * out a type from the beginning of a string, and modify the passed-in
69 * StringPiece to indicate the portion of the string not consumed.
70 *
71 *******************************************************************************
72 * NUMERIC / ENUM CONVERSIONS
73 *******************************************************************************
74 * Conv also supports a to<T>(S) overload, where T and S are numeric or enum
75 * types, that checks to see that the target type can represent its argument,
76 * and will throw if it cannot. This includes cases where a floating point ->
77 * integral conversion is attempted on a value with a non-zero fractional
78 * component, and integral -> floating point conversions that would lose
79 * precision. Enum conversions are range-checked for the underlying type of the
80 * enum, but there is no check that the input value is a valid choice of enum
81 * value.
82 *
83 *******************************************************************************
84 * CUSTOM TYPE CONVERSIONS
85 *******************************************************************************
86 * Users may customize the string conversion functionality for their own data
87 * types, . The key functions you should implement are:
88 * // Two functions to allow conversion to your type from a string.
89 * Expected<StringPiece, ConversionCode> parseTo(folly::StringPiece in,
90 * YourType& out);
91 * YourErrorType makeConversionError(YourErrorType in, StringPiece in);
92 * // Two functions to allow conversion from your type to a string.
93 * template <class String>
94 * void toAppend(const YourType& in, String* out);
95 * size_t estimateSpaceNeeded(const YourType& in);
96 *
97 * These are documented below, inline.
98 */
99
100 #pragma once
101
102 #include <algorithm>
103 #include <cassert>
104 #include <cctype>
105 #include <climits>
106 #include <cstddef>
107 #include <limits>
108 #include <stdexcept>
109 #include <string>
110 #include <tuple>
111 #include <type_traits>
112 #include <utility>
113
114 #include <double-conversion/double-conversion.h> // V8 JavaScript implementation
115
116 #include <folly/Demangle.h>
117 #include <folly/Expected.h>
118 #include <folly/FBString.h>
119 #include <folly/Likely.h>
120 #include <folly/Portability.h>
121 #include <folly/Range.h>
122 #include <folly/Traits.h>
123 #include <folly/Unit.h>
124 #include <folly/Utility.h>
125 #include <folly/lang/Exception.h>
126 #include <folly/lang/Pretty.h>
127 #include <folly/lang/ToAscii.h>
128 #include <folly/portability/Math.h>
129
130 namespace folly {
131
132 // Keep this in sync with kErrorStrings in Conv.cpp
133 enum class ConversionCode : unsigned char {
134 SUCCESS,
135 EMPTY_INPUT_STRING,
136 NO_DIGITS,
137 BOOL_OVERFLOW,
138 BOOL_INVALID_VALUE,
139 NON_DIGIT_CHAR,
140 INVALID_LEADING_CHAR,
141 POSITIVE_OVERFLOW,
142 NEGATIVE_OVERFLOW,
143 STRING_TO_FLOAT_ERROR,
144 NON_WHITESPACE_AFTER_END,
145 ARITH_POSITIVE_OVERFLOW,
146 ARITH_NEGATIVE_OVERFLOW,
147 ARITH_LOSS_OF_PRECISION,
148 NUM_ERROR_CODES, // has to be the last entry
149 };
150
151 struct ConversionErrorBase : std::range_error {
152 using std::range_error::range_error;
153 };
154
155 class ConversionError : public ConversionErrorBase {
156 public:
ConversionError(const std::string & str,ConversionCode code)157 ConversionError(const std::string& str, ConversionCode code)
158 : ConversionErrorBase(str), code_(code) {}
159
ConversionError(const char * str,ConversionCode code)160 ConversionError(const char* str, ConversionCode code)
161 : ConversionErrorBase(str), code_(code) {}
162
errorCode()163 ConversionCode errorCode() const { return code_; }
164
165 private:
166 ConversionCode code_;
167 };
168
169 /*******************************************************************************
170 * Custom Error Translation
171 *
172 * Your overloaded parseTo() function can return a custom error code on failure.
173 * ::folly::to() will call makeConversionError to translate that error code into
174 * an object to throw. makeConversionError is found by argument-dependent
175 * lookup. It should have this signature:
176 *
177 * namespace other_namespace {
178 * enum YourErrorCode { BAD_ERROR, WORSE_ERROR };
179 *
180 * struct YourConversionError : ConversionErrorBase {
181 * YourConversionError(const char* what) : ConversionErrorBase(what) {}
182 * };
183 *
184 * YourConversionError
185 * makeConversionError(YourErrorCode code, ::folly::StringPiece sp) {
186 * ...
187 * return YourConversionError(messageString);
188 * }
189 ******************************************************************************/
190 ConversionError makeConversionError(ConversionCode code, StringPiece input);
191
192 namespace detail {
193 /**
194 * Enforce that the suffix following a number is made up only of whitespace.
195 */
enforceWhitespaceErr(StringPiece sp)196 inline ConversionCode enforceWhitespaceErr(StringPiece sp) {
197 for (auto c : sp) {
198 if (UNLIKELY(!std::isspace(c))) {
199 return ConversionCode::NON_WHITESPACE_AFTER_END;
200 }
201 }
202 return ConversionCode::SUCCESS;
203 }
204
205 /**
206 * Keep this implementation around for prettyToDouble().
207 */
enforceWhitespace(StringPiece sp)208 inline void enforceWhitespace(StringPiece sp) {
209 auto err = enforceWhitespaceErr(sp);
210 if (err != ConversionCode::SUCCESS) {
211 throw_exception(makeConversionError(err, sp));
212 }
213 }
214 } // namespace detail
215
216 /**
217 * The identity conversion function.
218 * tryTo<T>(T) returns itself for all types T.
219 */
220 template <class Tgt, class Src>
221 typename std::enable_if<
222 std::is_same<Tgt, typename std::decay<Src>::type>::value,
223 Expected<Tgt, ConversionCode>>::type
tryTo(Src && value)224 tryTo(Src&& value) {
225 return std::forward<Src>(value);
226 }
227
228 template <class Tgt, class Src>
229 typename std::enable_if<
230 std::is_same<Tgt, typename std::decay<Src>::type>::value,
231 Tgt>::type
to(Src && value)232 to(Src&& value) {
233 return std::forward<Src>(value);
234 }
235
236 /*******************************************************************************
237 * Arithmetic to boolean
238 ******************************************************************************/
239
240 /**
241 * Unchecked conversion from arithmetic to boolean. This is different from the
242 * other arithmetic conversions because we use the C convention of treating any
243 * non-zero value as true, instead of range checking.
244 */
245 template <class Tgt, class Src>
246 typename std::enable_if<
247 is_arithmetic_v<Src> && !std::is_same<Tgt, Src>::value &&
248 std::is_same<Tgt, bool>::value,
249 Expected<Tgt, ConversionCode>>::type
tryTo(const Src & value)250 tryTo(const Src& value) {
251 return value != Src();
252 }
253
254 template <class Tgt, class Src>
255 typename std::enable_if<
256 is_arithmetic_v<Src> && !std::is_same<Tgt, Src>::value &&
257 std::is_same<Tgt, bool>::value,
258 Tgt>::type
to(const Src & value)259 to(const Src& value) {
260 return value != Src();
261 }
262
263 /*******************************************************************************
264 * Anything to string
265 ******************************************************************************/
266
267 namespace detail {
268
269 #ifdef _MSC_VER
270 // MSVC can't quite figure out the LastElementImpl::call() stuff
271 // in the base implementation, so we have to use tuples instead,
272 // which result in significantly more templates being compiled,
273 // though the runtime performance is the same.
274
275 template <typename... Ts>
276 auto getLastElement(Ts&&... ts) -> decltype(std::get<sizeof...(Ts) - 1>(
277 std::forward_as_tuple(std::forward<Ts>(ts)...))) {
278 return std::get<sizeof...(Ts) - 1>(
279 std::forward_as_tuple(std::forward<Ts>(ts)...));
280 }
281
getLastElement()282 inline void getLastElement() {}
283
284 template <size_t size, typename... Ts>
285 struct LastElementType : std::tuple_element<size - 1, std::tuple<Ts...>> {};
286
287 template <>
288 struct LastElementType<0> {
289 using type = void;
290 };
291
292 template <class... Ts>
293 struct LastElement
294 : std::decay<typename LastElementType<sizeof...(Ts), Ts...>::type> {};
295 #else
296 template <typename... Ts>
297 struct LastElementImpl {
298 static void call(Ignored<Ts>...) {}
299 };
300
301 template <typename Head, typename... Ts>
302 struct LastElementImpl<Head, Ts...> {
303 template <typename Last>
304 static Last call(Ignored<Ts>..., Last&& last) {
305 return std::forward<Last>(last);
306 }
307 };
308
309 template <typename... Ts>
310 auto getLastElement(const Ts&... ts)
311 -> decltype(LastElementImpl<Ts...>::call(ts...)) {
312 return LastElementImpl<Ts...>::call(ts...);
313 }
314
315 template <class... Ts>
316 struct LastElement : std::decay<decltype(LastElementImpl<Ts...>::call(
317 std::declval<Ts>()...))> {};
318 #endif
319
320 } // namespace detail
321
322 /*******************************************************************************
323 * Conversions from integral types to string types.
324 ******************************************************************************/
325
326 #if FOLLY_HAVE_INT128_T
327 namespace detail {
328
329 template <typename IntegerType>
330 constexpr unsigned int digitsEnough() {
331 // digits10 returns the number of decimal digits that this type can represent,
332 // not the number of characters required for the max value, so we need to add
333 // one. ex: char digits10 returns 2, because 256-999 cannot be represented,
334 // but we need 3.
335 auto const digits10 = std::numeric_limits<IntegerType>::digits10;
336 return static_cast<unsigned int>(digits10) + 1;
337 }
338
339 inline size_t unsafeTelescope128(
340 char* buffer, size_t room, unsigned __int128 x) {
341 typedef unsigned __int128 Usrc;
342 size_t p = room - 1;
343
344 while (x >= (Usrc(1) << 64)) { // Using 128-bit division while needed
345 const auto y = x / 10;
346 const auto digit = x % 10;
347
348 buffer[p--] = static_cast<char>('0' + digit);
349 x = y;
350 }
351
352 uint64_t xx = static_cast<uint64_t>(x); // Rest uses faster 64-bit division
353
354 while (xx >= 10) {
355 const auto y = xx / 10ULL;
356 const auto digit = xx % 10ULL;
357
358 buffer[p--] = static_cast<char>('0' + digit);
359 xx = y;
360 }
361
362 buffer[p] = static_cast<char>('0' + xx);
363
364 return p;
365 }
366
367 } // namespace detail
368 #endif
369
370 /**
371 * A single char gets appended.
372 */
373 template <class Tgt>
374 void toAppend(char value, Tgt* result) {
375 *result += value;
376 }
377
378 template <class T>
379 constexpr typename std::enable_if<std::is_same<T, char>::value, size_t>::type
380 estimateSpaceNeeded(T) {
381 return 1;
382 }
383
384 template <size_t N>
385 constexpr size_t estimateSpaceNeeded(const char (&)[N]) {
386 return N;
387 }
388
389 /**
390 * Everything implicitly convertible to const char* gets appended.
391 */
392 template <class Tgt, class Src>
393 typename std::enable_if<
394 std::is_convertible<Src, const char*>::value &&
395 IsSomeString<Tgt>::value>::type
396 toAppend(Src value, Tgt* result) {
397 // Treat null pointers like an empty string, as in:
398 // operator<<(std::ostream&, const char*).
399 const char* c = value;
400 if (c) {
401 result->append(value);
402 }
403 }
404
405 template <class Src>
406 typename std::enable_if<std::is_convertible<Src, const char*>::value, size_t>::
407 type
408 estimateSpaceNeeded(Src value) {
409 const char* c = value;
410 if (c) {
411 return folly::StringPiece(value).size();
412 };
413 return 0;
414 }
415
416 template <class Src>
417 typename std::enable_if<IsSomeString<Src>::value, size_t>::type
418 estimateSpaceNeeded(Src const& value) {
419 return value.size();
420 }
421
422 template <class Src>
423 typename std::enable_if<
424 std::is_convertible<Src, folly::StringPiece>::value &&
425 !IsSomeString<Src>::value &&
426 !std::is_convertible<Src, const char*>::value,
427 size_t>::type
428 estimateSpaceNeeded(Src value) {
429 return folly::StringPiece(value).size();
430 }
431
432 template <>
433 inline size_t estimateSpaceNeeded(std::nullptr_t /* value */) {
434 return 0;
435 }
436
437 template <class Src>
438 typename std::enable_if<
439 std::is_pointer<Src>::value &&
440 IsSomeString<std::remove_pointer<Src>>::value,
441 size_t>::type
442 estimateSpaceNeeded(Src value) {
443 return value->size();
444 }
445
446 /**
447 * Strings get appended, too.
448 */
449 template <class Tgt, class Src>
450 typename std::enable_if<
451 IsSomeString<Src>::value && IsSomeString<Tgt>::value>::type
452 toAppend(const Src& value, Tgt* result) {
453 result->append(value);
454 }
455
456 /**
457 * and StringPiece objects too
458 */
459 template <class Tgt>
460 typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(
461 StringPiece value, Tgt* result) {
462 result->append(value.data(), value.size());
463 }
464
465 /**
466 * There's no implicit conversion from fbstring to other string types,
467 * so make a specialization.
468 */
469 template <class Tgt>
470 typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(
471 const fbstring& value, Tgt* result) {
472 result->append(value.data(), value.size());
473 }
474
475 #if FOLLY_HAVE_INT128_T
476 /**
477 * Special handling for 128 bit integers.
478 */
479
480 template <class Tgt>
481 void toAppend(__int128 value, Tgt* result) {
482 typedef unsigned __int128 Usrc;
483 char buffer[detail::digitsEnough<unsigned __int128>() + 1];
484 size_t p;
485
486 if (value < 0) {
487 p = detail::unsafeTelescope128(buffer, sizeof(buffer), -Usrc(value));
488 buffer[--p] = '-';
489 } else {
490 p = detail::unsafeTelescope128(buffer, sizeof(buffer), value);
491 }
492
493 result->append(buffer + p, buffer + sizeof(buffer));
494 }
495
496 template <class Tgt>
497 void toAppend(unsigned __int128 value, Tgt* result) {
498 char buffer[detail::digitsEnough<unsigned __int128>()];
499 size_t p;
500
501 p = detail::unsafeTelescope128(buffer, sizeof(buffer), value);
502
503 result->append(buffer + p, buffer + sizeof(buffer));
504 }
505
506 template <class T>
507 constexpr
508 typename std::enable_if<std::is_same<T, __int128>::value, size_t>::type
509 estimateSpaceNeeded(T) {
510 return detail::digitsEnough<__int128>();
511 }
512
513 template <class T>
514 constexpr typename std::
515 enable_if<std::is_same<T, unsigned __int128>::value, size_t>::type
516 estimateSpaceNeeded(T) {
517 return detail::digitsEnough<unsigned __int128>();
518 }
519
520 #endif
521
522 /**
523 * int32_t and int64_t to string (by appending) go through here. The
524 * result is APPENDED to a preexisting string passed as the second
525 * parameter. This should be efficient with fbstring because fbstring
526 * incurs no dynamic allocation below 23 bytes and no number has more
527 * than 22 bytes in its textual representation (20 for digits, one for
528 * sign, one for the terminating 0).
529 */
530 template <class Tgt, class Src>
531 typename std::enable_if<
532 is_integral_v<Src> && is_signed_v<Src> && IsSomeString<Tgt>::value &&
533 sizeof(Src) >= 4>::type
534 toAppend(Src value, Tgt* result) {
535 char buffer[to_ascii_size_max_decimal<uint64_t>];
536 auto uvalue = value < 0 ? ~static_cast<uint64_t>(value) + 1
537 : static_cast<uint64_t>(value);
538 if (value < 0) {
539 result->push_back('-');
540 }
541 result->append(buffer, to_ascii_decimal(buffer, uvalue));
542 }
543
544 template <class Src>
545 typename std::enable_if<
546 is_integral_v<Src> && is_signed_v<Src> && sizeof(Src) >= 4 &&
547 sizeof(Src) < 16,
548 size_t>::type
549 estimateSpaceNeeded(Src value) {
550 auto uvalue = value < 0 ? ~static_cast<uint64_t>(value) + 1
551 : static_cast<uint64_t>(value);
552 return size_t(value < 0) + to_ascii_size_decimal(uvalue);
553 }
554
555 /**
556 * As above, but for uint32_t and uint64_t.
557 */
558 template <class Tgt, class Src>
559 typename std::enable_if<
560 is_integral_v<Src> && !is_signed_v<Src> && IsSomeString<Tgt>::value &&
561 sizeof(Src) >= 4>::type
562 toAppend(Src value, Tgt* result) {
563 char buffer[to_ascii_size_max_decimal<uint64_t>];
564 result->append(buffer, to_ascii_decimal(buffer, value));
565 }
566
567 template <class Src>
568 typename std::enable_if<
569 is_integral_v<Src> && !is_signed_v<Src> && sizeof(Src) >= 4 &&
570 sizeof(Src) < 16,
571 size_t>::type
572 estimateSpaceNeeded(Src value) {
573 return to_ascii_size_decimal(value);
574 }
575
576 /**
577 * All small signed and unsigned integers to string go through 32-bit
578 * types int32_t and uint32_t, respectively.
579 */
580 template <class Tgt, class Src>
581 typename std::enable_if<
582 is_integral_v<Src> && IsSomeString<Tgt>::value && sizeof(Src) < 4>::type
583 toAppend(Src value, Tgt* result) {
584 typedef typename std::conditional<is_signed_v<Src>, int64_t, uint64_t>::type
585 Intermediate;
586 toAppend<Tgt>(static_cast<Intermediate>(value), result);
587 }
588
589 template <class Src>
590 typename std::enable_if<
591 is_integral_v<Src> && sizeof(Src) < 4 && !std::is_same<Src, char>::value,
592 size_t>::type
593 estimateSpaceNeeded(Src value) {
594 typedef typename std::conditional<is_signed_v<Src>, int64_t, uint64_t>::type
595 Intermediate;
596 return estimateSpaceNeeded(static_cast<Intermediate>(value));
597 }
598
599 /**
600 * Enumerated values get appended as integers.
601 */
602 template <class Tgt, class Src>
603 typename std::enable_if<
604 std::is_enum<Src>::value && IsSomeString<Tgt>::value>::type
605 toAppend(Src value, Tgt* result) {
606 toAppend(to_underlying(value), result);
607 }
608
609 template <class Src>
610 typename std::enable_if<std::is_enum<Src>::value, size_t>::type
611 estimateSpaceNeeded(Src value) {
612 return estimateSpaceNeeded(to_underlying(value));
613 }
614
615 /*******************************************************************************
616 * Conversions from floating-point types to string types.
617 ******************************************************************************/
618
619 namespace detail {
620 constexpr int kConvMaxDecimalInShortestLow = -6;
621 constexpr int kConvMaxDecimalInShortestHigh = 21;
622 } // namespace detail
623
624 /** Wrapper around DoubleToStringConverter **/
625 template <class Tgt, class Src>
626 typename std::enable_if<
627 std::is_floating_point<Src>::value && IsSomeString<Tgt>::value>::type
628 toAppend(
629 Src value,
630 Tgt* result,
631 double_conversion::DoubleToStringConverter::DtoaMode mode,
632 unsigned int numDigits) {
633 using namespace double_conversion;
634 DoubleToStringConverter conv(
635 DoubleToStringConverter::NO_FLAGS,
636 "Infinity",
637 "NaN",
638 'E',
639 detail::kConvMaxDecimalInShortestLow,
640 detail::kConvMaxDecimalInShortestHigh,
641 6, // max leading padding zeros
642 1); // max trailing padding zeros
643 char buffer[256];
644 StringBuilder builder(buffer, sizeof(buffer));
645 FOLLY_PUSH_WARNING
646 FOLLY_CLANG_DISABLE_WARNING("-Wcovered-switch-default")
647 switch (mode) {
648 case DoubleToStringConverter::SHORTEST:
649 conv.ToShortest(value, &builder);
650 break;
651 case DoubleToStringConverter::SHORTEST_SINGLE:
652 conv.ToShortestSingle(static_cast<float>(value), &builder);
653 break;
654 case DoubleToStringConverter::FIXED:
655 conv.ToFixed(value, int(numDigits), &builder);
656 break;
657 case DoubleToStringConverter::PRECISION:
658 default:
659 assert(mode == DoubleToStringConverter::PRECISION);
660 conv.ToPrecision(value, int(numDigits), &builder);
661 break;
662 }
663 FOLLY_POP_WARNING
664 const size_t length = size_t(builder.position());
665 builder.Finalize();
666 result->append(buffer, length);
667 }
668
669 /**
670 * As above, but for floating point
671 */
672 template <class Tgt, class Src>
673 typename std::enable_if<
674 std::is_floating_point<Src>::value && IsSomeString<Tgt>::value>::type
675 toAppend(Src value, Tgt* result) {
676 toAppend(
677 value, result, double_conversion::DoubleToStringConverter::SHORTEST, 0);
678 }
679
680 /**
681 * Upper bound of the length of the output from
682 * DoubleToStringConverter::ToShortest(double, StringBuilder*),
683 * as used in toAppend(double, string*).
684 */
685 template <class Src>
686 typename std::enable_if<std::is_floating_point<Src>::value, size_t>::type
687 estimateSpaceNeeded(Src value) {
688 // kBase10MaximalLength is 17. We add 1 for decimal point,
689 // e.g. 10.0/9 is 17 digits and 18 characters, including the decimal point.
690 constexpr int kMaxMantissaSpace =
691 double_conversion::DoubleToStringConverter::kBase10MaximalLength + 1;
692 // strlen("E-") + digits10(numeric_limits<double>::max_exponent10)
693 constexpr int kMaxExponentSpace = 2 + 3;
694 static const int kMaxPositiveSpace = std::max({
695 // E.g. 1.1111111111111111E-100.
696 kMaxMantissaSpace + kMaxExponentSpace,
697 // E.g. 0.000001.1111111111111111, if kConvMaxDecimalInShortestLow is -6.
698 kMaxMantissaSpace - detail::kConvMaxDecimalInShortestLow,
699 // If kConvMaxDecimalInShortestHigh is 21, then 1e21 is the smallest
700 // number > 1 which ToShortest outputs in exponential notation,
701 // so 21 is the longest non-exponential number > 1.
702 detail::kConvMaxDecimalInShortestHigh,
703 });
704 return size_t(
705 kMaxPositiveSpace +
706 (value < 0 ? 1 : 0)); // +1 for minus sign, if negative
707 }
708
709 /**
710 * This can be specialized, together with adding specialization
711 * for estimateSpaceNeed for your type, so that we allocate
712 * as much as you need instead of the default
713 */
714 template <class Src>
715 struct HasLengthEstimator : std::false_type {};
716
717 template <class Src>
718 constexpr typename std::enable_if<
719 !std::is_fundamental<Src>::value &&
720 #if FOLLY_HAVE_INT128_T
721 // On OSX 10.10, is_fundamental<__int128> is false :-O
722 !std::is_same<__int128, Src>::value &&
723 !std::is_same<unsigned __int128, Src>::value &&
724 #endif
725 !IsSomeString<Src>::value &&
726 !std::is_convertible<Src, const char*>::value &&
727 !std::is_convertible<Src, StringPiece>::value &&
728 !std::is_enum<Src>::value && !HasLengthEstimator<Src>::value,
729 size_t>::type
730 estimateSpaceNeeded(const Src&) {
731 return sizeof(Src) + 1; // dumbest best effort ever?
732 }
733
734 namespace detail {
735
736 template <class Tgt>
737 typename std::enable_if<IsSomeString<Tgt>::value, size_t>::type
738 estimateSpaceToReserve(size_t sofar, Tgt*) {
739 return sofar;
740 }
741
742 template <class T, class... Ts>
743 size_t estimateSpaceToReserve(size_t sofar, const T& v, const Ts&... vs) {
744 return estimateSpaceToReserve(sofar + estimateSpaceNeeded(v), vs...);
745 }
746
747 template <class... Ts>
748 void reserveInTarget(const Ts&... vs) {
749 getLastElement(vs...)->reserve(estimateSpaceToReserve(0, vs...));
750 }
751
752 template <class Delimiter, class... Ts>
753 void reserveInTargetDelim(const Delimiter& d, const Ts&... vs) {
754 static_assert(sizeof...(vs) >= 2, "Needs at least 2 args");
755 size_t fordelim = (sizeof...(vs) - 2) *
756 estimateSpaceToReserve(0, d, static_cast<std::string*>(nullptr));
757 getLastElement(vs...)->reserve(estimateSpaceToReserve(fordelim, vs...));
758 }
759
760 /**
761 * Variadic base case: append one element
762 */
763 template <class T, class Tgt>
764 typename std::enable_if<
765 IsSomeString<typename std::remove_pointer<Tgt>::type>::value>::type
766 toAppendStrImpl(const T& v, Tgt result) {
767 toAppend(v, result);
768 }
769
770 template <class T, class... Ts>
771 typename std::enable_if<
772 sizeof...(Ts) >= 2 &&
773 IsSomeString<typename std::remove_pointer<
774 typename detail::LastElement<const Ts&...>::type>::type>::value>::type
775 toAppendStrImpl(const T& v, const Ts&... vs) {
776 toAppend(v, getLastElement(vs...));
777 toAppendStrImpl(vs...);
778 }
779
780 template <class Delimiter, class T, class Tgt>
781 typename std::enable_if<
782 IsSomeString<typename std::remove_pointer<Tgt>::type>::value>::type
783 toAppendDelimStrImpl(const Delimiter& /* delim */, const T& v, Tgt result) {
784 toAppend(v, result);
785 }
786
787 template <class Delimiter, class T, class... Ts>
788 typename std::enable_if<
789 sizeof...(Ts) >= 2 &&
790 IsSomeString<typename std::remove_pointer<
791 typename detail::LastElement<const Ts&...>::type>::type>::value>::type
792 toAppendDelimStrImpl(const Delimiter& delim, const T& v, const Ts&... vs) {
793 // we are really careful here, calling toAppend with just one element does
794 // not try to estimate space needed (as we already did that). If we call
795 // toAppend(v, delim, ....) we would do unnecessary size calculation
796 toAppend(v, detail::getLastElement(vs...));
797 toAppend(delim, detail::getLastElement(vs...));
798 toAppendDelimStrImpl(delim, vs...);
799 }
800 } // namespace detail
801
802 /**
803 * Variadic conversion to string. Appends each element in turn.
804 * If we have two or more things to append, we will not reserve
805 * the space for them and will depend on strings exponential growth.
806 * If you just append once consider using toAppendFit which reserves
807 * the space needed (but does not have exponential as a result).
808 *
809 * Custom implementations of toAppend() can be provided in the same namespace as
810 * the type to customize printing. estimateSpaceNeed() may also be provided to
811 * avoid reallocations in toAppendFit():
812 *
813 * namespace other_namespace {
814 *
815 * template <class String>
816 * void toAppend(const OtherType&, String* out);
817 *
818 * // optional
819 * size_t estimateSpaceNeeded(const OtherType&);
820 *
821 * }
822 */
823 template <class... Ts>
824 typename std::enable_if<
825 sizeof...(Ts) >= 3 &&
826 IsSomeString<typename std::remove_pointer<
827 typename detail::LastElement<const Ts&...>::type>::type>::value>::type
828 toAppend(const Ts&... vs) {
829 ::folly::detail::toAppendStrImpl(vs...);
830 }
831
832 #ifdef _MSC_VER
833 // Special case pid_t on MSVC, because it's a void* rather than an
834 // integral type. We can't do a global special case because this is already
835 // dangerous enough (as most pointers will implicitly convert to a void*)
836 // just doing it for MSVC.
837 template <class Tgt>
838 void toAppend(const pid_t a, Tgt* res) {
839 toAppend(uint64_t(a), res);
840 }
841 #endif
842
843 /**
844 * Special version of the call that preallocates exactly as much memory
845 * as need for arguments to be stored in target. This means we are
846 * not doing exponential growth when we append. If you are using it
847 * in a loop you are aiming at your foot with a big perf-destroying
848 * bazooka.
849 * On the other hand if you are appending to a string once, this
850 * will probably save a few calls to malloc.
851 */
852 template <class... Ts>
853 typename std::enable_if<IsSomeString<typename std::remove_pointer<
854 typename detail::LastElement<const Ts&...>::type>::type>::value>::type
855 toAppendFit(const Ts&... vs) {
856 ::folly::detail::reserveInTarget(vs...);
857 toAppend(vs...);
858 }
859
860 template <class Ts>
861 void toAppendFit(const Ts&) {}
862
863 /**
864 * Variadic base case: do nothing.
865 */
866 template <class Tgt>
867 typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(
868 Tgt* /* result */) {}
869
870 /**
871 * Variadic base case: do nothing.
872 */
873 template <class Delimiter, class Tgt>
874 typename std::enable_if<IsSomeString<Tgt>::value>::type toAppendDelim(
875 const Delimiter& /* delim */, Tgt* /* result */) {}
876
877 /**
878 * 1 element: same as toAppend.
879 */
880 template <class Delimiter, class T, class Tgt>
881 typename std::enable_if<IsSomeString<Tgt>::value>::type toAppendDelim(
882 const Delimiter& /* delim */, const T& v, Tgt* tgt) {
883 toAppend(v, tgt);
884 }
885
886 /**
887 * Append to string with a delimiter in between elements. Check out
888 * comments for toAppend for details about memory allocation.
889 */
890 template <class Delimiter, class... Ts>
891 typename std::enable_if<
892 sizeof...(Ts) >= 3 &&
893 IsSomeString<typename std::remove_pointer<
894 typename detail::LastElement<const Ts&...>::type>::type>::value>::type
895 toAppendDelim(const Delimiter& delim, const Ts&... vs) {
896 detail::toAppendDelimStrImpl(delim, vs...);
897 }
898
899 /**
900 * Detail in comment for toAppendFit
901 */
902 template <class Delimiter, class... Ts>
903 typename std::enable_if<IsSomeString<typename std::remove_pointer<
904 typename detail::LastElement<const Ts&...>::type>::type>::value>::type
905 toAppendDelimFit(const Delimiter& delim, const Ts&... vs) {
906 detail::reserveInTargetDelim(delim, vs...);
907 toAppendDelim(delim, vs...);
908 }
909
910 template <class De, class Ts>
911 void toAppendDelimFit(const De&, const Ts&) {}
912
913 /**
914 * to<SomeString>(v1, v2, ...) uses toAppend() (see below) as back-end
915 * for all types.
916 */
917 template <class Tgt, class... Ts>
918 typename std::enable_if<
919 IsSomeString<Tgt>::value &&
920 (sizeof...(Ts) != 1 ||
921 !std::is_same<Tgt, typename detail::LastElement<const Ts&...>::type>::
922 value),
923 Tgt>::type
924 to(const Ts&... vs) {
925 Tgt result;
926 toAppendFit(vs..., &result);
927 return result;
928 }
929
930 /**
931 * Special version of to<SomeString> for floating point. When calling
932 * folly::to<SomeString>(double), generic implementation above will
933 * firstly reserve 24 (or 25 when negative value) bytes. This will
934 * introduce a malloc call for most mainstream string implementations.
935 *
936 * But for most cases, a floating point doesn't need 24 (or 25) bytes to
937 * be converted as a string.
938 *
939 * This special version will not do string reserve.
940 */
941 template <class Tgt, class Src>
942 typename std::enable_if<
943 IsSomeString<Tgt>::value && std::is_floating_point<Src>::value,
944 Tgt>::type
945 to(Src value) {
946 Tgt result;
947 toAppend(value, &result);
948 return result;
949 }
950
951 /**
952 * toDelim<SomeString>(SomeString str) returns itself.
953 */
954 template <class Tgt, class Delim, class Src>
955 typename std::enable_if<
956 IsSomeString<Tgt>::value &&
957 std::is_same<Tgt, typename std::decay<Src>::type>::value,
958 Tgt>::type
959 toDelim(const Delim& /* delim */, Src&& value) {
960 return std::forward<Src>(value);
961 }
962
963 /**
964 * toDelim<SomeString>(delim, v1, v2, ...) uses toAppendDelim() as
965 * back-end for all types.
966 */
967 template <class Tgt, class Delim, class... Ts>
968 typename std::enable_if<
969 IsSomeString<Tgt>::value &&
970 (sizeof...(Ts) != 1 ||
971 !std::is_same<Tgt, typename detail::LastElement<const Ts&...>::type>::
972 value),
973 Tgt>::type
974 toDelim(const Delim& delim, const Ts&... vs) {
975 Tgt result;
976 toAppendDelimFit(delim, vs..., &result);
977 return result;
978 }
979
980 /*******************************************************************************
981 * Conversions from string types to integral types.
982 ******************************************************************************/
983
984 namespace detail {
985
986 Expected<bool, ConversionCode> str_to_bool(StringPiece* src) noexcept;
987
988 template <typename T>
989 Expected<T, ConversionCode> str_to_floating(StringPiece* src) noexcept;
990
991 extern template Expected<float, ConversionCode> str_to_floating<float>(
992 StringPiece* src) noexcept;
993 extern template Expected<double, ConversionCode> str_to_floating<double>(
994 StringPiece* src) noexcept;
995
996 template <class Tgt>
997 Expected<Tgt, ConversionCode> digits_to(const char* b, const char* e) noexcept;
998
999 extern template Expected<char, ConversionCode> digits_to<char>(
1000 const char*, const char*) noexcept;
1001 extern template Expected<signed char, ConversionCode> digits_to<signed char>(
1002 const char*, const char*) noexcept;
1003 extern template Expected<unsigned char, ConversionCode>
1004 digits_to<unsigned char>(const char*, const char*) noexcept;
1005
1006 extern template Expected<short, ConversionCode> digits_to<short>(
1007 const char*, const char*) noexcept;
1008 extern template Expected<unsigned short, ConversionCode>
1009 digits_to<unsigned short>(const char*, const char*) noexcept;
1010
1011 extern template Expected<int, ConversionCode> digits_to<int>(
1012 const char*, const char*) noexcept;
1013 extern template Expected<unsigned int, ConversionCode> digits_to<unsigned int>(
1014 const char*, const char*) noexcept;
1015
1016 extern template Expected<long, ConversionCode> digits_to<long>(
1017 const char*, const char*) noexcept;
1018 extern template Expected<unsigned long, ConversionCode>
1019 digits_to<unsigned long>(const char*, const char*) noexcept;
1020
1021 extern template Expected<long long, ConversionCode> digits_to<long long>(
1022 const char*, const char*) noexcept;
1023 extern template Expected<unsigned long long, ConversionCode>
1024 digits_to<unsigned long long>(const char*, const char*) noexcept;
1025
1026 #if FOLLY_HAVE_INT128_T
1027 extern template Expected<__int128, ConversionCode> digits_to<__int128>(
1028 const char*, const char*) noexcept;
1029 extern template Expected<unsigned __int128, ConversionCode>
1030 digits_to<unsigned __int128>(const char*, const char*) noexcept;
1031 #endif
1032
1033 template <class T>
1034 Expected<T, ConversionCode> str_to_integral(StringPiece* src) noexcept;
1035
1036 extern template Expected<char, ConversionCode> str_to_integral<char>(
1037 StringPiece* src) noexcept;
1038 extern template Expected<signed char, ConversionCode>
1039 str_to_integral<signed char>(StringPiece* src) noexcept;
1040 extern template Expected<unsigned char, ConversionCode>
1041 str_to_integral<unsigned char>(StringPiece* src) noexcept;
1042
1043 extern template Expected<short, ConversionCode> str_to_integral<short>(
1044 StringPiece* src) noexcept;
1045 extern template Expected<unsigned short, ConversionCode>
1046 str_to_integral<unsigned short>(StringPiece* src) noexcept;
1047
1048 extern template Expected<int, ConversionCode> str_to_integral<int>(
1049 StringPiece* src) noexcept;
1050 extern template Expected<unsigned int, ConversionCode>
1051 str_to_integral<unsigned int>(StringPiece* src) noexcept;
1052
1053 extern template Expected<long, ConversionCode> str_to_integral<long>(
1054 StringPiece* src) noexcept;
1055 extern template Expected<unsigned long, ConversionCode>
1056 str_to_integral<unsigned long>(StringPiece* src) noexcept;
1057
1058 extern template Expected<long long, ConversionCode> str_to_integral<long long>(
1059 StringPiece* src) noexcept;
1060 extern template Expected<unsigned long long, ConversionCode>
1061 str_to_integral<unsigned long long>(StringPiece* src) noexcept;
1062
1063 #if FOLLY_HAVE_INT128_T
1064 extern template Expected<__int128, ConversionCode> str_to_integral<__int128>(
1065 StringPiece* src) noexcept;
1066 extern template Expected<unsigned __int128, ConversionCode>
1067 str_to_integral<unsigned __int128>(StringPiece* src) noexcept;
1068 #endif
1069
1070 template <typename T>
1071 typename std::
1072 enable_if<std::is_same<T, bool>::value, Expected<T, ConversionCode>>::type
1073 convertTo(StringPiece* src) noexcept {
1074 return str_to_bool(src);
1075 }
1076
1077 template <typename T>
1078 typename std::enable_if<
1079 std::is_floating_point<T>::value,
1080 Expected<T, ConversionCode>>::type
1081 convertTo(StringPiece* src) noexcept {
1082 return str_to_floating<T>(src);
1083 }
1084
1085 template <typename T>
1086 typename std::enable_if<
1087 is_integral_v<T> && !std::is_same<T, bool>::value,
1088 Expected<T, ConversionCode>>::type
1089 convertTo(StringPiece* src) noexcept {
1090 return str_to_integral<T>(src);
1091 }
1092
1093 } // namespace detail
1094
1095 /**
1096 * String represented as a pair of pointers to char to unsigned
1097 * integrals. Assumes NO whitespace before or after.
1098 */
1099 template <typename Tgt>
1100 typename std::enable_if<
1101 is_integral_v<Tgt> && !std::is_same<Tgt, bool>::value,
1102 Expected<Tgt, ConversionCode>>::type
1103 tryTo(const char* b, const char* e) {
1104 return detail::digits_to<Tgt>(b, e);
1105 }
1106
1107 template <typename Tgt>
1108 typename std::enable_if< //
1109 is_integral_v<Tgt> && !std::is_same<Tgt, bool>::value,
1110 Tgt>::type
1111 to(const char* b, const char* e) {
1112 return tryTo<Tgt>(b, e).thenOrThrow(
1113 [](Tgt res) { return res; },
1114 [=](ConversionCode code) {
1115 return makeConversionError(code, StringPiece(b, e));
1116 });
1117 }
1118
1119 /*******************************************************************************
1120 * Conversions from string types to arithmetic types.
1121 ******************************************************************************/
1122
1123 /**
1124 * Parsing strings to numeric types.
1125 */
1126 template <typename Tgt>
1127 FOLLY_NODISCARD inline typename std::enable_if< //
1128 is_arithmetic_v<Tgt>,
1129 Expected<StringPiece, ConversionCode>>::type
1130 parseTo(StringPiece src, Tgt& out) {
1131 return detail::convertTo<Tgt>(&src).then(
1132 [&](Tgt res) { return void(out = res), src; });
1133 }
1134
1135 /*******************************************************************************
1136 * Integral / Floating Point to integral / Floating Point
1137 ******************************************************************************/
1138
1139 namespace detail {
1140
1141 /**
1142 * Bool to integral/float doesn't need any special checks, and this
1143 * overload means we aren't trying to see if a bool is less than
1144 * an integer.
1145 */
1146 template <class Tgt>
1147 typename std::enable_if<
1148 !std::is_same<Tgt, bool>::value &&
1149 (is_integral_v<Tgt> || std::is_floating_point<Tgt>::value),
1150 Expected<Tgt, ConversionCode>>::type
1151 convertTo(const bool& value) noexcept {
1152 return static_cast<Tgt>(value ? 1 : 0);
1153 }
1154
1155 /**
1156 * Checked conversion from integral to integral. The checks are only
1157 * performed when meaningful, e.g. conversion from int to long goes
1158 * unchecked.
1159 */
1160 template <class Tgt, class Src>
1161 typename std::enable_if<
1162 is_integral_v<Src> && !std::is_same<Tgt, Src>::value &&
1163 !std::is_same<Tgt, bool>::value && is_integral_v<Tgt>,
1164 Expected<Tgt, ConversionCode>>::type
1165 convertTo(const Src& value) noexcept {
1166 if /* constexpr */ (
1167 make_unsigned_t<Tgt>(std::numeric_limits<Tgt>::max()) <
1168 make_unsigned_t<Src>(std::numeric_limits<Src>::max())) {
1169 if (greater_than<Tgt, std::numeric_limits<Tgt>::max()>(value)) {
1170 return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW);
1171 }
1172 }
1173 if /* constexpr */ (
1174 is_signed_v<Src> && (!is_signed_v<Tgt> || sizeof(Src) > sizeof(Tgt))) {
1175 if (less_than<Tgt, std::numeric_limits<Tgt>::min()>(value)) {
1176 return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW);
1177 }
1178 }
1179 return static_cast<Tgt>(value);
1180 }
1181
1182 /**
1183 * Checked conversion from floating to floating. The checks are only
1184 * performed when meaningful, e.g. conversion from float to double goes
1185 * unchecked.
1186 */
1187 template <class Tgt, class Src>
1188 typename std::enable_if<
1189 std::is_floating_point<Tgt>::value && std::is_floating_point<Src>::value &&
1190 !std::is_same<Tgt, Src>::value,
1191 Expected<Tgt, ConversionCode>>::type
1192 convertTo(const Src& value) noexcept {
1193 if /* constexpr */ (
1194 std::numeric_limits<Tgt>::max() < std::numeric_limits<Src>::max()) {
1195 if (value > std::numeric_limits<Tgt>::max()) {
1196 return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW);
1197 }
1198 if (value < std::numeric_limits<Tgt>::lowest()) {
1199 return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW);
1200 }
1201 }
1202 return static_cast<Tgt>(value);
1203 }
1204
1205 /**
1206 * Check if a floating point value can safely be converted to an
1207 * integer value without triggering undefined behaviour.
1208 */
1209 template <typename Tgt, typename Src>
1210 inline typename std::enable_if<
1211 std::is_floating_point<Src>::value && is_integral_v<Tgt> &&
1212 !std::is_same<Tgt, bool>::value,
1213 bool>::type
1214 checkConversion(const Src& value) {
1215 constexpr Src tgtMaxAsSrc = static_cast<Src>(std::numeric_limits<Tgt>::max());
1216 constexpr Src tgtMinAsSrc = static_cast<Src>(std::numeric_limits<Tgt>::min());
1217 if (value >= tgtMaxAsSrc) {
1218 if (value > tgtMaxAsSrc) {
1219 return false;
1220 }
1221 const Src mmax = folly::nextafter(tgtMaxAsSrc, Src());
1222 if (static_cast<Tgt>(value - mmax) >
1223 std::numeric_limits<Tgt>::max() - static_cast<Tgt>(mmax)) {
1224 return false;
1225 }
1226 } else if (is_signed_v<Tgt> && value <= tgtMinAsSrc) {
1227 if (value < tgtMinAsSrc) {
1228 return false;
1229 }
1230 const Src mmin = folly::nextafter(tgtMinAsSrc, Src());
1231 if (static_cast<Tgt>(value - mmin) <
1232 std::numeric_limits<Tgt>::min() - static_cast<Tgt>(mmin)) {
1233 return false;
1234 }
1235 }
1236 return true;
1237 }
1238
1239 // Integers can always safely be converted to floating point values
1240 template <typename Tgt, typename Src>
1241 constexpr typename std::enable_if<
1242 is_integral_v<Src> && std::is_floating_point<Tgt>::value,
1243 bool>::type
1244 checkConversion(const Src&) {
1245 return true;
1246 }
1247
1248 // Also, floating point values can always be safely converted to bool
1249 // Per the standard, any floating point value that is not zero will yield true
1250 template <typename Tgt, typename Src>
1251 constexpr typename std::enable_if<
1252 std::is_floating_point<Src>::value && std::is_same<Tgt, bool>::value,
1253 bool>::type
1254 checkConversion(const Src&) {
1255 return true;
1256 }
1257
1258 /**
1259 * Checked conversion from integral to floating point and back. The
1260 * result must be convertible back to the source type without loss of
1261 * precision. This seems Draconian but sometimes is what's needed, and
1262 * complements existing routines nicely. For various rounding
1263 * routines, see <math>.
1264 */
1265 template <typename Tgt, typename Src>
1266 typename std::enable_if<
1267 (is_integral_v<Src> && std::is_floating_point<Tgt>::value) ||
1268 (std::is_floating_point<Src>::value && is_integral_v<Tgt>),
1269 Expected<Tgt, ConversionCode>>::type
1270 convertTo(const Src& value) noexcept {
1271 if (LIKELY(checkConversion<Tgt>(value))) {
1272 Tgt result = static_cast<Tgt>(value);
1273 if (LIKELY(checkConversion<Src>(result))) {
1274 Src witness = static_cast<Src>(result);
1275 if (LIKELY(value == witness)) {
1276 return result;
1277 }
1278 }
1279 }
1280 return makeUnexpected(ConversionCode::ARITH_LOSS_OF_PRECISION);
1281 }
1282
1283 template <typename Tgt, typename Src>
1284 inline std::string errorValue(const Src& value) {
1285 return to<std::string>("(", pretty_name<Tgt>(), ") ", value);
1286 }
1287
1288 template <typename Tgt, typename Src>
1289 using IsArithToArith = bool_constant<
1290 !std::is_same<Tgt, Src>::value && !std::is_same<Tgt, bool>::value &&
1291 is_arithmetic_v<Src> && is_arithmetic_v<Tgt>>;
1292
1293 } // namespace detail
1294
1295 template <typename Tgt, typename Src>
1296 typename std::enable_if<
1297 detail::IsArithToArith<Tgt, Src>::value,
1298 Expected<Tgt, ConversionCode>>::type
1299 tryTo(const Src& value) noexcept {
1300 return detail::convertTo<Tgt>(value);
1301 }
1302
1303 template <typename Tgt, typename Src>
1304 typename std::enable_if<detail::IsArithToArith<Tgt, Src>::value, Tgt>::type to(
1305 const Src& value) {
1306 return tryTo<Tgt>(value).thenOrThrow(
1307 [](Tgt res) { return res; },
1308 [&](ConversionCode e) {
1309 return makeConversionError(e, detail::errorValue<Tgt>(value));
1310 });
1311 }
1312
1313 /*******************************************************************************
1314 * Custom Conversions
1315 *
1316 * Any type can be used with folly::to by implementing parseTo. The
1317 * implementation should be provided in the namespace of the type to facilitate
1318 * argument-dependent lookup:
1319 *
1320 * namespace other_namespace {
1321 * ::folly::Expected<::folly::StringPiece, SomeErrorCode>
1322 * parseTo(::folly::StringPiece, OtherType&) noexcept;
1323 * }
1324 ******************************************************************************/
1325 template <class T>
1326 FOLLY_NODISCARD typename std::enable_if<
1327 std::is_enum<T>::value,
1328 Expected<StringPiece, ConversionCode>>::type
1329 parseTo(StringPiece in, T& out) noexcept {
1330 typename std::underlying_type<T>::type tmp{};
1331 auto restOrError = parseTo(in, tmp);
1332 out = static_cast<T>(tmp); // Harmless if parseTo fails
1333 return restOrError;
1334 }
1335
1336 FOLLY_NODISCARD
1337 inline Expected<StringPiece, ConversionCode> parseTo(
1338 StringPiece in, StringPiece& out) noexcept {
1339 out = in;
1340 return StringPiece{in.end(), in.end()};
1341 }
1342
1343 namespace detail {
1344
1345 template <class Str>
1346 FOLLY_ERASE Expected<StringPiece, ConversionCode> parseToStr(
1347 StringPiece in, Str& out) {
1348 out.clear();
1349 out.append(in.data(), in.size()); // TODO try/catch?
1350 return StringPiece{in.end(), in.end()};
1351 }
1352
1353 } // namespace detail
1354
1355 FOLLY_NODISCARD
1356 inline Expected<StringPiece, ConversionCode> parseTo(
1357 StringPiece in, std::string& out) {
1358 return detail::parseToStr(in, out);
1359 }
1360
1361 #if FOLLY_HAS_STRING_VIEW
1362 FOLLY_NODISCARD
1363 inline Expected<StringPiece, ConversionCode> parseTo(
1364 StringPiece in, std::string_view& out) {
1365 out = std::string_view(in.data(), in.size());
1366 return StringPiece{in.end(), in.end()};
1367 }
1368 #endif
1369
1370 FOLLY_NODISCARD
1371 inline Expected<StringPiece, ConversionCode> parseTo(
1372 StringPiece in, fbstring& out) {
1373 return detail::parseToStr(in, out);
1374 }
1375
1376 template <class Str>
1377 FOLLY_NODISCARD inline typename std::enable_if<
1378 IsSomeString<Str>::value,
1379 Expected<StringPiece, ConversionCode>>::type
1380 parseTo(StringPiece in, Str& out) {
1381 return detail::parseToStr(in, out);
1382 }
1383
1384 namespace detail {
1385 template <typename Tgt>
1386 using ParseToResult = decltype(parseTo(StringPiece{}, std::declval<Tgt&>()));
1387
1388 struct CheckTrailingSpace {
1389 Expected<Unit, ConversionCode> operator()(StringPiece sp) const {
1390 auto e = enforceWhitespaceErr(sp);
1391 if (UNLIKELY(e != ConversionCode::SUCCESS)) {
1392 return makeUnexpected(e);
1393 }
1394 return unit;
1395 }
1396 };
1397
1398 template <class Error>
1399 struct ReturnUnit {
1400 template <class T>
1401 constexpr Expected<Unit, Error> operator()(T&&) const {
1402 return unit;
1403 }
1404 };
1405
1406 // Older versions of the parseTo customization point threw on error and
1407 // returned void. Handle that.
1408 template <class Tgt>
1409 inline typename std::enable_if<
1410 std::is_void<ParseToResult<Tgt>>::value,
1411 Expected<StringPiece, ConversionCode>>::type
1412 parseToWrap(StringPiece sp, Tgt& out) {
1413 parseTo(sp, out);
1414 return StringPiece(sp.end(), sp.end());
1415 }
1416
1417 template <class Tgt>
1418 inline typename std::enable_if<
1419 !std::is_void<ParseToResult<Tgt>>::value,
1420 ParseToResult<Tgt>>::type
1421 parseToWrap(StringPiece sp, Tgt& out) {
1422 return parseTo(sp, out);
1423 }
1424
1425 template <typename Tgt>
1426 using ParseToError = ExpectedErrorType<decltype(detail::parseToWrap(
1427 StringPiece{}, std::declval<Tgt&>()))>;
1428
1429 } // namespace detail
1430
1431 /**
1432 * String or StringPiece to target conversion. Accepts leading and trailing
1433 * whitespace, but no non-space trailing characters.
1434 */
1435
1436 template <class Tgt>
1437 inline typename std::enable_if<
1438 !std::is_same<StringPiece, Tgt>::value,
1439 Expected<Tgt, detail::ParseToError<Tgt>>>::type
1440 tryTo(StringPiece src) {
1441 Tgt result{};
1442 using Error = detail::ParseToError<Tgt>;
1443 using Check = typename std::conditional<
1444 is_arithmetic_v<Tgt>,
1445 detail::CheckTrailingSpace,
1446 detail::ReturnUnit<Error>>::type;
1447 return parseTo(src, result).then(Check(), [&](Unit) {
1448 return std::move(result);
1449 });
1450 }
1451
1452 template <class Tgt, class Src>
1453 inline typename std::enable_if<
1454 IsSomeString<Src>::value && !std::is_same<StringPiece, Tgt>::value,
1455 Tgt>::type
1456 to(Src const& src) {
1457 return to<Tgt>(StringPiece(src.data(), src.size()));
1458 }
1459
1460 template <class Tgt>
1461 inline
1462 typename std::enable_if<!std::is_same<StringPiece, Tgt>::value, Tgt>::type
1463 to(StringPiece src) {
1464 Tgt result{};
1465 using Error = detail::ParseToError<Tgt>;
1466 using Check = typename std::conditional<
1467 is_arithmetic_v<Tgt>,
1468 detail::CheckTrailingSpace,
1469 detail::ReturnUnit<Error>>::type;
1470 auto tmp = detail::parseToWrap(src, result);
1471 return tmp
1472 .thenOrThrow(
1473 Check(),
1474 [&](Error e) { throw_exception(makeConversionError(e, src)); })
1475 .thenOrThrow(
1476 [&](Unit) { return std::move(result); },
1477 [&](Error e) {
1478 throw_exception(makeConversionError(e, tmp.value()));
1479 });
1480 }
1481
1482 /**
1483 * tryTo/to that take the strings by pointer so the caller gets information
1484 * about how much of the string was consumed by the conversion. These do not
1485 * check for trailing whitespace.
1486 */
1487 template <class Tgt>
1488 Expected<Tgt, detail::ParseToError<Tgt>> tryTo(StringPiece* src) {
1489 Tgt result;
1490 return parseTo(*src, result).then([&, src](StringPiece sp) -> Tgt {
1491 *src = sp;
1492 return std::move(result);
1493 });
1494 }
1495
1496 template <class Tgt>
1497 Tgt to(StringPiece* src) {
1498 Tgt result{};
1499 using Error = detail::ParseToError<Tgt>;
1500 return parseTo(*src, result)
1501 .thenOrThrow(
1502 [&, src](StringPiece sp) -> Tgt {
1503 *src = sp;
1504 return std::move(result);
1505 },
1506 [=](Error e) { return makeConversionError(e, *src); });
1507 }
1508
1509 /*******************************************************************************
1510 * Enum to anything and back
1511 ******************************************************************************/
1512
1513 template <class Tgt, class Src>
1514 typename std::enable_if<
1515 std::is_enum<Src>::value && !std::is_same<Src, Tgt>::value &&
1516 !std::is_convertible<Tgt, StringPiece>::value,
1517 Expected<Tgt, ConversionCode>>::type
1518 tryTo(const Src& value) {
1519 return tryTo<Tgt>(to_underlying(value));
1520 }
1521
1522 template <class Tgt, class Src>
1523 typename std::enable_if<
1524 !std::is_convertible<Src, StringPiece>::value && std::is_enum<Tgt>::value &&
1525 !std::is_same<Src, Tgt>::value,
1526 Expected<Tgt, ConversionCode>>::type
1527 tryTo(const Src& value) {
1528 using I = typename std::underlying_type<Tgt>::type;
1529 return tryTo<I>(value).then([](I i) { return static_cast<Tgt>(i); });
1530 }
1531
1532 template <class Tgt, class Src>
1533 typename std::enable_if<
1534 std::is_enum<Src>::value && !std::is_same<Src, Tgt>::value &&
1535 !std::is_convertible<Tgt, StringPiece>::value,
1536 Tgt>::type
1537 to(const Src& value) {
1538 return to<Tgt>(to_underlying(value));
1539 }
1540
1541 template <class Tgt, class Src>
1542 typename std::enable_if<
1543 !std::is_convertible<Src, StringPiece>::value && std::is_enum<Tgt>::value &&
1544 !std::is_same<Src, Tgt>::value,
1545 Tgt>::type
1546 to(const Src& value) {
1547 return static_cast<Tgt>(to<typename std::underlying_type<Tgt>::type>(value));
1548 }
1549
1550 } // namespace folly
1551