Zoppo
asked on
STL locale problem in VisualStudio 2015 Release builds.
Hi everybody,
I'm porting some code from VS 2010 to VS 2015 and found a problem with a functionality which uses a STL stringstream to convert a double to a string using system's locale comma-/thousands-separator .
I was able to break it down to this sample program:
Does anyone yet encounter similar problems and could probably help me finding a fix or a workaround?
Thanks in advance,
best regards,
ZOPPO
Addition: I found it seems the problem happens at following code in xlocnum around Line 1409:
But for any reason my do_decimal_point isn't called ...
I'm porting some code from VS 2010 to VS 2015 and found a problem with a functionality which uses a STL stringstream to convert a double to a string using system's locale comma-/thousands-separator
I was able to break it down to this sample program:
#include <iostream>
#include <sstream>
class my_numpunct : public std::numpunct<char>
{
protected:
char do_decimal_point() const override
{
std::cout << "do_decimal_point()" << std::endl;
return localeconv()->decimal_point[0];
}
char do_thousands_sep() const override
{
std::cout << "do_thousands_sep()" << std::endl;
return localeconv()->thousands_sep[0];
}
};
std::string bar( double v )
{
std::locale loc( std::locale( "" ), new my_numpunct() );
std::stringstream ss;
std::string s;
ss.imbue( loc );
ss << v;
ss >> s;
std::cout << s << std::endl;
return s;
}
int main( int argc, char* argv[] )
{
setlocale( LC_ALL, "" );
bar( 1234.56789 );
return 0;
}
When I compile this code as Debug in VS 2015 the output is (as expected):
do_thousands_sep()
do_decimal_point()
1234,57
But if I compile it as Release in VS 2015 the output is just:
1234.56789
In VS 2010 it works correct in both Release and Debug builds.Does anyone yet encounter similar problems and could probably help me finding a fix or a workaround?
Thanks in advance,
best regards,
ZOPPO
Addition: I found it seems the problem happens at following code in xlocnum around Line 1409:
if (_Poff != _Count)
_Groupstring[_Poff] = _Punct_fac.decimal_point();
In the debugger I can see the _Punct_fac seems an instance of my class when I look at its __vptr:
*((TestCpp.exe!std::_Facet_base*)(&(*((TestCpp.exe!std::locale::facet*)(&(*((TestCpp.exe!std::numpunct<char>*)(&(*((TestCpp.exe!my_numpunct*)(&(_Punct_fac))))))))))))).__vfptr,8 0x000000013f6a8978 {TestCpp.exe!const my_numpunct::`vftable'} {0x000000013f6a2ee0 {TestCpp.exe!my_numpunct::`scalar deleting destructor'(unsigned int)}, ...} void *[0x00000008]
[0x00000000] 0x000000013f6a2ee0 {TestCpp.exe!my_numpunct::`scalar deleting destructor'(unsigned int)} void *
[0x00000001] 0x000000013f6a71fa {TestCpp.exe!std::locale::facet::_Incref(void)} void *
[0x00000002] 0x000000013f6a71f4 {TestCpp.exe!std::locale::facet::_Decref(void)} void *
[0x00000003] 0x000000013f6a4f20 {TestCpp.exe!my_numpunct::do_decimal_point(void)} void *
...
But for any reason my do_decimal_point isn't called ...
ASKER
Hi Sara,
thanks for the reply, but I fear both ways are no really a suitable solution for me because this would mean to find and change each and every place where we use such numpunct-derived classes in a similar way in our code, I guess this would mean some hundred places within more than 2 million LOC - I can only think about such a workaround when I'm 100% sure there's no other way to get it working without need to find and change all those places.
Further (and IMO this is very important) if possible I would like to know why this happens. I now debugged some hours within this functionality and still can't figure out why the virtual functions aren't called so I'm curious if this could probably be a compiler bug, because if so it would be interesting to know if other functionalities could be bogus too.
Best regards,
ZOPPO
thanks for the reply, but I fear both ways are no really a suitable solution for me because this would mean to find and change each and every place where we use such numpunct-derived classes in a similar way in our code, I guess this would mean some hundred places within more than 2 million LOC - I can only think about such a workaround when I'm 100% sure there's no other way to get it working without need to find and change all those places.
Further (and IMO this is very important) if possible I would like to know why this happens. I now debugged some hours within this functionality and still can't figure out why the virtual functions aren't called so I'm curious if this could probably be a compiler bug, because if so it would be interesting to know if other functionalities could be bogus too.
Best regards,
ZOPPO
i have noticed when i read the docs that there is also a numpunct Facet for wchar_t type and wonder whether it could be possible that the compiler could not properly detect the template type from your derived Facet class. that would explain why your function wasn't called at all in release mode. perhaps you could try to make your class a template class as well or check whether other facets are working.
at home i have downloaded visual studio 2015 community, though not installed yet. i will try to find time to test your code. i assume it is a win32 console project with multi-byte character set and precompiled header disabled?
Sara
so I'm curious if this could probably be a compiler buga wrong format of a decimal number is a bug that probably would be reported within days after a new release. even if most projects will not using a new compiler, the big companies would at least do some tests and i can't believe that such an issue could pass unnoticed for a longer period.
at home i have downloaded visual studio 2015 community, though not installed yet. i will try to find time to test your code. i assume it is a win32 console project with multi-byte character set and precompiled header disabled?
Sara
ASKER
Hm - I can't see how a 'wchar_t' type could be relevant here since all classes I use are 'char'-based, no std::wstring or std::wstringstream is used.
Anyway, if I use 'class my_numpunct : public std::numpunct<wchar_t>' the result is even wrong and the overridden functions aren't called either.
As far as I can see the main problem is there probably situations can exist where a virtual function is not called allthough it should be, it's not 'only' a wrong decimal point.
It is a simple new create Win32 console project, character set is 'Not Set', it uses precompiled headers but in 'stdafx.h' only one line exists:
#include "targetver.h"
In Release configuration I also disabled all optimization options, beside this I didn't change any settings from the defaults in both Debug and Release.
Anyway, if I use 'class my_numpunct : public std::numpunct<wchar_t>' the result is even wrong and the overridden functions aren't called either.
As far as I can see the main problem is there probably situations can exist where a virtual function is not called allthough it should be, it's not 'only' a wrong decimal point.
It is a simple new create Win32 console project, character set is 'Not Set', it uses precompiled headers but in 'stdafx.h' only one line exists:
#include "targetver.h"
In Release configuration I also disabled all optimization options, beside this I didn't change any settings from the defaults in both Debug and Release.
I can't see how a 'wchar_t' type could be relevant herei didn't meant that the compiler used the wrong Facet but perhaps wasn't able to detect the char type and then did nothing with the argument since it doesn't match any of the existing facets.
you could try
template <class chartype>
class my_numpunct : public std::numpunct<chartype>
and then pass my_numpunct<char> to the constructor. by the way, the new object never was deleted, right?
As far as I can see the main problem is there probably situations can exist where a virtual function is not called allthough it should beyou don't mean that actually, do you? a virtual table could go corrupted or get lost if you do a wrong cast, but not calling a virtual function although it was existing, is not a realistic scenario, in my opinion.
Sara
ASKER
I tried that already without any difference, the (with 'new') created my_numpunc-instance is deleted with the stringstream when it's deleted at the end of the function.
I start thinking the problem comes from within runtime library DLLs. When I change the compile settings for 'Runtime Library' from 'Multi-threaded DLL (/MD)' to 'Multi-threaded (/MT)' the problem disappears. Unfortunateley this is even not a good option for me since it would collide with other projects' settings I think.
I'll ask Microsoft Support tomorrow if they know what could be done.
Thanks anyway,
ZOPPO
PS: I leave office now, so I won't respond on further comments before tomorrow morning ...
I start thinking the problem comes from within runtime library DLLs. When I change the compile settings for 'Runtime Library' from 'Multi-threaded DLL (/MD)' to 'Multi-threaded (/MT)' the problem disappears. Unfortunateley this is even not a good option for me since it would collide with other projects' settings I think.
I'll ask Microsoft Support tomorrow if they know what could be done.
Thanks anyway,
ZOPPO
PS: I leave office now, so I won't respond on further comments before tomorrow morning ...
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hi Sara,
thanks for verifying the issue. And well, it's not really a problem about redudancy, we simply have a big amount of classes with functions like 'toString' and 'fromString', most of these functions are implemented in a similar way.
I sent a report about this to Microsoft, let's see what they say.
Have a nice day,
best regards,
ZOPPO
thanks for verifying the issue. And well, it's not really a problem about redudancy, we simply have a big amount of classes with functions like 'toString' and 'fromString', most of these functions are implemented in a similar way.
I sent a report about this to Microsoft, let's see what they say.
Have a nice day,
best regards,
ZOPPO
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I agree closing the question this way.
perhaps you could try whether the issue could be avoided with this kind of coding.
last resort might be proprietary coding like i did recently:
Open in new window
Sara