Opened 7 years ago

Closed 7 years ago

#11878 closed Bugs (wontfix)

Locale dependend number formatting doesn't work with MSVC release config

Reported by: octavian.cacina@… Owned by: Artyom Beilis
Milestone: To Be Determined Component: locale
Version: Boost 1.60.0 Severity: Problem
Keywords: Cc:

Description

I have this Problem on Windows with Visual Studio 2015 and boost 1.60. The formatting of float numbers is not working properly, when using Release configuration with dynamic runtime. The Debug configuration or the Release with static runtime work as expected. This can be quite a show stopper if one is relying on boost::locale for number formatting.

For a german locale the number must be formatted as "2,14". In Release config is formatted as "2.14"

Here is the test code and the output for Debug and Release.

#include <locale>
#include <string>
#include <iostream>

#include "boost/locale.hpp"

int main()
{
  using namespace std;

  float v = 2.14f;

  // Use std locale to format a float

  auto stdLocale = std::locale("German_germany");  
  cout.imbue(stdLocale);
  cout << "std locale " << v << endl;

  auto& stdNumPunct = std::use_facet<std::numpunct<char>>(stdLocale);
  cout << "std locale decimal_point " << stdNumPunct.decimal_point() << endl;

  cout << "std locale num_put ";
  auto& stdFacet = std::use_facet<std::num_put<char>>(stdLocale);
  stdFacet.put(cout, cout, '0', v);
  cout << endl;

  // Use boost locale to format a float

  boost::locale::generator gen;
  auto boostLocale = gen("de_DE.UTF-8");
  cout.imbue(boostLocale);

  cout << "boost locale " << boost::locale::as::number << v << endl;

  auto& boostNumPunct = std::use_facet<std::numpunct<char>>(boostLocale);
  cout << "boost locale decimal_point " << boostNumPunct.decimal_point() << endl;

  cout << "boost locale num_put ";
  auto& boostFacet = std::use_facet<std::num_put<char>>(boostLocale);
  boostFacet.put(cout, cout, '0', v);
  cout << endl;

  return 0;
}

...\Debug> LocaleNumPunct.exe
std locale 2,14
std locale decimal_point ,
std locale num_put 2,14
boost locale 2,14
boost locale decimal_point ,
boost locale num_put 2,14

...\Release>LocaleNumPunct.exe
std locale 2,14
std locale decimal_point ,
std locale num_put 2,14
boost locale 2.14
boost locale decimal_point ,
boost locale num_put 2.14

Change History (13)

comment:1 by Artyom Beilis, 7 years ago

Several things:

  1. Does both release and Debug linked to ICU or not?
  2. Does your static build is liked to static runtime or dynamic?
  3. Do you link your application to static or dynamic runtime.

Note: both boost-locale and application must have access to same runtime. Best is to use dynamic build for both runtime and boost-locale.

It is most likely your configuration/build combination issue - i.e. something compiled with something incompatible.

So please try to check exactly all your build/link options and boost-locale configuration.

in reply to:  1 comment:2 by octavian.cacina@…, 7 years ago

Replying to artyom:

  1. Does both release and Debug linked to ICU or not?

There is no ICU, just the default Windows backend.

  1. Does your static build is liked to static runtime or dynamic?

I've tried both, Release config with boost static and runtime static works. Release config with boost static and runtime dynamic (my production config) does _not_ work.

  1. Do you link your application to static or dynamic runtime.

See the previous answer.

Note: both boost-locale and application must have access to same runtime. Best is to use dynamic build for both runtime and boost-locale.

I've tested this too (boost as dll with shared runtime), I've thought would be a workaround, it does _not_ work. I have disabled the optimizations and tried to see, in the inspector window of the debugger, some diferences between the static and the dynamic build, but couldn't notice anything suspicious.

It is most likely your configuration/build combination issue - i.e. something compiled with something incompatible.

So please try to check exactly all your build/link options and boost-locale configuration.

I think I've checked and tried every combination. I have no other ideas. If I can help with any binary artifacts or other information, please let me know.

comment:3 by Artyom Beilis, 7 years ago

Release config with boost static and runtime dynamic (my production config) does _not_ work.

There maybe some confusion. If you build boost with runtime-link=static it creates static library linked to STATIC runtime. And if you use one they refer to "same" stuff in standard library that would actually be different!.

You should may use static library liked to dynamic runtime and yourself link to dynamic runtime so you both look into same DLL.

Think of this: when boost locale uses/installs facet - it uses some stuff placed in MSVC DLL and if other code does not use the dll it would miss it.

It is critical to do linking correctly.

in reply to:  3 comment:4 by octavian.cacina@…, 7 years ago

Replying to artyom:

Release config with boost static and runtime dynamic (my production config) does _not_ work.

There maybe some confusion. If you build boost with runtime-link=static it creates static library linked to STATIC runtime. And if you use one they refer to "same" stuff in standard library that would actually be different!.

You should may use static library liked to dynamic runtime and yourself link to dynamic runtime so you both look into same DLL.

Exactly, that's how I'm using it. Sorry not beeing clear enough. So this configuration would be with boost beeing compiled so:

