title: imbue/facet ignored when used from shared libraries under Windows
body: <p>I had a wierd behaviour with <code>facet</code> passed to <code>std::ostream</code> through <code>imbue</code> being ignored in a very specific architecture under Android (<a href="https://stackoverflow.com/questions/67784836/imbue-facet-ignored-when-used-from-shared-libraries-depending-on-dynamic-load-or">imbue/facet ignored when used from shared libraries depending on dynamic load order under Android</a>).</p>
  <p>While trying to workaround the unresolved issue, I could reproduce the issue under Windows with a much easier architecture. And this is much more surprising to hit this issue in such a simple MCVE:</p>
  <p>I declare a shared library linking statically to boost date_time:</p>
  <p><em>bug_datetime_libwin.h:</em></p>
  <pre><code>#pragma once
  
  #ifdef BUG_DATETIME_LIBWIN_EXPORTS
      #define BUG_DATETIME_LIBWIN_API __declspec(dllexport)
  #else
      #define BUG_DATETIME_LIBWIN_API __declspec(dllimport)
  #endif
  
  #include &lt;ostream&gt;
  
  class BUG_DATETIME_LIBWIN_API BoostFacets
  {
  public:
      static void SetupStream( std::ostream&amp; str );
      static void PrintTime( std::ostream&amp; str );
  };
  </code></pre>
  <p><em>bug_datetime_libwin.cpp:</em></p>
  <pre><code>#include &quot;bug_datetime_libwin.h&quot;
  
  #ifdef _MSC_VER
  #pragma warning(push)
  #pragma warning( disable: 4005 ) // if one already included math.h, we get duplicated macro defeinition warnings
  #endif
  #include &lt;boost/date_time/posix_time/posix_time.hpp&gt;
  #ifdef _MSC_VER
  #pragma warning(pop)
  #endif
  
  #define READABLE_DATE_FORMAT &quot;%Y-%b-%d&quot;
  #define READABLE_TIME_FORMAT_ACC &quot;%H:%M:%s&quot;
  
  void BoostFacets::SetupStream( std::ostream&amp; str )
  {
      static std::string accurateFormat = std::string(READABLE_DATE_FORMAT) + &quot; &quot; + READABLE_TIME_FORMAT_ACC;
      
      boost::posix_time::time_facet* facet = new boost::posix_time::time_facet(accurateFormat.c_str());
      str.imbue(std::locale(str.getloc(), facet));
  }
  
  void BoostFacets::PrintTime( std::ostream&amp; str )
  {
      SetupStream( str );
      boost::posix_time::ptime t1(boost::gregorian::date(2002,boost::gregorian::Jan,10),
                                  boost::posix_time::time_duration(1,2,4));
      str &lt;&lt; t1;
  }
  </code></pre>
  <p>Then I have an application linking dynamically to the shared library and statically to boost date_time:</p>
  <p><em>main.cpp:</em></p>
  <pre><code>#include &lt;sstream&gt;
  #include &lt;ostream&gt;
  #include &lt;boost/date_time/posix_time/posix_time.hpp&gt;
  
  #include &quot;bug_datetime_libwin/bug_datetime_libwin.h&quot;
  
  int main( int argc, char* argv[] )
  {
      {
          std::stringstream temp;
          BoostFacets::PrintTime( temp );
  
          std::cout &lt;&lt; &quot;PrintTime: &quot; &lt;&lt; temp.str().c_str() &lt;&lt; std::endl;
      }
  
      {
          std::stringstream temp;
          BoostFacets::SetupStream( temp );
  
          boost::posix_time::ptime t1(boost::gregorian::date(2002,boost::gregorian::Jan,10),
                                      boost::posix_time::time_duration(1,2,4));
          temp &lt;&lt; t1;
  
          std::cout &lt;&lt; &quot;SetupStream: &quot; &lt;&lt; temp.str().c_str() &lt;&lt; std::endl;
      }
  
      return 0;
  }
  </code></pre>
  <p>This function outputs:</p>
  <pre><code>PrintTime: 2002-Jan-10 01:02:04.000000
  SetupStream: 2002-Jan-10 01:02:04
  </code></pre>
  <p>As you can see, if the lib does an <code>imbue</code> on a stream, creates a <code>boost::posix_time::ptime</code> object and prints it to the stream, it works fine.</p>
  <p>But if lib does an <code>imbue</code> on a stream, and the application creates a <code>boost::posix_time::ptime</code> object and prints it to the string, it does not work fine: <code>imbue</code> has no effect!</p>
  <p>What am I doing wrong?</p>
  <hr />
  <p>Update 1:</p>
  <p>Tried to compile boost as shared libraries to prevent ODR violation as proposed by Alan Birtles. It did not fix the issue. Actually, boost does not even need to be linked for this code to work as used mincludes are &quot;header-only&quot;. So the issue is not due to static boost linkage.</p>
  <hr />
  <p>Update 2:</p>
  <p>I used the debugger to see what's going on:</p>
  <p>The issue comes from:</p>
  <pre><code>template &lt;class CharT, class TraitsT&gt;
    inline
    std::basic_ostream&lt;CharT, TraitsT&gt;&amp;
    operator&lt;&lt;(std::basic_ostream&lt;CharT, TraitsT&gt;&amp; os,
               const ptime&amp; p) {
      boost::io::ios_flags_saver iflags(os);
      typedef boost::date_time::time_facet&lt;ptime, CharT&gt; custom_ptime_facet;
      std::ostreambuf_iterator&lt;CharT&gt; oitr(os);
      if (std::has_facet&lt;custom_ptime_facet&gt;(os.getloc()))
        std::use_facet&lt;custom_ptime_facet&gt;(os.getloc()).put(oitr, os, os.fill(), p);
      else {
        //instantiate a custom facet for dealing with times since the user
        //has not put one in the stream so far.  This is for efficiency 
        //since we would always need to reconstruct for every time period
        //if the locale did not already exist.  Of course this will be overridden
        //if the user imbues as some later point.
        custom_ptime_facet* f = new custom_ptime_facet();
        std::locale l = std::locale(os.getloc(), f);
        os.imbue(l);
        f-&gt;put(oitr, os, os.fill(), p);
      }
      return os;
    }
  </code></pre>
  <p>in posix_time_io.hpp.</p>
  <ul>
  <li>When main calls <code>PrintTime</code>, it calls <code>SetupStream</code>, here created <code>facet</code> id (<code>boost::posix_time::time_facet::id</code>) is <code>5</code></li>
  <li>Then when <code>SetupStream</code> calls <code>str &lt;&lt; t1</code>, <code>custom_ptime_facet</code>, in posix_time_io.hpp's <code>operator&lt;&lt;</code>, has an <code>id</code> value of <code>5</code>, so <code>has_facet</code> resturns <code>true</code> and string gets formatted.</li>
  <li>When main calls <code>SetupStream</code>, here created <code>facet</code> id (<code>boost::posix_time::time_facet::id</code>) is still <code>5</code></li>
  <li>But when <code>main</code> calls <code>str &lt;&lt; t1</code>, <code>custom_ptime_facet</code>, in posix_time_io.hpp's <code>operator&lt;&lt;</code>, has an <code>id</code> value of <code>44</code>, so <code>has_facet</code> resturns <code>false</code> and string does not get formatted.</li>
  </ul>
  <p>I suspect this is because <code>posix_time_io.hpp</code> is included twice:</p>
  <ul>
  <li>once from the library, assigning static <code>custom_ptime_facet::id</code> to <code>5</code></li>
  <li>once from the program, assigning static <code>custom_ptime_facet::id</code> to <code>44</code></li>
  </ul>
  <p>So I tested, and doing this at the end of the MCVE's main function:</p>
  <pre><code>{
      static std::string accurateFormat = std::string(READABLE_DATE_FORMAT) + &quot; &quot; + READABLE_TIME_FORMAT_ACC;
  
      std::stringstream temp;
      boost::posix_time::time_facet* facet = new boost::posix_time::time_facet(accurateFormat.c_str());
      temp.imbue(std::locale(temp.getloc(), facet));
  
      boost::posix_time::ptime t1(boost::gregorian::date(2002,boost::gregorian::Jan,10),
                                  boost::posix_time::time_duration(1,2,4));
      temp &lt;&lt; t1;
  
      std::cout &lt;&lt; &quot;main: &quot; &lt;&lt; temp.str().c_str() &lt;&lt; std::endl;
  }
  </code></pre>
  <p>outputs <code>PrintTime: 2002-Jan-10 01:02:04.000000</code>. It works because <code>facet</code> created here has an <code>id</code> of <code>44</code>, then <code>has_facet</code> returns true.</p>
  <p><strong>Does that mean that, by construction, because it uses header-only approach, boost date time won't allow one module to set <code>imbue</code> and another to use <code>operator&lt;&lt;</code>?</strong></p>