Skip to content

Commit d4e9fec

Browse files
committed
fix #523: Refactor string conversion
1 parent fabdcad commit d4e9fec

File tree

10 files changed

+513
-172
lines changed

10 files changed

+513
-172
lines changed

include/scratchcpp/value.h

Lines changed: 159 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <cassert>
1010
#include <iomanip>
1111
#include <utf8.h>
12+
#include <iostream>
1213

1314
#include "global.h"
1415

@@ -41,14 +42,28 @@ class LIBSCRATCHCPP_EXPORT Value
4142
Value(float numberValue) :
4243
m_type(Type::Double)
4344
{
44-
m_doubleValue = floatToDouble(numberValue);
45+
if (isInf(numberValue))
46+
m_type = Type::Infinity;
47+
else if (isNegativeInf(numberValue))
48+
m_type = Type::NegativeInfinity;
49+
else if (std::isnan(numberValue))
50+
m_type = Type::NaN;
51+
else
52+
m_doubleValue = floatToDouble(numberValue);
4553
}
4654

4755
/*! Constructs a number Value. */
4856
Value(double numberValue) :
4957
m_type(Type::Double)
5058
{
51-
m_doubleValue = numberValue;
59+
if (isInf(numberValue))
60+
m_type = Type::Infinity;
61+
else if (isNegativeInf(numberValue))
62+
m_type = Type::NegativeInfinity;
63+
else if (std::isnan(numberValue))
64+
m_type = Type::NaN;
65+
else
66+
m_doubleValue = numberValue;
5267
}
5368

5469
/*! Constructs a number Value. */
@@ -83,8 +98,14 @@ class LIBSCRATCHCPP_EXPORT Value
8398
Value(const std::string &stringValue) :
8499
m_type(Type::String)
85100
{
86-
new (&m_stringValue) std::string(stringValue);
87-
initString(stringValue);
101+
if (stringValue == "Infinity")
102+
m_type = Type::Infinity;
103+
else if (stringValue == "-Infinity")
104+
m_type = Type::NegativeInfinity;
105+
else if (stringValue == "NaN")
106+
m_type = Type::NaN;
107+
else
108+
new (&m_stringValue) std::string(stringValue);
88109
}
89110

90111
/*! Constructs a string Value. */
@@ -140,17 +161,81 @@ class LIBSCRATCHCPP_EXPORT Value
140161
Type type() const { return m_type; }
141162

142163
/*! Returns true if the value is infinity. */
143-
bool isInfinity() const { return m_type == Type::Infinity; }
164+
bool isInfinity() const
165+
{
166+
switch (m_type) {
167+
case Type::Infinity:
168+
return true;
169+
case Type::Integer:
170+
return isInf(m_intValue);
171+
case Type::Double:
172+
return isInf(m_doubleValue);
173+
case Type::String:
174+
return m_stringValue == "Infinity";
175+
default:
176+
return false;
177+
}
178+
}
144179

145180
/*! Returns true if the value is negative infinity. */
146-
bool isNegativeInfinity() const { return m_type == Type::NegativeInfinity; }
181+
bool isNegativeInfinity() const
182+
{
183+
switch (m_type) {
184+
case Type::NegativeInfinity:
185+
return true;
186+
case Type::Integer:
187+
return isNegativeInf(-m_intValue);
188+
case Type::Double:
189+
return isNegativeInf(-m_doubleValue);
190+
case Type::String:
191+
return m_stringValue == "-Infinity";
192+
default:
193+
return false;
194+
}
195+
}
147196

148197
/*! Returns true if the value is NaN (Not a Number). */
149-
bool isNaN() const { return m_type == Type::NaN; }
198+
bool isNaN() const
199+
{
200+
switch (m_type) {
201+
case Type::NaN:
202+
return true;
203+
case Type::Double:
204+
assert(!std::isnan(m_doubleValue));
205+
return std::isnan(m_doubleValue);
206+
case Type::String:
207+
return m_stringValue == "NaN";
208+
default:
209+
return false;
210+
}
211+
}
150212

151213
/*! Returns true if the value is a number. */
152214
bool isNumber() const { return m_type == Type::Integer || m_type == Type::Double; }
153215

