Skip to content

Commit 3d70be2

Browse files
Merge pull request #1949 from arcaneframework/dev/gg-add-use-fromchars-for-real3
Add support to convert String to Real3 using the same mechanism as String to Real
2 parents 8658e3c + c4120a4 commit 3d70be2

File tree

2 files changed

+202
-113
lines changed

2 files changed

+202
-113
lines changed

arcane/src/arcane/utils/ValueConvert.cc

Lines changed: 161 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "arcane/utils/ValueConvert.h"
1515

1616
#include "arcane/utils/OStringStream.h"
17+
#include "arcane/utils/NotSupportedException.h"
1718
#include "arcane/utils/internal/ValueConvertInternal.h"
1819

1920
// En théorie std::from_chars() est disponible avec le C++17 mais pour
@@ -42,6 +43,31 @@ namespace
4243
return reinterpret_cast<const char*>(s.bytes().data());
4344
}
4445

46+
/*!
47+
* \brief Retourne une vue en supprimant les caratères blancs du début.
48+
*
49+
* Un caractère blanc est un caractère pour lequel std::isspace() est vrai.
50+
* \a pos indique la position dans \a s à partir de laquelle
51+
* on cherche les blancs.
52+
*/
53+
StringView _removeLeadingSpaces(StringView s, Int64 pos)
54+
{
55+
Span<const Byte> bytes = s.bytes();
56+
Int64 nb_byte = bytes.size();
57+
// Supprime les espaces potentiels
58+
for (; pos < nb_byte; ++pos) {
59+
int charv = static_cast<unsigned char>(bytes[pos]);
60+
// Visual Studio 2017 or less
61+
#if defined(_MSC_VER) && _MSC_VER <= 1916
62+
if (std::isspace(charv, std::locale()) != 0)
63+
break;
64+
#else
65+
if (!std::isspace(charv) != 0)
66+
break;
67+
#endif
68+
}
69+
return StringView(bytes.subSpan(pos, nb_byte));
70+
}
4571
} // namespace
4672

4773
/*---------------------------------------------------------------------------*/
@@ -91,113 +117,125 @@ arcaneSetUseSameValueConvertForAllReal(bool v)
91117

92118
/*---------------------------------------------------------------------------*/
93119
/*---------------------------------------------------------------------------*/
94-
95-
namespace
120+
/*!
121+
* \brief Classe pour convertir une 'StringView' en 'double'.
122+
*/
123+
class StringViewToDoubleConverter
96124
{
97-
#if defined(ARCANE_USE_FROMCHARS)
98-
/*!
99-
* \brief Converti une chaîne de caractères en un double.
100-
*
101-
* Converti \a s en un double et range la valeur dans \a v.
102-
* Il ne doit pas y avoir de caractères blancs au début de \a s.
103-
*
104-
* Le comportement de cette méthode est identique à std::strtod()
105-
* avec le locale 'C' si on est en C++20. Sinon il est identique
106-
* à std::strtod() avec le locale actuel (ce qui peut changer par exemple
107-
* le séparateur décimal). La documentation de référence est
108-
* ici: https://en.cppreference.com/w/cpp/utility/from_chars.
109-
*
110-
* \retval (-1) si la conversion a échouée.
111-
* \retval la position dans \s du dernier caratère lu plus 1.
112-
*/
113-
Int64 _getDoubleValueWithFromChars(double& v, StringView s)
114-
{
115-
// ATTENTION: il ne faut pas d'espace en début de \a s
116-
auto bytes = s.bytes();
117-
Int64 size = bytes.size();
118-
if (size == 0)
119-
// NOTE: Avec la version historique d'Arcane (avant la 3.15) il
120-
// n'y avait pas d'erreur retournée lorsqu'on converti une chaîne vide.
121-
// A priori cela n'était jamais utilisé donc cela ne pose pas de
122-
// problème de corriger ce bug.
123-
return (-1);
124-
const char* orig_data = reinterpret_cast<const char*>(bytes.data());
125-
const char* last_ptr = nullptr;
126-
std::chars_format fmt = std::chars_format::general;
127-
const char* data = orig_data;
128-
bool do_negatif = false;
129-
const bool is_verbose = global_value_convert_verbosity > 0;
130-
// std::from_chars() peut lire les valeurs au format hexadécimal
131-
// mais il ne doit pas contenir le '0x' ou '0X' du début, contrairement
132-
// à std::strtod(). On détecte ce cas et on commence la conversion
133-
// après le '0x' ou '0X'.
134-
135-
// Détecte '-0x' ou '-0X'
136-
if (size >= 3 && (bytes[0] == '-') && (bytes[1] == '0') && (bytes[2] == 'x' || bytes[2] == 'X')) {
137-
fmt = std::chars_format::hex;
138-
data += 3;
139-
do_negatif = true;
140-
}
141-
// Détecte '0x' ou '0X'
142-
else if (size >= 2 && (bytes[0] == '0') && (bytes[1] == 'x' || bytes[1] == 'X')) {
143-
fmt = std::chars_format::hex;
144-
data += 2;
145-
}
146-
// Cas général
147-
{
148-
auto [ptr, ec] = std::from_chars(data, data + size, v, fmt);
149-
last_ptr = ptr;
150-
if (is_verbose)
151-
std::cout << "FromChars:TRY GET_DOUBLE data=" << data << " v=" << v << " is_ok=" << (ec == std::errc()) << "\n";
152-
if (ec != std::errc())
153-
return (-1);
154-
}
155-
// Prend en compte le signe '-' si demandé
156-
if (do_negatif)
157-
v = -v;
158-
if (is_verbose) {
159-
char* ptr2 = nullptr;
160-
double v2 = ::strtod(orig_data, &ptr2);
161-
std::cout << "FromChars: COMPARE GET_DOUBLE via strtod v2=" << v2 << " pos=" << (ptr2 - orig_data) << "\n";
162-
}
163-
return (last_ptr - orig_data);
164-
}
165-
#endif
125+
public:
166126

167-
/*---------------------------------------------------------------------------*/
168-
/*---------------------------------------------------------------------------*/
127+
static Int64 _getDoubleValueWithFromChars(double& v, StringView s);
128+
static Int64 _getDoubleValue(double& v, StringView s);
129+
};
169130

170-
/*!
171-
* \brief Converti \a s en un double.
172-
*
173-
* Utilise std::from_chars() si \a global_use_from_chars est vrai.
174-
* Sinon, utilise strtod().
175-
*/
176-
Int64 _getDoubleValue(double& v, StringView s)
177-
{
131+
/*---------------------------------------------------------------------------*/
132+
/*---------------------------------------------------------------------------*/
133+
/*!
134+
* \brief Converti \a s en un double.
135+
*
136+
* Utilise std::from_chars() si \a global_use_from_chars est vrai.
137+
* Sinon, utilise strtod().
138+
*/
139+
Int64 StringViewToDoubleConverter::
140+
_getDoubleValue(double& v, StringView s)
141+
{
178142
#if defined(ARCANE_USE_FROMCHARS)
179-
if (global_use_from_chars) {
180-
Int64 p = _getDoubleValueWithFromChars(v, s);
181-
return p;
182-
}
143+
if (global_use_from_chars) {
144+
Int64 p = _getDoubleValueWithFromChars(v, s);
145+
return p;
146+
}
183147
#endif
184148

185-
const char* ptr = _stringViewData(s);
149+
const char* ptr = _stringViewData(s);
186150
#ifdef WIN32
187-
if (s == "infinity" || s == "inf") {
188-
v = std::numeric_limits<double>::infinity();
189-
return false;
190-
}
151+
if (s == "infinity" || s == "inf") {
152+
v = std::numeric_limits<double>::infinity();
153+
return s.size();
154+
}
191155
#endif
192-
char* ptr2 = nullptr;
156+
char* ptr2 = nullptr;
157+
if (ptr)
193158
v = ::strtod(ptr, &ptr2);
194-
return (ptr2 - ptr);
195-
}
159+
return (ptr2 - ptr);
160+
}
196161

