Skip to content

Commit 2da6f54

Browse files
committed
Make real number parsing and serializing locale-independent, as suggested by Ian Clevy.
1 parent 5f677ba commit 2da6f54

File tree

2 files changed

+52
-4
lines changed

2 files changed

+52
-4
lines changed

src/ifcparse/IfcParse.cpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,27 @@
3838

3939
using namespace IfcParse;
4040

41+
// A static locale for the real number parser. strtod() is locale-dependent, causing issues
42+
// in locales that have ',' as a decimal separator. Therefore the non standard _strtod_l() /
43+
// strtod_l() is used and a reference to the "C" locale is obtained here. The alternative is
44+
// to use std::istringstream::imbue(std::locale::classic()), but there are subtleties in
45+
// parsing in MSVC2010 and it appears to be much slower.
46+
#ifdef _MSC_VER
47+
static _locale_t locale = (_locale_t) 0;
48+
void init_locale() {
49+
if (locale == (_locale_t) 0) {
50+
locale = _create_locale(LC_NUMERIC, "C");
51+
}
52+
}
53+
#else
54+
static locale_t locale = (locale_t) 0;
55+
void init_locale() {
56+
if (locale == (_locale_t) 0) {
57+
locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
58+
}
59+
}
60+
#endif
61+
4162
//
4263
// Opens the file, gets the filesize and reads a chunk in memory
4364
//
@@ -333,22 +354,28 @@ Token IfcParse::TokenPtr() { return Token((IfcSpfLexer*)0,0); }
333354
bool TokenFunc::startsWith(const Token& t, char c) {
334355
return t.first->stream->Read(t.second) == c;
335356
}
357+
336358
bool TokenFunc::isOperator(const Token& t, char op) {
337359
return (!t.first) && (!op || op == t.second);
338360
}
361+
339362
bool TokenFunc::isIdentifier(const Token& t) {
340363
return ! isOperator(t) && startsWith(t, '#');
341364
}
365+
342366
bool TokenFunc::isString(const Token& t) {
343367
return ! isOperator(t) && startsWith(t, '\'');
344368
}
369+
345370
bool TokenFunc::isEnumeration(const Token& t) {
346371
return ! isOperator(t) && startsWith(t, '.');
347372
}
373+
348374
bool TokenFunc::isKeyword(const Token& t) {
349375
// bool is a subtype of enumeration, no need to test for that
350376
return !isOperator(t) && !isIdentifier(t) && !isString(t) && !isEnumeration(t) && !isInt(t) && !isFloat(t);
351377
}
378+
352379
bool TokenFunc::isInt(const Token& t) {
353380
if (isOperator(t)) return false;
354381
const std::string str = asString(t);
@@ -357,19 +384,26 @@ bool TokenFunc::isInt(const Token& t) {
357384
long result = strtol(start,&end,10);
358385
return ((end - start) == str.length());
359386
}
387+
360388
bool TokenFunc::isBool(const Token& t) {
361389
if (!isEnumeration(t)) return false;
362390
const std::string str = asString(t);
363391
return str == "T" || str == "F";
364392
}
393+
365394
bool TokenFunc::isFloat(const Token& t) {
366395
if (isOperator(t)) return false;
367396
const std::string str = asString(t);
368397
const char* start = str.c_str();
369398
char* end;
370-
double result = strtod(start,&end);
399+
#ifdef _MSC_VER
400+
double result = _strtod_l(start,&end,locale);
401+
#else
402+
double result = strtod_l(start,&end,locale);
403+
#endif
371404
return ((end - start) == str.length());
372405
}
406+
373407
int TokenFunc::asInt(const Token& t) {
374408
const std::string str = asString(t);
375409
// In case of an ENTITY_INSTANCE_NAME skip the leading #
@@ -379,24 +413,32 @@ int TokenFunc::asInt(const Token& t) {
379413
if ( start == end ) throw IfcException("Token is not an integer or identifier");
380414
return (int) result;
381415
}
416+
382417
bool TokenFunc::asBool(const Token& t) {
383418
const std::string str = asString(t);
384419
return str == "T";
385420
}
421+
386422
double TokenFunc::asFloat(const Token& t) {
387423
const std::string str = asString(t);
388424
const char* start = str.c_str();
389425
char* end;
390-
double result = strtod(start,&end);
426+
#ifdef _MSC_VER
427+
double result = _strtod_l(start,&end,locale);
428+
#else
429+
double result = strtod_l(start,&end,locale);
430+
#endif
391431
if ( start == end ) throw IfcException("Token is not a real");
392432
return result;
393433
}
434+
394435
std::string TokenFunc::asString(const Token& t) {
395436
if ( isOperator(t,'$') ) return "";
396437
else if ( isOperator(t) ) throw IfcException("Token is not a string");
397438
std::string str = t.first->TokenString(t.second);
398439
return isString(t) || isEnumeration(t) ? str.substr(1,str.size()-2) : str;
399440
}
441+
400442
std::string TokenFunc::toString(const Token& t) {
401443
if ( isOperator(t) ) return std::string ( (char*) &t.second , 1 );
402444
else return t.first->TokenString(t.second);
@@ -769,6 +811,10 @@ bool IfcFile::Init(void* data, int len) {
769811
return IfcFile::Init(new IfcSpfStream(data,len));
770812
}
771813
bool IfcFile::Init(IfcParse::IfcSpfStream* s) {
814+
// Initialize a "C" locale for locale-independent
815+
// number parsing. See comment above on line 41.
816+
init_locale();
817+
772818
stream = s;
773819
if (!stream->valid) {
774820
return false;

src/ifcparse/IfcWrite.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
* *
1818
********************************************************************************/
1919

20-
#include <iomanip>
20+
#include <iomanip>
21+
#include <locale>
2122

2223
#include "../ifcparse/IfcParse.h"
2324
#include "../ifcparse/IfcWrite.h"
@@ -204,7 +205,8 @@ class StringBuilderVisitor : public boost::static_visitor<void> {
204205
// REAL = [ SIGN ] DIGIT { DIGIT } "." { DIGIT } [ "E" [ SIGN ] DIGIT { DIGIT } ] .
205206
std::string format_double(const double& d) {
206207
std::ostringstream oss;
207-
oss << std::setprecision(16) << d;
208+
oss.imbue(std::locale::classic());
209+
oss << std::setprecision(15) << d;
208210
const std::string str = oss.str();
209211
oss.str("");
210212
std::string::size_type e = str.find('e');

0 commit comments

Comments
 (0)