216+
/*! Returns true if the value is a number or can be converted to a number. */
217+
bool isValidNumber() const
218+
{
219+
if (isNaN())
220+
return false;
221+
222+
if (isInfinity() || isNegativeInfinity())
223+
return true;
224+
225+
assert(m_type != Type::Infinity && m_type != Type::NegativeInfinity);
226+
227+
switch (m_type) {
228+
case Type::Integer:
229+
case Type::Double:
230+
case Type::Bool:
231+
return true;
232+
case Type::String:
233+
return m_stringValue.empty() || checkString(m_stringValue) > 0;
234+
default:
235+
return false;
236+
}
237+
}
238+
154239
/*! Returns true if the value is a boolean. */
155240
bool isBool() const { return m_type == Type::Bool; }
156241

@@ -163,16 +248,9 @@ class LIBSCRATCHCPP_EXPORT Value
163248
/*! Returns the long representation of the value. */
164249
long toLong() const
165250
{
166-
if (static_cast<int>(m_type) < 0) {
167-
switch (m_type) {
168-
case Type::Infinity:
169-
return std::numeric_limits<long>::infinity();
170-
case Type::NegativeInfinity:
171-
return -std::numeric_limits<long>::infinity();
172-
default:
173-
return 0;
174-
}
175-
} else {
251+
if (static_cast<int>(m_type) < 0)
252+
return 0;
253+
else {
176254
switch (m_type) {
177255
case Type::Integer:
178256
return m_intValue;
@@ -376,8 +454,17 @@ class LIBSCRATCHCPP_EXPORT Value
376454
if (m_type == Type::String)
377455
m_stringValue.~basic_string();
378456

379-
m_type = Type::Double;
380-
m_doubleValue = floatToDouble(v);
457+
if (isInf(v))
458+
m_type = Type::Infinity;
459+
else if (isNegativeInf(v))
460+
m_type = Type::NegativeInfinity;
461+
else if (std::isnan(v))
462+
m_type = Type::NaN;
463+
else {
464+
m_type = Type::Double;
465+
m_doubleValue = floatToDouble(v);
466+
}
467+
381468
return *this;
382469
}
383470

@@ -386,8 +473,16 @@ class LIBSCRATCHCPP_EXPORT Value
386473
if (m_type == Type::String)
387474
m_stringValue.~basic_string();
388475

389-
m_type = Type::Double;
390-
m_doubleValue = v;
476+
if (isInf(v))
477+
m_type = Type::Infinity;
478+
else if (isNegativeInf(v))
479+
m_type = Type::NegativeInfinity;
480+
else if (std::isnan(v))
481+
m_type = Type::NaN;
482+
else {
483+
m_type = Type::Double;
484+
m_doubleValue = v;
485+
}
391486
return *this;
392487
}
393488

@@ -423,30 +518,33 @@ class LIBSCRATCHCPP_EXPORT Value
423518

424519
const Value &operator=(const std::string &v)
425520
{
426-
if (m_type == Type::String)
427-
m_stringValue = v;
428-
else {
429-
new (&m_stringValue) std::string(v);
430-
m_type = Type::String;
431-
}
521+
if (v == "Infinity") {
522+
if (m_type == Type::String)
523+
m_stringValue.~basic_string();
432524

433-
initString(v);
434-
return *this;
435-
}
525+
m_type = Type::Infinity;
526+
} else if (v == "-Infinity") {
527+
if (m_type == Type::String)
528+
m_stringValue.~basic_string();
436529

437-
const Value &operator=(const char *v)
438-
{
439-
if (m_type == Type::String)
530+
m_type = Type::NegativeInfinity;
531+
} else if (v == "NaN") {
532+
if (m_type == Type::String)
533+
m_stringValue.~basic_string();
534+
535+
m_type = Type::NaN;
536+
} else if (m_type == Type::String)
440537
m_stringValue = v;
441538
else {
442539
new (&m_stringValue) std::string(v);
443540
m_type = Type::String;
444541
}
445542

446-
initString(v);
447543
return *this;
448544
}
449545

546+
const Value &operator=(const char *v) { return (*this = std::string(v)); }
547+
450548
const Value &operator=(const Value &v)
451549
{
452550
switch (v.m_type) {
@@ -496,59 +594,22 @@ class LIBSCRATCHCPP_EXPORT Value
496594
std::string m_stringValue;
497595
};
498596

499-
// -1 - error
500597
// 0 - is string
501598
// 1 - is long
502599
// 2 - is double
503-
static int checkString(const std::string &str, long *longValue, double *doubleValue)
600+
static int checkString(const std::string &str)
504601
{
505-
if (!longValue || !doubleValue)
506-
return -1;
507-
508602
bool ok;
509603

510604
if ((str.find_first_of('.') == std::string::npos) && (str.find_first_of('e') == std::string::npos) && (str.find_first_of('E') == std::string::npos)) {
511-
*longValue = stringToLong(str, &ok);
605+
stringToLong(str, &ok);
512606
return ok ? 1 : 0;
513607
} else {
514-
*doubleValue = stringToDouble(str, &ok);
608+
stringToDouble(str, &ok);
515609
return ok ? 2 : 0;
516610
}
517611
}
518612

519-
void initString(const std::string &str)
520-
{
521-
if (str.empty())
522-
return;
523-
else if (str == "Infinity") {
524-
m_type = Type::Infinity;
525-
return;
526-
} else if (str == "-Infinity") {
527-
m_type = Type::NegativeInfinity;
528-
return;
529-
} else if (str == "NaN") {
530-
m_type = Type::NaN;
531-
return;
532-
}
533-
534-
long l;
535-
double d;
536-
int type = checkString(str, &l, &d);
537-
538-
switch (type) {
539-
case 1:
540-
*this = l;
541-
m_type = Type::Integer;
542-
break;
543-
case 2:
544-
*this = d;
545-
m_type = Type::Double;
546-
break;
547-
default:
548-
break;
549-
}
550-
}
551-
552613
double getNumber(bool *ok = nullptr) const
553614
{
554615
// Equivalent to JavaScript Number(), *ok == false means NaN
@@ -589,8 +650,6 @@ class LIBSCRATCHCPP_EXPORT Value
589650
}
590651
}
591652

592-
void initString(const char *str) { initString(std::string(str)); }
593-
594653
Type m_type;
595654

596655
friend bool operator==(const Value &v1, const Value &v2)
@@ -922,7 +981,7 @@ class LIBSCRATCHCPP_EXPORT Value
922981
}
923982
}
924983