197-
/*---------------------------------------------------------------------------*/
198-
/*---------------------------------------------------------------------------*/
162+
/*---------------------------------------------------------------------------*/
163+
/*---------------------------------------------------------------------------*/
164+
/*!
165+
* \brief Converti une chaîne de caractères en un double.
166+
*
167+
* Converti \a s en un double et range la valeur dans \a v.
168+
* Il ne doit pas y avoir de caractères blancs au début de \a s.
169+
*
170+
* Le comportement de cette méthode est identique à std::strtod()
171+
* avec le locale 'C' si on est en C++20. Sinon il est identique
172+
* à std::strtod() avec le locale actuel (ce qui peut changer par exemple
173+
* le séparateur décimal). La documentation de référence est
174+
* ici: https://en.cppreference.com/w/cpp/utility/from_chars.
175+
*
176+
* \retval (-1) si la conversion a échouée.
177+
* \retval la position dans \s du dernier caratère lu plus 1.
178+
*/
179+
Int64 StringViewToDoubleConverter::
180+
_getDoubleValueWithFromChars(double& v, StringView s)
181+
{
182+
#if defined(ARCANE_USE_FROMCHARS)
183+
// ATTENTION: il ne faut pas d'espace en début de \a s
184+
auto bytes = s.bytes();
185+
Int64 size = bytes.size();
186+
if (size == 0)
187+
// NOTE: Avec la version historique d'Arcane (avant la 3.15) il
188+
// n'y avait pas d'erreur retournée lorsqu'on converti une chaîne vide.
189+
// A priori cela n'était jamais utilisé donc cela ne pose pas de
190+
// problème de corriger ce bug.
191+
return (-1);
192+
const char* orig_data = reinterpret_cast<const char*>(bytes.data());
193+
const char* last_ptr = nullptr;
194+
std::chars_format fmt = std::chars_format::general;
195+
const char* data = orig_data;
196+
bool do_negatif = false;
197+
const bool is_verbose = global_value_convert_verbosity > 0;
198+
// std::from_chars() peut lire les valeurs au format hexadécimal
199+
// mais il ne doit pas contenir le '0x' ou '0X' du début, contrairement
200+
// à std::strtod(). On détecte ce cas et on commence la conversion
201+
// après le '0x' ou '0X'.
202+
203+
// Détecte '-0x' ou '-0X'
204+
if (size >= 3 && (bytes[0] == '-') && (bytes[1] == '0') && (bytes[2] == 'x' || bytes[2] == 'X')) {
205+
fmt = std::chars_format::hex;
206+
data += 3;
207+
do_negatif = true;
208+
}
209+
// Détecte '0x' ou '0X'
210+
else if (size >= 2 && (bytes[0] == '0') && (bytes[1] == 'x' || bytes[1] == 'X')) {
211+
fmt = std::chars_format::hex;
212+
data += 2;
213+
}
214+
// Cas général
215+
{
216+
auto [ptr, ec] = std::from_chars(data, data + size, v, fmt);
217+
last_ptr = ptr;
218+
if (is_verbose)
219+
std::cout << "FromChars:TRY GET_DOUBLE data=" << data << " v=" << v << " is_ok=" << (ec == std::errc()) << "\n";
220+
if (ec != std::errc())
221+
return (-1);
222+
}
223+
// Prend en compte le signe '-' si demandé
224+
if (do_negatif)
225+
v = -v;
226+
if (is_verbose) {
227+
char* ptr2 = nullptr;
228+
double v2 = ::strtod(orig_data, &ptr2);
229+
std::cout << "FromChars: COMPARE GET_DOUBLE via strtod v2=" << v2 << " pos=" << (ptr2 - orig_data) << "\n";
230+
}
231+
return (last_ptr - orig_data);
232+
#else
233+
ARCANE_THROW(NotSupportedException, "using std::from_chars() is not available on this platform");
234+
#endif
235+
}
199236

