Hi Robert,
I'm sorry, the problem was not the C++ locale, but the C locale.
The imbue call is right and should be there just in the case somebody
calls std::locale::global(mylocale), but I should have remembered that
the floating point numbers in the osg files are not read through C++
iostream library, but with the Field class methods.
The right place to fix this is the Field::getFloat methods, that are
using the atof C function.
The problem with atof, strtod and other C conversion functions is that
they rely in the C library locales, that are global. The power of C++
locales is that you can have a different locale setting on each stream.
You could change this:
bool Field::getFloat(float& f) const
{
getFieldType();
if (_fieldType==REAL || _fieldType==INTEGER)
{
f = (float)atof(_fieldCache);
return true;
}
else
{
return false;
}
}
With this C++ implementation:
bool Field::getFloat(float& f) const
{
getFieldType();
if (_fieldType==REAL || _fieldType==INTEGER)
{
std::istringstream stream(_fieldCache);
stream.imbue(std::locale::classic());
stream >> f;
return true;
}
else
{
return false;
}
}
And do the same for the double overloaded method.
But I've done a performance test, just to be sure that this will not
slow down OSG loading, and here are my results:
For 10.000.000 conversions:
atof: 3.73257 seconds
istringstream: 27.5036 seconds
istringstream 2: 3.4705 seconds
In the case of strtol, the C function takes 0.3 seconds while the C++
stream takes 3.2. In this case C is faster, but I don't know if it's
important to take ints into account for locale issues...
The fastests is the second istringstream approach. This one consists on
caching the stream object between conversions, and feed it with the
"thing" to convert using its str method. This way:
Field header file:
Field [...] {
std::istringstream _converter;
}
Field sources:
void Field::_init()
{
[...]
_converter.imbue(std::locale::classic());
}
bool Field::getFloat(float& f) const
{
getFieldType();
if (_fieldType==REAL || _fieldType==INTEGER)
{
_converter.str(_fieldCache);
_converter >> f; // Faster than atof, and locale-friendly
return true;
}
else
{
return false;
}
}
I attach a modified version of Field class with everything ported to
iostreams. Use it only as a proof of concept, I could not test it at all
(lack of time, sorry), I only ensured that it compiles.
Regards.
El lun, 21-05-2007 a las 07:48 +0100, Robert Osfield escribió:
> Hi Ruben,
>
> I've added imbue(std::locale::classic()); to the read and write
> sections of ReaderWriterOSG.cpp. Could you do a svn update and let me
> know how you get on.
>
> Cheers,
> Robert.
>
> On 5/20/07, Ruben <[EMAIL PROTECTED]> wrote:
> > I think that I got it!
> >
> > The problem is that the ReaderWriterOSG assumes that the locale on the
> > system is the default one, where the decimal separator is a point. My
> > app changes the C++ locale to the "local" one, that in Spain uses the
> > comma as decimal separator. So every point is read as an int, ignoring
> > the decimals...
> >
> > The openflight loader solves this by setting the desired locale in its
> > ifstream:
> >
> > fin.imbue(std::locale::classic());
> >
> > Where fin is the ifstream, of course :)
> >
> > Could this be added to the ReaderWriterOSG also, please?
> >
> > In the meaintime I'm using a dirty hack, I'm changing the C locale
> > before doing osgDB::readNodeFile and restoring it after.
> >
> > Thanks.
> >
> >
> > > Hi,
> > >
> > > I am having a very strange problem. When I start my app, if I load the
> > > cow.osg it is loaded completely. But after the program renders a bit, if
> > > I load the cow.osg again, I get the cow voxelized. I attach an image
> > > that shows this strange behaviour. It is a composition of the solid and
> > > the wireframe to show what is happening.
> > >
> > > My app is not doing anything related to levels of detail, and the only
> > > thing that is's doing about osgDB is loading and saving with
> > > readNodeFile and writeNodeFile.
> > > osgDB::Registry::instance()->getOptions() returns NULL at any of the two
> > > reads.
> > >
> > > To ensure that it is not something that I am doing in my app, I did
> > > this:
> > > osg::Node *colliders_real = osgDB::readNodeFile("cow.osg");
> > > osgDB::writeNodeFile(*colliders_real, "crash.osg");
> > >
> > > And the image that I attach is an osgviewer over crash.osg
> > >
> > > Any hint of what may be going wrong?
> > >
> > > I'm using OSG 1.9.3.
> > >
> > > _______________________________________________
> > > osg-users mailing list
> > > [email protected]
> > > http://openscenegraph.net/mailman/listinfo/osg-users
> > > http://www.openscenegraph.org/
> > _______________________________________________
> > osg-users mailing list
> > [email protected]
> > http://openscenegraph.net/mailman/listinfo/osg-users
> > http://www.openscenegraph.org/
> >
> _______________________________________________
> osg-users mailing list
> [email protected]
> http://openscenegraph.net/mailman/listinfo/osg-users
> http://www.openscenegraph.org/
>
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#ifndef OSGDB_FIELD
#define OSGDB_FIELD 1
#include <osgDB/Export>
#include <sstream>
#include <string>
#include <stdlib.h>
namespace osgDB {
class OSGDB_EXPORT Field
{
public:
enum {
MIN_CACHE_SIZE = 256
};
Field();
Field(const Field& field);
virtual ~Field();
virtual Field& operator = (const Field& ic);
void reset();
void addChar(char c);
int getNoCharacters() const { return _fieldCacheSize; }
void setWithinQuotes(bool withinQuotes=true);
bool getWithinQuotes();
void setNoNestedBrackets(int no);
int getNoNestedBrackets();
enum FieldType
{
OPEN_BRACKET,
CLOSE_BRACKET,
STRING,
WORD,
REAL,
INTEGER,
BLANK,
UNINITIALISED
};
FieldType getFieldType() const;
bool isValid() const;
bool isOpenBracket() const;
bool isCloseBracket() const;
bool isWord() const;
bool matchWord(const char* str) const;
bool matchWord(const char* str,int noCharacters) const;
bool isString() const;
bool matchString(const char* str) const;
bool matchString(const char* str,int noCharacters) const;
bool isQuotedString() const;
const char* getStr() const;
char* takeStr();
bool isInt() const;
bool matchInt(int i) const;
bool getInt(int& i) const;
bool isUInt() const;
bool matchUInt(unsigned int i) const;
bool getUInt(unsigned int& i) const;
bool isFloat() const;
bool matchFloat(float f) const;
bool getFloat(float& f) const;
bool getFloat(double& f) const;
static FieldType calculateFieldType(const char* str,bool withinQuotes=false);
protected:
void _init();
void _free();
void _copy(const Field& ic);
int _fieldCacheCapacity;
int _fieldCacheSize;
char* _fieldCache;
mutable FieldType _fieldType;
bool _withinQuotes;
int _noNestedBrackets;
mutable std::istringstream _converter;
};
}
#endif // __SG_FIELD_H
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#include <locale>
#include <osgDB/Field>
using namespace osgDB;
using namespace std;
Field::Field()
{
_init();
}
Field::Field(const Field& ic)
{
_copy(ic);
}
Field::~Field()
{
_free();
}
Field& Field::operator = (const Field& ic)
{
if (this==&ic) return *this;
_free();
_copy(ic);
return *this;
}
void Field::_free()
{
// free all data
if (_fieldCache) delete [] _fieldCache;
_init();
}
void Field::_init()
{
_fieldCacheCapacity = 256;
_fieldCacheSize = 0;
_fieldCache = NULL;
_fieldType = UNINITIALISED;
_withinQuotes = false;
_noNestedBrackets = 0;
_converter.imbue(std::locale::classic());
}
void Field::_copy(const Field& ic)
{
// copy string cache.
if (ic._fieldCache)
{
_fieldCacheCapacity = ic._fieldCacheCapacity;
_fieldCacheSize = ic._fieldCacheSize;
_fieldCache = new char [_fieldCacheCapacity];
strncpy(_fieldCache,ic._fieldCache,_fieldCacheCapacity);
}
else
{
_fieldCacheCapacity = 0;
_fieldCacheSize = 0;
_fieldCache = NULL;
}
_fieldType = ic._fieldType;
_withinQuotes = ic._withinQuotes;
_noNestedBrackets = ic._noNestedBrackets;
}
void Field::setWithinQuotes(bool withinQuotes)
{
_withinQuotes=withinQuotes;
_fieldType = UNINITIALISED;
}
bool Field::getWithinQuotes()
{
return _withinQuotes;
}
void Field::setNoNestedBrackets(int no)
{
_noNestedBrackets=no;
}
int Field::getNoNestedBrackets()
{
return _noNestedBrackets;
}
const char* Field::getStr() const
{
if (_fieldCacheSize!=0) return _fieldCache;
else return NULL;
}
char* Field::takeStr()
{
char* field = _fieldCache;
_fieldCache = NULL;
_fieldCacheSize = 0;
_fieldType = UNINITIALISED;
_withinQuotes = false;
return field;
}
void Field::reset()
{
_fieldCacheSize = 0;
if (_fieldCache)
{
_fieldCache[_fieldCacheSize] = 0;
}
_withinQuotes = false;
_noNestedBrackets = 0;
}
void Field::addChar(char c)
{
if (_fieldCache==NULL)
{
if (_fieldCacheCapacity<MIN_CACHE_SIZE) _fieldCacheCapacity=MIN_CACHE_SIZE;
_fieldCache = new char[_fieldCacheCapacity];
memset(_fieldCache,0,_fieldCacheCapacity);
_fieldCacheSize = 0;
}
else if (_fieldCacheSize>=_fieldCacheCapacity-1)
{
if (_fieldCacheCapacity<MIN_CACHE_SIZE) _fieldCacheCapacity=MIN_CACHE_SIZE;
while (_fieldCacheSize>=_fieldCacheCapacity-1) _fieldCacheCapacity *= 2;
char* tmp_str = _fieldCache;
_fieldCache = new char[_fieldCacheCapacity];
memset(_fieldCache,0,_fieldCacheCapacity);
strncpy(_fieldCache,tmp_str,_fieldCacheSize);
delete [] tmp_str;
}
_fieldCache[_fieldCacheSize++] = c;
_fieldCache[_fieldCacheSize] = 0;
_fieldType = UNINITIALISED;
}
Field::FieldType Field::getFieldType() const
{
if (_fieldType==UNINITIALISED && _fieldCache)
{
_fieldType = calculateFieldType(_fieldCache,_withinQuotes);
}
return _fieldType;
}
bool Field::isValid() const
{
if (_fieldCacheSize>0 && !_withinQuotes) return true;
else return false;
}
bool Field::isOpenBracket() const
{
if (_fieldCacheSize==1) return _fieldCache[0]=='{';
else return false;
}
bool Field::isCloseBracket() const
{
if (_fieldCacheSize==1) return _fieldCache[0]=='}';
else return false;
}
bool Field::isWord() const
{
getFieldType();
return (_fieldType==WORD);
}
bool Field::matchWord(const char* str) const
{
getFieldType();
return _fieldType==WORD && strcmp(_fieldCache,str)==0;
}
bool Field::matchWord(const char* str,int noCharacters) const
{
getFieldType();
return _fieldType==WORD && strncmp(_fieldCache,str,noCharacters)==0;
}
bool Field::isString() const
{
return getNoCharacters()!=0;
}
bool Field::matchString(const char* str) const
{
return strcmp(_fieldCache,str)==0;
}
bool Field::matchString(const char* str,int noCharacters) const
{
return strncmp(_fieldCache,str,noCharacters)==0;
}
bool Field::isQuotedString() const
{
return _withinQuotes;
}
bool Field::isInt() const
{
getFieldType();
return _fieldType==INTEGER;
}
bool Field::matchInt(int i) const
{
getFieldType();
if (_fieldType==INTEGER)
{
int tmp;
_converter.str(_fieldCache);
_converter >> tmp;
return tmp==i;
}
else
{
return false;
}
}
bool Field::getInt(int& i) const
{
getFieldType();
if (_fieldType==INTEGER)
{
_converter.str(_fieldCache);
_converter >> i;
return true;
}
else
{
return false;
}
}
bool Field::isUInt() const
{
getFieldType();
return _fieldType==INTEGER;
}
bool Field::matchUInt(unsigned int i) const
{
getFieldType();
if (_fieldType==INTEGER)
{
unsigned int tmp;
_converter.str(_fieldCache);
_converter >> tmp;
return tmp==i;
}
else
{
return false;
}
}
bool Field::getUInt(unsigned int& i) const
{
getFieldType();
if (_fieldType==INTEGER)
{
_converter.str(_fieldCache);
_converter >> i;
return true;
}
else
{
return false;
}
}
bool Field::isFloat() const
{
getFieldType();
return _fieldType==REAL || _fieldType==INTEGER;
}
bool Field::matchFloat(float f) const
{
getFieldType();
if (_fieldType==REAL || _fieldType==INTEGER)
{
float tmp;
_converter.str(_fieldCache);
_converter >> tmp;
return tmp == f;
}
else
{
return false;
}
}
bool Field::getFloat(float& f) const
{
getFieldType();
if (_fieldType==REAL || _fieldType==INTEGER)
{
_converter.str(_fieldCache);
_converter >> f;
return true;
}
else
{
return false;
}
}
bool Field::getFloat(double& f) const
{
getFieldType();
if (_fieldType==REAL || _fieldType==INTEGER)
{
_converter.str(_fieldCache);
_converter >> f;
return true;
}
else
{
return false;
}
}
Field::FieldType Field::calculateFieldType(const char* str,bool withinQuotes)
{
if (str==NULL) return BLANK;
if (*str==0) return BLANK;
if (withinQuotes) return STRING;
bool hadPlusMinus = false;
bool hadDecimalPlace = false;
bool hadExponent = false;
bool couldBeInt = true;
bool couldBeFloat = true;
int noZeroToNine = 0;
const char* ptr = str;
// check if could be a hex number.
if (strncmp(ptr,"0x",2)==0)
{
// skip over leading 0x, and then go through rest of string
// checking to make sure all values are 0...9 or a..f.
ptr+=2;
while (
*ptr!=0 &&
((*ptr>='0' && *ptr<='9') ||
(*ptr>='a' && *ptr<='f') ||
(*ptr>='A' && *ptr<='F'))
)
{
++ptr;
}
// got to end of string without failure, therefore must be a hex integer.
if (*ptr==0) return INTEGER;
}
ptr = str;
// check if a float or an int.
while (*ptr!=0 && couldBeFloat)
{
if (*ptr=='+' || *ptr=='-')
{
if (hadPlusMinus)
{
couldBeInt = false;
couldBeFloat = false;
} else hadPlusMinus = true;
}
else if (*ptr>='0' && *ptr<='9')
{
noZeroToNine++;
}
else if (*ptr=='.')
{
if (hadDecimalPlace)
{
couldBeInt = false;
couldBeFloat = false;
}
else
{
hadDecimalPlace = true;
couldBeInt = false;
}
}
else if (*ptr=='e' || *ptr=='E')
{
if (hadExponent || noZeroToNine==0)
{
couldBeInt = false;
couldBeFloat = false;
}
else
{
hadExponent = true;
couldBeInt = false;
hadDecimalPlace = false;
hadPlusMinus = false;
noZeroToNine=0;
}
}
else
{
couldBeInt = false;
couldBeFloat = false;
}
++ptr;
}
if (couldBeInt && noZeroToNine>0) return INTEGER;
if (couldBeFloat && noZeroToNine>0) return REAL;
if (str[0]=='{') return OPEN_BRACKET;
if (str[0]=='}') return CLOSE_BRACKET;
return WORD;
}
_______________________________________________
osg-users mailing list
[email protected]
http://openscenegraph.net/mailman/listinfo/osg-users
http://www.openscenegraph.org/