925-
static const std::string digits = "0123456789+-";
984+
static const std::string digits = "0123456789.eE+-";
926985

927986
for (char c : s) {
928987
if (digits.find(c) == std::string::npos) {
@@ -944,7 +1003,17 @@ class LIBSCRATCHCPP_EXPORT Value
9441003
static std::string doubleToString(double v)
9451004
{
9461005
std::stringstream stream;
947-
stream << std::setprecision(std::max(16u, digitCount(v))) << v;
1006+
1007+
if (v != 0) {
1008+
const int exponent = std::log10(std::abs(v));
1009+
1010+
if (exponent > 20)
1011+
stream << std::scientific << std::setprecision(digitCount(v / std::pow(10, exponent + 1)) - 1) << v;
1012+
else
1013+
stream << std::setprecision(std::max(16u, digitCount(v))) << v;
1014+
} else
1015+
stream << std::setprecision(std::max(16u, digitCount(v))) << v;
1016+
9481017
std::string s = stream.str();
9491018
std::size_t index;
9501019

@@ -989,6 +1058,18 @@ class LIBSCRATCHCPP_EXPORT Value
9891058

9901059
return i + j;
9911060
}
1061+
1062+
template<typename T>
1063+
static bool isInf(T v)
1064+
{
1065+
return v > 0 && std::isinf(v);
1066+
}
1067+
1068+
template<typename T>
1069+
static bool isNegativeInf(T v)
1070+
{
1071+
return v < 0 && std::isinf(v);
1072+
}
9921073
};
9931074

9941075
} // namespace libscratchcpp

0 commit comments

Comments
 (0)