Boost logo

Boost Users :

Subject: [Boost-users] bug in boost::date_time::time_facet?
From: Jakub Zytka (jakub.zytka_at_[hidden])
Date: 2013-10-16 13:51:09


Hi all

I've run into a problem with *_facet from boost::date_time when
compiling for windows and I'm pretty convinced it is a bug in boost
(tested 1.49 and 1.54). However, since gcc makes a successful attempt
not to trigger it, and I'm not exactly an expert on windows
formats/linkers/etc I'd like to have second opinion before I fill a ticket.
So, here we go:

Consider following code:

------------- setFacet.h --------------------------
#include <iostream>

__declspec(dllexport) void setFacet(std::ostream &);

-------------- setFacet.cpp -----------------------
#include <boost/date_time/posix_time/posix_time.hpp>
#include <locale>
#include "setFacet.h"

__declspec(dllexport) void setFacet(std::ostream & os)
{
         boost::posix_time::time_facet * dateFormat =new
boost::posix_time::time_facet("%Y-%m");
         os.imbue(std::locale(os.getloc(), dateFormat));
}

------------------- test.cpp --------------------------
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
#include "setFacet.h"

using namespace std;

int main()
{
         std::string ts("20020131T235959");
         boost::posix_time::ptime t(boost::posix_time::from_iso_string(ts));
         cerr << t << endl;
         setFacet(cerr);
         cerr << t << endl;
         return 0;
}
-------------------------------------------------------

The expected result is that facet with "%Y-%m" format is imbued to cerr
after calling "setFacet" function and that second print uses specified
format.
Now, suppose we built it so that we have setFacet.dll exporting function
setFacet, and test.exe is linked against this dll.
In such setting the code works under linux (gcc 4.1, 4.7), but it
doesn't work under mingw-w64 4.6.4 or msvc10 - default format is used
both times:
linux:
2002-Jan-31 23:59:59
2002-01

windows:
2002-Jan-31 23:59:59
2002-Jan-31 23:59:59

Why? Well, lets look at operator<< for ptime. It does one interesting
comparison:
if (std::has_facet<custom_ptime_facet>(os.getloc()))
this comparison gives different results under linux and windows.
The key is static member "id" of a facet class. It is declared in
boost/date_time/time_facet.hpp and it is used by has_facet to give
answer whether locale has facet for the given class.
Since this header is included twice - once in dll and once in exe there
are two storages for id of the same facet class.

In gcc 4.1 appropriate symbols are weak objects. Apparently only one is
selected as the proper one by the linker and used universally.

In gcc 4.7 it is even better. Appropriate symbols are "unique global
symbol" which explicitly means there should be only one instance in
whole process.

In mingw both these storages are used independently and the same class
(boost::posix_time::time_facet) has different id in dll (in setFacet
function) and different in exe file (in operator<<). This is why
operator<< believes locale has no facet for posix_time.
At this point I was suspecting some incompatibility between windows'
linker and mingw's libstdc++ implementation (regarding std::locale::id
initialization)

However, under msvc10 it doesn't work either, with msvc library.

I believe it is a bug in boost, even if some deficiency in
mingw/msvc/windows is involved.

Also, let me confess I'm far from having learned boost docs by heart. If
there is some big red sign somewhere telling I must link statically if
using facets from boost::date_time have mercy on me.

command lines used to build example:

gcc (just get rid of these dllspecs):
g++ -fPIC -shared -o libsetFacet.so setFacet.cpp
g++ -L. -lsetFacet -Wl,--rpath=. -o test test.cpp

mingw:
x86_64-w64-mingw32-g++ -isystem pathToBoostIncludes -shared -o
libsetFacet.dll -m32 -Wl,--enable-runtime-pseudo-reloc-v2
-Wl,--out-implib,libsetFacet.lib setFacet.cpp
x86_64-w64-mingw32-g++ pathToBoostIncludes -o test.exe -m32
-Wl,--enable-runtime-pseudo-reloc-v2 -L. -lsetFacet test.cpp

msvc10:
cl.exe /I"pathToBoostIncludes" /LD set.cpp /EHsc /link /out:setFacet.dll
/LIBPATH:"pathToBoostLibs"
cl.exe /I"pathToBoostIncludes" /EHsc setFacet.lib test.cpp /link
/LIBPATH:"pathToBoostLibs"

regards,
Jakub Zytka


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net