2007-08-10 Travis Vitek <[EMAIL PROTECTED]>
* 22.locale.money.put.mt.cpp(MyIos, MyStreambuf, MyMoneyData)
Added structures to simplify testing.
(run_test): Build table of in/outputs for verification in test
threads.
(thread_func): Assert that data written matches expected.
(main): Add
------------------------------------------------------------------------
Index: 22.locale.money.put.mt.cpp
===================================================================
--- 22.locale.money.put.mt.cpp (revision 564268)
+++ 22.locale.money.put.mt.cpp (working copy)
@@ -30,15 +30,17 @@
#include <iterator> // for ostreambuf_iterator
#include <locale> // for locale, money_put
-#include <cstring> // for strlen()
+#include <cstring> // for strlen ()
#include <rw_locale.h>
#include <rw_thread.h>
-#include <driver.h>
+#include <driver.h> // for rw_assert ()
+#include <valcmp.h> // for rw_strncmp ()
// maximum number of threads allowed by the command line interface
-#define MAX_THREADS 32
+#define MAX_THREADS 32
+#define MAX_LOOPS 100000
// default number of threads (will be adjusted to the number
// of processors/cores later)
@@ -48,6 +50,13 @@
// otherwise on the command line)
int rw_opt_nloops = 100000;
+// number of locales to use
+int rw_opt_nlocales = MAX_THREADS;
+
+// should all threads share the same set of locale objects instead
+// of creating their own?
+int rw_opt_shared_locale;
+
/**************************************************************************/
// array of locale names to use for testing
@@ -60,97 +69,146 @@
/**************************************************************************/
-extern "C" {
+static const char n_money_vals[][20] = {
+ "1", "12", "123", "1234", "12345", "123456", "1234567", "12345678",
+ "-9", "-98", "-987", "-9876", "-98765", "-987654", "-9876543",
+ "1.9", "-12.89", "123.789", "-1234.6789", "-12345.56789"
+};
-bool test_char; // exercise money_put<char>
-bool test_wchar; // exercise money_put<wchar_t>
+#ifndef _RWSTD_NO_WCHAR_T
+static const wchar_t w_money_vals[][20] = {
+ L"1", L"12", L"123", L"1234", L"12345", L"123456", L"1234567",
+ L"-9", L"-98", L"-987", L"-9876", L"-98765", L"-987654", L"-9876543",
+ L"1.9", L"-12.89", L"123.789", L"-1234.6789", L"-12345.56789"
+};
-static void*
-thread_func (void*)
+#endif // _RWSTD_NO_WCHAR_T
+
+//
+struct MyMoneyData
{
- // dummy streambuf-derived object the doesn't do anything
- // but allows ostreambuf_iterator to "think" it can write
- // to it
- struct NarrowBuf: std::streambuf {
- int_type overflow (int_type c) { return c; }
- } sb;
+ enum { BufferSize = 16 };
- const char str_vals[][20] = {
- "1", "12", "123", "1234", "12345", "123456", "1234567", "12345678",
- "-9", "-98", "-987", "-9876", "-98765", "-987654", "-9876543",
- "1.9", "-12.89", "123.789", "-1234.6789", "-12345.56789"
- };
+ // name of the locale the data corresponds to
+ const char* locale_name_;
+ // optionally set to the named locale for threads to share
+ std::locale locale_;
+
+ // international or domestic format flag
+ bool intl_;
+
+ // the time struct used to generate strings below
+ long double money_value_;
+
+ // index into string array [n,w]_money_vals
+ unsigned n_money_index_;
+ unsigned w_money_index_;
+
+ //
+ struct Source {
+
+ // narrow representations of money_
+ char ncs_ [BufferSize];
+
#ifndef _RWSTD_NO_WCHAR_T
- struct WideBuf: std::wstreambuf {
- int_type overflow (int_type c) { return c; }
- } wb;
+ // wide representations of money_
+ wchar_t wcs_ [BufferSize];
- const wchar_t wstr_vals[][20] = {
- L"1", L"12", L"123", L"1234", L"12345", L"123456", L"1234567",
- L"-9", L"-98", L"-987", L"-9876", L"-98765", L"-987654", L"-9876543",
- L"1.9", L"-12.89", L"123.789", L"-1234.6789", L"-12345.56789"
- };
+#endif // _RWSTD_NO_WCHAR_T
-#endif // _RWSTD_NO_WCHAR_T
+ } numeric_, string_;
- struct Ios: std::ios {
- Ios () { this->init (0); }
- } io;
+} my_money_data [MAX_THREADS];
- for (int i = 0; i != rw_opt_nloops; ++i) {
- // save the name of the locale
- const char* const locale_name = locales [i % nlocales];
+template <class charT, class Traits>
+struct MyIos: std::basic_ios<charT, Traits>
+{
+ MyIos () {
+ this->init (0);
+ }
+};
- // construct a named locale and imbue it in the ios object
- // so that the locale is used not only by the money_put facet
- // but also by the numpunct facet
- const std::locale loc (locale_name);
- io.imbue (loc);
- enum PutId {
- put_ldbl,
- put_string,
- put_max
- };
+template <class charT, class Traits>
+struct MyStreambuf: std::basic_streambuf<charT, Traits>
+{
+ typedef std::basic_streambuf<charT, Traits> Base;
- io.width (i % 16);
+ MyStreambuf ()
+ : Base () {
+ }
- // exercise postive and negative values
- long double ldval = i & 1 ? -i : i;
+ void pubsetp (charT *pbeg, std::streamsize n) {
+ this->setp (pbeg, pbeg + n);
+ }
+};
- // add some random fractional digits
- if (i & 2)
- ldval += ldval / 3.14;
- // exercise domestic formats every other iteration
- // and international formats the rest
- const bool intl = 0 == (i & 1);
+#define countof(x) (sizeof (x) / sizeof (*x))
+extern "C" {
+
+bool test_char; // exercise money_put<char>
+bool test_wchar; // exercise money_put<wchar_t>
+
+
+static void*
+thread_func (void*)
+{
+ char ncs [MyMoneyData::BufferSize];
+ MyIos<char, std::char_traits<char> > nio;
+ MyStreambuf<char, std::char_traits<char> > nsb;
+
+#ifndef _RWSTD_NO_WCHAR_T
+ wchar_t wcs [MyMoneyData::BufferSize];
+ MyIos<wchar_t, std::char_traits<wchar_t> > wio;
+ MyStreambuf<wchar_t, std::char_traits<wchar_t> > wsb;
+#endif // _RWSTD_NO_WCHAR_T
+
+ for (int i = 0; i != rw_opt_nloops; ++i) {
+
+ // save the name of the locale
+ const MyMoneyData& data = my_money_data [i % nlocales];
+
+ // construct a named locale, get a reference to the money_put
+ // facet from it and use it to format a random money value
+ const std::locale loc =
+ rw_opt_shared_locale ? data.locale_
+ : std::locale (data.locale_name_);
+
if (test_char) {
// exercise the narrow char specialization of the facet
- const std::money_put<char> &mp =
+ const std::money_put<char> &np =
std::use_facet<std::money_put<char> >(loc);
- const std::ostreambuf_iterator<char> iter (&sb);
+ nio.imbue (loc);
- switch (i % put_max) {
- case put_ldbl:
- mp.put (iter, intl, io, ' ', ldval);
- break;
+ // verify the conversion from double
+ nsb.pubsetp (ncs, countof (ncs));
+ nio.rdbuf (&nsb);
- case put_string: {
- const char* const strval =
- str_vals [i % sizeof str_vals / sizeof *str_vals];
+ *np.put (std::ostreambuf_iterator<char>(&nsb),
+ data.intl_, nio, ' ', data.money_value_) = char();
- mp.put (iter, intl, io, ' ', strval);
- break;
- }
- }
+ RW_ASSERT (!nio.fail ());
+ RW_ASSERT (!rw_strncmp(ncs, data.numeric_.ncs_));
+
+ // verify the conversion from string
+ nsb.pubsetp (ncs, countof (ncs));
+ nio.rdbuf (&nsb);
+
+ *np.put (std::ostreambuf_iterator<char>(&nsb),
+ data.intl_, nio, ' ',
+ n_money_vals [data.n_money_index_]) = char();
+
+ RW_ASSERT (!nio.fail ());
+ RW_ASSERT (!rw_strncmp(ncs, data.string_.ncs_));
+
}
// both specializations may be tested at the same time
@@ -160,25 +218,32 @@
#ifndef _RWSTD_NO_WCHAR_T
- const std::money_put<wchar_t> &mp =
+ const std::money_put<wchar_t> &wp =
std::use_facet<std::money_put<wchar_t> >(loc);
- const std::ostreambuf_iterator<wchar_t> iter (&wb);
+ wio.imbue (loc);
- switch (i % put_max) {
- case put_ldbl:
- mp.put (iter, intl, io, ' ', ldval);
- break;
+ // verify the conversion from double
+ wsb.pubsetp (wcs, countof (wcs));
+ wio.rdbuf (&wsb);
- case put_string: {
- const wchar_t* const strval =
- wstr_vals [i % sizeof wstr_vals / sizeof *wstr_vals];
+ *wp.put (std::ostreambuf_iterator<wchar_t>(&wsb),
+ data.intl_, wio, ' ', data.money_value_) = wchar_t();
+
+ RW_ASSERT (!wio.fail ());
+ RW_ASSERT (!rw_strncmp(wcs, data.numeric_.wcs_));
- mp.put (iter, intl, io, ' ', strval);
- break;
- }
- }
+ // verify the conversion from string
+ wsb.pubsetp (wcs, countof (wcs));
+ wio.rdbuf (&wsb);
+ *wp.put (std::ostreambuf_iterator<wchar_t>(&wsb),
+ data.intl_, wio, ' ',
+ w_money_vals [data.w_money_index_]) = wchar_t();
+
+ RW_ASSERT (!wio.fail ());
+ RW_ASSERT (!rw_strncmp(wcs, data.string_.wcs_));
+
#endif // _RWSTD_NO_WCHAR_T
}
@@ -194,19 +259,152 @@
static int
run_test (int, char**)
{
- // find all installed locales for which setlocale(LC_ALL) succeeds
+ MyIos<char, std::char_traits<char> > nio;
+ MyStreambuf<char, std::char_traits<char> > nsb;
+
+#ifndef _RWSTD_NO_WCHAR_T
+ MyIos<wchar_t, std::char_traits<wchar_t> > wio;
+ MyStreambuf<wchar_t, std::char_traits<wchar_t> > wsb;
+#endif // _RWSTD_NO_WCHAR_T
+
+ // find all installed locales for which setlocale (LC_ALL) succeeds
const char* const locale_list =
rw_opt_locales ? rw_opt_locales : rw_locales (_RWSTD_LC_ALL);
const std::size_t maxinx = sizeof locales / sizeof *locales;
- for (const char *name = locale_list; *name; name += std::strlen (name) +1) {
- locales [nlocales++] = name;
+ const char* const possible_locale_options[] = {
+ locale_list, "C\0", 0
+ };
- if (nlocales == maxinx)
- break;
+ for (int p = 0; possible_locale_options[p]; ++p) {
+
+ for (const char* name = locale_list;
+ *name;
+ name += std::strlen (name) + 1) {
+
+ const std::size_t inx = nlocales;
+ locales [inx] = name;
+
+ // fill in the money and results for this locale
+ MyMoneyData& data = my_money_data [inx];
+ data.locale_name_ = name;
+
+ try {
+ const std::locale loc (data.locale_name_);
+
+ // initialize with random but valid values
+
+ data.money_value_ = inx;
+ data.n_money_index_ = inx % countof (n_money_vals);
+ data.w_money_index_ = inx % countof (w_money_vals);
+
+ // exercise domestic formats every other iteration
+ // and international formats the rest
+ data.intl_ = 0 == (inx & 1);
+
+ // exercise postive and negative values
+ if (inx & 1)
+ data.money_value_ *= -1.;
+
+ // add some random fractional digits
+ if (inx & 2)
+ data.money_value_ += data.money_value_ / 3.14;
+
+ const std::money_put<char> &np =
+ std::use_facet<std::money_put<char> >(loc);
+
+ nio.imbue (loc);
+
+ // write money from double
+ nsb.pubsetp (data.numeric_.ncs_,
+ countof (data.numeric_.ncs_));
+ nio.rdbuf (&nsb);
+
+ *np.put (std::ostreambuf_iterator<char>(&nsb),
+ data.intl_, nio, ' ', data.money_value_) = char();
+
+ rw_assert (!nio.fail (), __FILE__, __LINE__,
+ "money_put<char>::put(..., %d, ..., %g) failed "
+ "for locale(%#s)",
+ data.intl_, data.money_value_, data.locale_name_);
+
+ // write money from string
+ nsb.pubsetp (data.string_.ncs_,
+ countof (data.string_.ncs_));
+ nio.rdbuf (&nsb);
+
+ *np.put (std::ostreambuf_iterator<char>(&nsb),
+ data.intl_, nio, ' ',
+ n_money_vals [data.n_money_index_]) = char();
+
+ rw_assert (!nio.fail (), __FILE__, __LINE__,
+ "money_put<char>::put(..., %d, ..., '%s') "
+ "failed for locale(%#s)",
+ data.intl_, n_money_vals [data.n_money_index_],
+ data.locale_name_);
+
+#ifndef _RWSTD_NO_WCHAR_T
+
+ const std::money_put<wchar_t> &wp =
+ std::use_facet<std::money_put<wchar_t> >(loc);
+
+ wio.imbue (loc);
+
+ // write money from double
+ wsb.pubsetp (data.numeric_.wcs_,
+ countof (data.numeric_.wcs_));
+ wio.rdbuf (&wsb);
+
+ *wp.put (std::ostreambuf_iterator<wchar_t>(&wsb),
+ data.intl_, wio, L' ', data.money_value_) = wchar_t();
+
+ rw_assert (!wio.fail (), __FILE__, __LINE__,
+ "money_put<wchar_t>::put(..., %d, ..., %g) "
+ "failed for locale(%#s)",
+ data.intl_, data.money_value_, data.locale_name_);
+
+ // write money from string
+ wsb.pubsetp (data.string_.wcs_,
+ countof (data.string_.wcs_));
+ wio.rdbuf (&wsb);
+
+ *wp.put (std::ostreambuf_iterator<wchar_t>(&wsb),
+ data.intl_, wio, L' ',
+ w_money_vals [data.w_money_index_]) = wchar_t();
+
+ rw_assert (!wio.fail (), __FILE__, __LINE__,
+ "money_put<wchar_t>::put(..., %d, ..., '%s') "
+ "failed for locale(%#s)",
+ data.intl_, w_money_vals [data.w_money_index_],
+ data.locale_name_);
+
+#endif // _RWSTD_NO_WCHAR_T
+
+ if (rw_opt_shared_locale)
+ data.locale_ = loc;
+
+ nlocales += 1;
+
+ }
+ catch (...) {
+ rw_warn (!rw_opt_locales, 0, __LINE__,
+ "failed to create locale(%#s)", name);
+ }
+
+ if (nlocales == maxinx || nlocales == std::size_t
(rw_opt_nlocales))
+ break;
+ }
+
+ if (nlocales != 0) {
+ break; // found at least one locale
+ }
}
+ // avoid divide by zero in thread if there are no locales to test
+ rw_fatal (nlocales != 0, 0, __LINE__,
+ "failed to create one or more usable locales!");
+
rw_info (0, 0, 0,
"testing std::money_put<charT> with %d thread%{?}s%{;}, "
"%zu iteration%{?}s%{;} each, in locales { %{ [EMAIL PROTECTED]
}",
@@ -220,7 +418,7 @@
test_wchar = false;
// create and start a pool of threads and wait for them to finish
- int result =
+ int result = 0;
rw_thread_pool (0, std::size_t (rw_opt_nthreads), 0, thread_func, 0);
rw_error (result == 0, 0, __LINE__,
@@ -242,7 +440,7 @@
"rw_thread_pool(0, %d, 0, %{#f}, 0) failed",
rw_opt_nthreads, thread_func);
- // exercise bothe the char and the wchar_t specializations
+ // exercise both the char and the wchar_t specializations
// at the same time
rw_info (0, 0, 0,
@@ -282,11 +480,15 @@
return rw_test (argc, argv, __FILE__,
"lib.locale.money.put",
"thread safety", run_test,
- "|-nloops#0 " // must be non-negative
- "|-nthreads#0-* " // must be in [0, MAX_THREADS]
- "|-locales=", // must be provided
+ "|-nloops#0 " // must be non-negative
+ "|-nthreads#0-* " // must be in [0, MAX_THREADS]
+ "|-nlocales#0 " // arg must be non-negative
+ "|-locales= " // must be provided
+ "|-shared-locale# ",
&rw_opt_nloops,
int (MAX_THREADS),
&rw_opt_nthreads,
- &rw_opt_setlocales);
+ &rw_opt_nlocales,
+ &rw_opt_setlocales,
+ &rw_opt_shared_locale);
}