b2 link=static threading=multi runtime-link=shared stage

The test app uses also shared runtime: Multi-threaded DLL (/MD) This app links then libboost_locale-vc140-mt-1_60.lib (so the static compiled boost lib that uses the shared runtime). So everybody does look in the same runtime dll.

comment:5 by Artyom Beilis, 7 years ago

Can you show me the exact command line you compile your sample application with.

Yes the library should be ok I just need to have FULL parameters as is (even better some bat file to copy-paste) so I can reporduce exactly the issue you report.

The problem is that there so many variables that even a single flag can mess the stuff around.

So please provide (as comands list:

  1. Command you build the boost-locale and where:
  2. List of created libraries - make sure before it is clean
  3. Command you build your sample code and where
  4. Output.

Also small question: does it happen with different version of MSVC?

There was a case when MSVC had actually fixed their code because it didn't link properly with boost-locale (they missed some dll-export/import stuff)

Just want to make sure we don't have the same case. (Can't find right now the reference)

Last edited 7 years ago by Artyom Beilis (previous) (diff)

in reply to:  5 comment:6 by octavian.cacina@…, 7 years ago

Replying to artyom:

So please provide (as comands list:

boost build

D:\LIBS\boost_1_60_0>b2 -d+2 --debug-building link=static threading=multi runtime-link=shared stage

I've used -d+2 and --debug-building to see the used compilation flags.

Extract from the boost building output

file bin.v2\libs\locale\build\msvc-14.0\release\link-static\threading-multi\win32\win_backend.obj.rsp
"libs\locale\src\win32\win_backend.cpp" -Fo"bin.v2\libs\locale\build\msvc-14.0\release\link-static\threading-multi\win32\win_backend.obj"    -TP /O2 /Ob2 /W3 /GR /MD /Zc:forScope /Zc:wchar_t /wd4675 /EHs -c 
-DBOOST_ALL_NO_LIB=1 
-DBOOST_LOCALE_NO_POSIX_BACKEND=1 
-DBOOST_SYSTEM_STATIC_LINK=1 
-DBOOST_THREAD_NO_LIB=1 
-DNDEBUG 
"-I."
compile-c-c++ bin.v2\libs\locale\build\msvc-14.0\release\link-static\threading-multi\win32\win_backend.obj

    call "C:\Users\Tavi\AppData\Local\Temp\b2_msvc_14.0_vcvarsall_x86.cmd" >nul
cl /Zm800 -nologo @"bin.v2\libs\locale\build\msvc-14.0\release\link-static\threading-multi\win32\win_backend.obj.rsp" 

...

link /lib /NOLOGO  /out:"bin.v2\libs\locale\build\msvc-14.0\release\link-static\threading-multi\libboost_locale-vc140-mt-1_60.lib" @"bin.v2\libs\locale\build\msvc-14.0\release\link-static\threading-multi\libboost_locale-vc140-mt-1_60.lib.rsp"
    
copy /b "bin.v2\libs\locale\build\msvc-14.0\release\link-static\threading-multi\libboost_locale-vc140-mt-1_60.lib" + this-file-does-not-exist-A698EE7806899E69 "stage\lib\libboost_locale-vc140-mt-1_60.lib"

For this test, I used the command line and exactly the same flags as used by boost. I've copied the generated b2_msvc_14.0_vcvarsall_x86.cmd to have the same environment: path, include and libpath.

Build the test app:

call b2_msvc_14.0_vcvarsall_x86.cmd
cl "LocaleNumPunct.cpp" /Zm800 -nologo -DNDEBUG -TP /O2 /Ob2 /W3 /GR /MD /Zc:forScope /Zc:wchar_t /wd4675 /EHs /I"D:\LIBS\boost_1_60_0" /link "libboost_locale-vc140-mt-1_60.lib" "libboost_system-vc140-mt-1_60.lib" /LIBPATH:"D:\LIBS\boost_1_60_0\stage\lib"

LocaleNumPunct.cpp

#include <locale>
#include <string>
#include <iostream>

#include "boost/locale.hpp"

int main()
{
  using namespace std;

  float v = 2.14f;

  // Use std locale to format a float

  auto stdLocale = std::locale("German_germany");  
  cout.imbue(stdLocale);
  cout << "std locale " << v << endl;

  auto& stdNumPunct = std::use_facet<std::numpunct<char>>(stdLocale);
  cout << "std locale decimal_point " << stdNumPunct.decimal_point() << endl;

  cout << "std locale num_put ";
  auto& stdFacet = std::use_facet<std::num_put<char>>(stdLocale);
  stdFacet.put(cout, cout, '0', v);
  cout << endl;

  // Use boost locale to format a float

  boost::locale::generator gen;
  auto boostLocale = gen("de_DE.UTF-8");
  cout.imbue(boostLocale);

  cout << "boost locale " << boost::locale::as::number << v << endl;

  auto& boostNumPunct = std::use_facet<std::numpunct<char>>(boostLocale);
  cout << "boost locale decimal_point " << boostNumPunct.decimal_point() << endl;

  cout << "boost locale num_put ";
  auto& boostFacet = std::use_facet<std::num_put<char>>(boostLocale);
  boostFacet.put(cout, cout, '0', v);
  cout << endl;

  return 0;
}

LocaleNumPunct.exe output:

std locale 2,14
std locale decimal_point ,
std locale num_put 2,14
boost locale 2.14
boost locale decimal_point ,
boost locale num_put 2.14

Also small question: does it happen with different version of MSVC?

Unfortunatelly, I do not have the previous MSVC at hand.

comment:7 by Artyom Beilis, 7 years ago

Ok, it works 100% from me for MSVC 10 (Don't have 15 so can't check)

For me it starts looking like MSVC bug.

Can you please add following lines to code at the end:

  cout << "boost locale num_put ";
  auto& boostFacet = std::use_facet<std::num_put<char>>(boostLocale);
  std::cout << typeid(boostFacet).name() << std::endl;
  std::cout << "Has Facet: "<< std::has_facet<boost::locale::info>(boostLocale) << std::endl;
  boostFacet.put(cout, cout, '0', v);
  cout << endl;

My output for MSVC10 is

E:\Projects\modular-boost\tmp>LocaleNumPunct.exe
std locale 2,14
std locale decimal_point ,
std locale num_put 2,14
boost locale 2,14
boost locale decimal_point ,
boost locale num_put class boost::locale::impl_win::num_format<char>
Has Facet: 1
2,14

Note the name of num_put facet and see that info is installed.

It also can be boost::locale::util::base_num_format<char> depending on backend Can you please run the modified sample.

Last edited 7 years ago by Artyom Beilis (previous) (diff)

comment:8 by Artyom Beilis, 7 years ago

One more: can you try to use std backed by adding these lines and report the result:

boost::locale::localization_backend_manager my = boost::locale::localization_backend_manager::global(); 
my.select("std"); 
boost::locale::generator gen(my);

in reply to:  7 comment:9 by octavian.cacina@…, 7 years ago

Replying to artyom:

Can you please add following lines to code at the end:

My output is:

d:\SCRATCH\LocaleNumPunct>LocaleNumPunct.exe 
std locale 2,14
std locale decimal_point ,
std locale num_put 2,14
boost locale 2.14
boost locale decimal_point ,
boost locale num_put class boost::locale::impl_win::num_format<char>
Has Facet: 1
2.14

can you try to use std backed by adding these lines and report the result

With std backend I get:

std locale 2,14
std locale decimal_point ,
std locale num_put 2,14
boost locale 2.14
boost locale decimal_point ,
boost locale num_put class boost::locale::util::base_num_format<char>
Has Facet: 1
2.14

The types seem to be ok.

in reply to:  7 comment:10 by octavian.cacina@…, 7 years ago

Replying to artyom:

For me it starts looking like MSVC bug.

You are right. I have isolated it. The dynamic release runtime ignores the numpunct facet:

#include <locale>
#include <iostream>

/* Customized numpunct facet */
class MyNumPunct : public std::numpunct<char>
{
public:
  MyNumPunct(std::size_t refs = 0) : std::numpunct<char>(refs) { }

protected:
  char do_decimal_point() const override
  {
    std::cout << " " __FUNCTION__ " ";
    return ',';
  }
};

int main()
{
  using namespace std;

  float v = 2.14f;

  // Use a modified locale
  auto myLocale = std::locale(std::locale::classic(), new MyNumPunct);

  cout.imbue(myLocale);
  cout << "stream " << v << endl;

  auto& myNumPunct = std::use_facet<std::numpunct<char>>(myLocale);
  cout << "decimal_point " << myNumPunct.decimal_point() << endl;

  cout << "num_put ";
  auto& myFacet = std::use_facet<std::num_put<char>>(myLocale);
  myFacet.put(cout, cout, '0', v);
  cout << endl;

  return 0;
}


Config Debug OK

stream  MyNumPunct::do_decimal_point 2,14
 MyNumPunct::do_decimal_point decimal_point ,
num_put  MyNumPunct::do_decimal_point 2,14

Config Release static runtime (/MT) OK

stream  MyNumPunct::do_decimal_point 2,14
 MyNumPunct::do_decimal_point decimal_point ,
num_put  MyNumPunct::do_decimal_point 2,14

Config Release dynamic runtime (/MD) BAD

stream 2.14
 MyNumPunct::do_decimal_point decimal_point ,
num_put 2.14

comment:11 by Artyom Beilis, 7 years ago

Great!!!

Thanks.

Now it is time to open a ticket for MSVC.

I'll drop the mail to Boost mailing list.

If it is ok for you I'll close the ticket.

comment:12 by octavian.cacina@…, 7 years ago

I tried to post this earlier but trac reported it as spam. Now another try.

I've reported the MSVC bug here: https://connect.microsoft.com/VisualStudio/feedback/details/2185894 You may close this ticket then. Thank you.

comment:13 by Artyom Beilis, 7 years ago

Resolution: wontfix
Status: newclosed

Closes as it is MSVC issues that need to be resolved by Microsoft.

Note: See TracTickets for help on using tickets.