200-
} // namespace
237+
/*---------------------------------------------------------------------------*/
238+
/*---------------------------------------------------------------------------*/
201239

202240
/*---------------------------------------------------------------------------*/
203241
/*---------------------------------------------------------------------------*/
@@ -207,7 +245,7 @@ builtInGetValue(double& v, StringView s)
207245
{
208246
#if defined(ARCANE_USE_FROMCHARS)
209247
if (global_use_from_chars) {
210-
Int64 p = _getDoubleValueWithFromChars(v, s);
248+
Int64 p = StringViewToDoubleConverter::_getDoubleValueWithFromChars(v, s);
211249
return (p == (-1) || (p != s.size()));
212250
}
213251
#endif
@@ -339,33 +377,43 @@ builtInGetValue(Real2& v, StringView s)
339377
const bool is_verbose = global_value_convert_verbosity > 0;
340378
if (is_verbose)
341379
std::cout << "Try Read Real2: '" << s << "'\n";
342-
Int64 p = _getDoubleValue(v.x, s);
380+
Int64 p = StringViewToDoubleConverter::_getDoubleValue(v.x, s);
343381
if (p == (-1))
344382
return true;
345-
Span<const Byte> bytes = s.bytes();
346-
Int64 nb_byte = bytes.size();
347-
// Supprime les espaces potentiels
348-
for (; p < nb_byte; ++p)
349-
{
350-
#ifdef _MSC_VER <= 1916 // Visual Studio 2017 or less
351-
if (!std::isspace((char)bytes[p], std::locale()))
352-
break;
353-
#else
354-
if (!std::isspace(bytes[p]))
355-
break;
356-
#endif
357-
}
358-
s = StringView(bytes.subSpan(p, nb_byte));
383+
s = _removeLeadingSpaces(s, p);
359384
if (is_verbose)
360385
std::cout << "VX=" << v.x << " remaining_s='" << s << "'\n";
361-
p = _getDoubleValue(v.y, s);
386+
p = StringViewToDoubleConverter::_getDoubleValue(v.y, s);
362387
return (p == (-1) || (p != s.size()));
363388
}
364389
return impl::builtInGetValueGeneric(v, s);
365390
}
391+
366392
template <> ARCANE_UTILS_EXPORT bool
367393
builtInGetValue(Real3& v, StringView s)
368394
{
395+
if (global_use_same_value_convert_for_all_real) {
396+
// ATTENTION: Pour l'instant ce nouveau mécanisme ne tolère pas
397+
// les espaces en début de \a s.
398+
v = {};
399+
const bool is_verbose = global_value_convert_verbosity > 0;
400+
if (is_verbose)
401+
std::cout << "Try Read Real3: '" << s << "'\n";
402+
Int64 p = StringViewToDoubleConverter::_getDoubleValue(v.x, s);
403+
if (p == (-1) || (p == s.size()))
404+
return true;
405+
s = _removeLeadingSpaces(s, p);
406+
if (is_verbose)
407+
std::cout << "VX=" << v.x << " remaining_s='" << s << "'\n";
408+
p = StringViewToDoubleConverter::_getDoubleValue(v.y, s);
409+
if (p == (-1) || (p == s.size()))
410+
return true;
411+
s = _removeLeadingSpaces(s, p);
412+
if (is_verbose)
413+
std::cout << "VY=" << v.x << " remaining_s='" << s << "'\n";
414+
p = StringViewToDoubleConverter::_getDoubleValue(v.z, s);
415+
return (p == (-1) || (p != s.size()));
416+
}
369417
return impl::builtInGetValueGeneric(v, s);
370418
}
371419

0 commit comments

Comments
 (0)