Note that there are some explanatory texts on larger screens.

plurals
  1. POConvert wide character strings to boost dates
    text
    copied!<p>I need to convert several million dates stored as wide strings into boost dates</p> <p>The following code works. However, it generates a horrible compiler warning and does not seem efficient.</p> <p>Is there a better way?</p> <pre><code>#include "boost/date_time/gregorian/gregorian.hpp" using namespace boost::gregorian; #include &lt;string&gt; using namespace std; wstring ws( L"2008/01/01" ); string temp(ws.length(), '\0'); copy(ws.begin(), ws.end(), temp.begin()); date d1( from_simple_string( temp ) ); cout &lt;&lt; d1; </code></pre> <hr> <p>The better way turns out to be to use the standard C++ library <em>locale</em>, which is a collection of <em>facets</em>. A facet is a service which allows the stream operators to handle a particular choice for date or time representation or just about anything else. All the choices about diferent things, each handled by its own facet, are gathered together in a locale.</p> <p>This solution was pointed out to me by <a href="https://stackoverflow.com/users/34509/litb">litb</a> who gave me enough help to use facets in my production code, making it terser and faster. Thanks.</p> <p>There is an <a href="http://www.cantrip.org/locale.html" rel="nofollow noreferrer">excellent tutorial</a> on locales and facets by Nathan Myers who designed facets. He has a light style which makes his tutorial easy to read, though this is advanced stuff and your brain may hurt after the first read through - mine did. I suggest you go there now. For anyone who just wants the practicalities of converting wide character strings to boost dates, the rest of this post describes the minimum necessary to make it work.</p> <hr> <p>litb first offered the following simple solution that removes the compiler warning. ( The solution was edited before I got around to accepting it. ) This looks like it does the same thing, converting wide characters one by one, but it avoids mucking around with temp strings and therefore is much clearer, I think. I really like that the compiler warning is gone.</p> <pre><code>#include "boost/date_time/gregorian/gregorian.hpp" using namespace boost::gregorian; #include &lt;string&gt; using namespace std; wstring ws( L"2008/01/01" ); date d1( from_simple_string( string( ws.begin(), ws.end() ) ); cout &lt;&lt; d1; </code></pre> <hr> <p>litb went on to suggest using "facets", which I had never heard of before. They seem to do the job, producing incredibly terse code inside the loop, at the cost of a prologue where the locale is set up.</p> <pre><code>wstring ws( L"2008/01/01" ); // construct a locale to collect all the particulars of the 'greek' style locale greek_locale; // construct a facet to handle greek dates - wide characters in 2008/Dec/31 format wdate_input_facet greek_date_facet(L"%Y/%m/%d"); // add facet to locale greek_locale = locale( greek_locale, &amp;greek_date_facet ); // construct stringstream to use greek locale std::wstringstream greek_ss; greek_ss.imbue( greek_locale ); date d2; greek_ss &lt;&lt; ws; greek_ss &gt;&gt; d2; cout &lt;&lt; d2; </code></pre> <p>This, it turns out, is also more efficient:</p> <pre><code>clock_t start, finish; double duration; start = clock(); for( int k = 0; k &lt; 100000; k++ ) { string temp(ws.length(), '\0'); copy(ws.begin(), ws.end(), temp.begin()); date d1( from_simple_string( temp ) ); } finish = clock(); duration = (double)(finish - start) / CLOCKS_PER_SEC; cout &lt;&lt; "1st method: " &lt;&lt; duration &lt;&lt; endl; start = clock(); for( int k = 0; k &lt; 100000; k++ ) { date d1( from_simple_string( string( ws.begin(), ws.end() ) ) ); } finish = clock(); duration = (double)(finish - start) / CLOCKS_PER_SEC; cout &lt;&lt; "2nd method: " &lt;&lt; duration &lt;&lt; endl; start = clock(); for( int k = 0; k &lt; 100000; k++ ) { greek_ss &lt;&lt; ws; greek_ss &gt;&gt; d2; ss.clear(); } finish = clock(); duration = (double)(finish - start) / CLOCKS_PER_SEC; cout &lt;&lt; "3rd method: " &lt;&lt; duration &lt;&lt; endl; </code></pre> <p>Produces the following output:</p> <pre> 1st method: 2.453 2nd method: 2.422 3rd method: 1.968 </pre> <p>OK, this is now in the production code and passing regression tests. It looks like this:</p> <pre><code> // .. construct greek locale and stringstream // ... loop over input extracting date strings // convert range to boost dates date d1; greek_ss&lt;&lt; sd1; greek_ss &gt;&gt; d1; if( greek_ss.fail() ) { // input is garbled wcout &lt;&lt; L"do not understand " &lt;&lt; sl &lt;&lt; endl; exit(1); } greek_ss.clear(); // finish processing and end loop </code></pre> <p>I have one final question about this. Adding the facet to the locale seems to require two invocations of the locale copy constructor</p> <pre><code> // add facet to locale greek_locale = locale( greek_locale, &amp;greek_date_facet ); </code></pre> <p>Why is there not an add( facet* ) method? ( _Addfac() is complex, undocumented and deprecated )</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload