29
29
#include <cstring>
30
30
#include <ctime>
31
31
32
+ /* While <charconv> is part of C++17, it can be used by earlier versions as an extension.
33
+ * This entails that it is possible some implementations lack features. Hence the
34
+ * #ifdef __cpp_lib_to_chars additional check.
35
+ */
36
+ #if defined __has_include
37
+ # if __has_include (< charconv > )
38
+ # include <charconv>
39
+ # ifdef __cpp_lib_to_chars
40
+ # define WT_CPP_LIB_TO_CHARS
41
+ # endif
42
+ # endif
43
+ #endif
44
+
32
45
#include <boost/config/warning_disable.hpp>
33
46
#include <boost/spirit/include/karma.hpp>
34
47
#include <boost/spirit/include/qi_parse.hpp>
@@ -106,6 +119,17 @@ namespace {
106
119
107
120
static inline std ::string double_to_s (const double d )
108
121
{
122
+ #ifdef WT_CPP_LIB_TO_CHARS
123
+ std ::array < char , 30 > buf ;
124
+ auto returnValue = std ::to_chars (buf .begin (), buf .end (), d );
125
+
126
+ if (returnValue .ec != std ::errc ()) {
127
+ throw std ::invalid_argument (
128
+ "double_to_s: to_chars failed for double (as to_string() '" +
129
+ std ::to_string (d ) + "'), err: " + std ::error_condition (returnValue .ec ).message ());
130
+ }
131
+ return std ::string (buf .data (), returnValue .ptr - buf .data ());
132
+ #else
109
133
char buf [30 ];
110
134
char * p = buf ;
111
135
if (d != 0 ) {
@@ -115,10 +139,22 @@ namespace {
115
139
}
116
140
* p = '\0' ;
117
141
return std ::string (buf , p );
142
+ #endif
118
143
}
119
144
120
145
static inline std ::string float_to_s (const float f )
121
146
{
147
+ #ifdef WT_CPP_LIB_TO_CHARS
148
+ std ::array < char , 30 > buf ;
149
+ auto returnValue = std ::to_chars (buf .begin (), buf .end (), f );
150
+
151
+ if (returnValue .ec != std ::errc ()) {
152
+ throw std ::invalid_argument (
153
+ "float_to_s: to_chars failed for float (as to_string() '" +
154
+ std ::to_string (f ) + "'), err: " + std ::error_condition (returnValue .ec ).message ());
155
+ }
156
+ return std ::string (buf .data (), returnValue .ptr - buf .data ());
157
+ #else
122
158
char buf [30 ];
123
159
char * p = buf ;
124
160
if (f != 0 ) {
@@ -128,6 +164,7 @@ namespace {
128
164
}
129
165
* p = '\0' ;
130
166
return std ::string (buf , p );
167
+ #endif
131
168
}
132
169
}
133
170
@@ -553,12 +590,31 @@ public:
553
590
if (PQgetisnull (result_ , row_ , column ))
554
591
return false;
555
592
593
+
594
+ #ifdef WT_CPP_LIB_TO_CHAR
595
+ std ::string result_s = PQgetvalue (result_ , row_ , column );
596
+
597
+ // try to convert with from_chars which has good round-trip properties
598
+ auto returnValue = std ::from_chars (result_s .data (), result_s .data () + result_s .size (), * value );
599
+
600
+ // fall-back to boost::spirit for "out of range", e.g. subnormals in some implementations
601
+ if (returnValue .ec == std ::errc ::result_out_of_range ) {
602
+ try {
603
+ * value = std ::stof (PQgetvalue (result_ , row_ , column ));
604
+ } catch (std ::out_of_range & ) {
605
+ * value = convert < float > ("stof" , boost ::spirit ::float_ , PQgetvalue (result_ , row_ , column ));
606
+ }
607
+ } else if (returnValue .ec != std ::errc ()) {
608
+ throw PostgresException (std ::string ("getResult: from_chars (float) of '" ) +
609
+ result_s + "' failed, err: " + std ::error_condition (returnValue .ec ).message ());
610
+ }
611
+ #else
556
612
try {
557
613
* value = std ::stof (PQgetvalue (result_ , row_ , column ));
558
614
} catch (std ::out_of_range & ) {
559
615
* value = convert < float > ("stof" , boost ::spirit ::float_ , PQgetvalue (result_ , row_ , column ));
560
616
}
561
-
617
+ #endif
562
618
LOG_DEBUG (this << " result float " << column << " " << * value );
563
619
564
620
return true;
@@ -569,12 +625,31 @@ public:
569
625
if (PQgetisnull (result_ , row_ , column ))
570
626
return false;
571
627
628
+
629
+ #ifdef WT_CPP_LIB_TO_CHAR
630
+ std ::string result_s = PQgetvalue (result_ , row_ , column );
631
+
632
+ // try to convert with from_chars which has good round-trip properties
633
+ auto returnValue = std ::from_chars (result_s .data (), result_s .data () + result_s .size (), * value );
634
+
635
+ // fall-back to boost::spirit for "out of range", e.g. subnormals in some implementations
636
+ if (retutnValue .ec == std ::errc ::result_out_of_range ) {
637
+ try {
638
+ * value = std ::stod (PQgetvalue (result_ , row_ , column ));
639
+ } catch (std ::out_of_range & ) {
640
+ * value = convert < double > ("stod" , boost ::spirit ::double_ , PQgetvalue (result_ , row_ , column ));
641
+ }
642
+ } else if (returnValue .ec != std ::errc ()) {
643
+ throw PostgresException (std ::string ("getResult: from_chars (float) of '" ) +
644
+ result_s + "' failed, err: " + std ::error_condition (returnValue .ec ).message ());
645
+ }
646
+ #else
572
647
try {
573
648
* value = std ::stod (PQgetvalue (result_ , row_ , column ));
574
649
} catch (std ::out_of_range & ) {
575
650
* value = convert < double > ("stod" , boost ::spirit ::double_ , PQgetvalue (result_ , row_ , column ));
576
651
}
577
-
652
+ #endif
578
653
LOG_DEBUG (this << " result double " << column << " " << * value );
579
654
580
655
return true;
0 commit comments