diff mbox

[1/3] libstdc++: Add time_get::get support.

Message ID 7500311.ZoLkc9LrmZ@descartes
State New
Headers show

Commit Message

Rüdiger Sonderfeld April 15, 2014, 9:19 p.m. UTC
This patch adds support for std::time_get::get and std::time_get::do_get
[locale.time.get].  Currently do_get is not virtual because this caused
segfault errors for me.

* libstdc++-v3/include/bits/locale_facets_nonio.h (time_get::get):
  (time_get::do_get):  New method (C++11).
* libstdc++-v3/include/bits/locale_facets_nonio.tcc (time_get::get):
  (time_get::do_get):  New method (C++11).
* libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc: New file.
* libstdc++-v3/testsuite/22_locale/time_get/get/char/2.cc: New file.
---
 libstdc++-v3/include/bits/locale_facets_nonio.h    |  73 ++++++++++++
 libstdc++-v3/include/bits/locale_facets_nonio.tcc  | 102 ++++++++++++++++
 .../testsuite/22_locale/time_get/get/char/1.cc     | 129 ++++++++++++++++++++
 .../testsuite/22_locale/time_get/get/char/2.cc     | 103 ++++++++++++++++
 .../testsuite/22_locale/time_get/get/wchar_t/1.cc  | 130 +++++++++++++++++++++
 .../testsuite/22_locale/time_get/get/wchar_t/2.cc  | 104 +++++++++++++++++
 6 files changed, 641 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc
 create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/char/2.cc
 create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/1.cc
 create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/2.cc

Comments

Paolo Carlini April 15, 2014, 9:36 p.m. UTC | #1
Hi,

On 04/15/2014 11:19 PM, Rüdiger Sonderfeld wrote:
> This patch adds support for std::time_get::get and std::time_get::do_get
> [locale.time.get].  Currently do_get is not virtual because this caused
> segfault errors for me.
Those should be isolated and a compiler bug report opened including a 
minimized reproducer.

Anyway, the real issue is indeed that implementing those bits requires a 
new virtual function, and that would break the ABI. We do have a bug in 
Bugzilla tracking the issue.

Thanks!
Paolo.
Marc Glisse April 16, 2014, 5:58 a.m. UTC | #2
On Tue, 15 Apr 2014, Paolo Carlini wrote:

> Anyway, the real issue is indeed that implementing those bits requires a new 
> virtual function, and that would break the ABI.

What is the status of the ABI half-break plan (abi_tag and such), 
necessary to get the remaining pieces of C++11?
Rüdiger Sonderfeld April 16, 2014, 11:19 a.m. UTC | #3
On Tuesday 15 April 2014 23:36:51 Paolo Carlini wrote:
> Those should be isolated and a compiler bug report opened including a
> minimized reproducer.

I'm not sure if this is a compiler bug or simply due to the fact that I didn't 
add the virtual function to the ABI linker script.

> Anyway, the real issue is indeed that implementing those bits requires a
> new virtual function, and that would break the ABI. We do have a bug in
> Bugzilla tracking the issue.

But now that 4.9 has branched isn't it acceptable to extent the ABI?

Regards,
Rüdiger
Jonathan Wakely April 16, 2014, 12:21 p.m. UTC | #4
On 16/04/14 13:19 +0200, Rüdiger Sonderfeld wrote:
>On Tuesday 15 April 2014 23:36:51 Paolo Carlini wrote:
>> Those should be isolated and a compiler bug report opened including a
>> minimized reproducer.
>
>I'm not sure if this is a compiler bug or simply due to the fact that I didn't
>add the virtual function to the ABI linker script.
>
>> Anyway, the real issue is indeed that implementing those bits requires a
>> new virtual function, and that would break the ABI. We do have a bug in
>> Bugzilla tracking the issue.
>
>But now that 4.9 has branched isn't it acceptable to extent the ABI?

Adding new virtual functions does not "extend" the ABI, it makes it
incompatible. We either need to change the library's SONAME (not
acceptable to some GNU/Linux dsitributions) or mangle the type
differently so it is a new type for linkage purposes (which is what we
plan to do using the abi_tag attribute).
Jonathan Wakely Dec. 20, 2014, 1:07 p.m. UTC | #5
On 15/04/14 23:19 +0200, Rüdiger Sonderfeld wrote:
>This patch adds support for std::time_get::get and std::time_get::do_get
>[locale.time.get].  Currently do_get is not virtual because this caused
>segfault errors for me.

Hi Rüdiger, I'm trying to apply this patch and the tests don't work.

There also seem to be some problems in the new functions.

>* libstdc++-v3/include/bits/locale_facets_nonio.h (time_get::get):
>  (time_get::do_get):  New method (C++11).
>* libstdc++-v3/include/bits/locale_facets_nonio.tcc (time_get::get):
>  (time_get::do_get):  New method (C++11).
>* libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc: New file.
>* libstdc++-v3/testsuite/22_locale/time_get/get/char/2.cc: New file.
>---
> libstdc++-v3/include/bits/locale_facets_nonio.h    |  73 ++++++++++++
> libstdc++-v3/include/bits/locale_facets_nonio.tcc  | 102 ++++++++++++++++
> .../testsuite/22_locale/time_get/get/char/1.cc     | 129 ++++++++++++++++++++
> .../testsuite/22_locale/time_get/get/char/2.cc     | 103 ++++++++++++++++
> .../testsuite/22_locale/time_get/get/wchar_t/1.cc  | 130 +++++++++++++++++++++
> .../testsuite/22_locale/time_get/get/wchar_t/2.cc  | 104 +++++++++++++++++
> 6 files changed, 641 insertions(+)
> create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc
> create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/char/2.cc
> create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/1.cc
> create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/2.cc
>
>diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.tcc b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
>index 41d944d..93466b6 100644
>--- a/libstdc++-v3/include/bits/locale_facets_nonio.tcc
>+++ b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
>@@ -1137,6 +1137,108 @@ _GLIBCXX_END_NAMESPACE_LDBL
>       return __beg;
>     }
>
>+#if __cplusplus >= 201103L
>+  template<typename _CharT, typename _InIter>
>+    inline
>+    _InIter
>+    time_get<_CharT, _InIter>::
>+    get(iter_type __s, iter_type __end, ios_base& __io,
>+        ios_base::iostate& __err, tm* __tm, const char_type *__fmt,
>+        const char_type *__fmtend) const
>+    {
>+      const locale& __loc = __io._M_getloc();
>+      ctype<_CharT> const& __ctype = use_facet<ctype<_CharT> >(__loc);
>+      __err = ios_base::goodbit;
>+      while (__fmt != __fmtend &&
>+             __err == ios_base::goodbit)
>+        {
>+          if (__s == __end)
>+            {
>+              __err = ios_base::eofbit | ios_base::failbit;
>+              break;
>+            }
>+          else if (__ctype.narrow(*__fmt, 0) == '%')
>+            {
>+              char __format;
>+              char __mod = 0;
>+              if (++__fmt == __fmtend)
>+                {
>+                  __err = ios_base::failbit;
>+                  break;
>+                }
>+              const char __c = __ctype.narrow(*__fmt, 0);
>+              if (__c != 'E' && __c != 'O')
>+                __format = __c;
>+              else if (++__fmt != __fmtend)
>+                {
>+                  __mod = __c;
>+                  __format = __ctype.narrow(*__fmt, 0);
>+                }
>+              else
>+                {
>+                  __err = ios_base::failbit;
>+                  break;
>+                }
>+              __s = this->do_get(__s, __end, __io, __err, __tm, __format, __mod);
>+              ++__fmt;
>+            }
>+          else if (__ctype.is(ctype_base::space, *__fmt))
>+            {
>+              ++__fmt;
>+              while (__fmt != __fmtend &&
>+                     __ctype.is(ctype_base::space, *__fmt))
>+                ++__fmt;
>+
>+              while (__s != __end &&
>+                     __ctype.is(ctype_base::space, *__s))
>+                ++__s;
>+            }
>+          // TODO real case-insensitive comparison
>+          else if (__ctype.tolower(*__s) == __ctype.tolower(*__fmt) ||
>+                   __ctype.toupper(*__s) == __ctype.toupper(*__fmt))
>+            {
>+              ++__s;
>+              ++__fmt;
>+            }
>+          else
>+            {
>+              __err = ios_base::failbit;
>+              break;
>+            }
>+        }
>+      return __s;
>+    }
>+
>+  template<typename _CharT, typename _InIter>
>+    inline
>+    _InIter
>+    time_get<_CharT, _InIter>::
>+    do_get(iter_type __beg, iter_type __end, ios_base& __io,
>+           ios_base::iostate& __err, tm* __tm,
>+           char __format, char __mod) const
>+    {
>+      const locale& __loc = __io._M_getloc();
>+      ctype<_CharT> const& __ctype = use_facet<ctype<_CharT> >(__loc);

Shouldn't this set err = goodbit before doing anything?

  "Effects: The function starts by evaluating err = ios_base::goodbit."

>+      char_type __fmt[4];
>+      __fmt[0] = __ctype.widen('%');
>+      if (!__mod)
>+        {
>+          __fmt[1] = __format;
>+          __fmt[2] = char_type();
>+        }
>+      else
>+        {
>+          __fmt[1] = __mod;
>+          __fmt[2] = __format;
>+          __fmt[3] = char_type();
>+        }
>+
>+      return _M_extract_via_format(__beg, __end, __io, __err, __tm, __fmt);

It also doesn't seem to meet this requirement:

  "When s == end evaluates to true after reading a character the
  function evaluates err |= ios_base::eofbit.

>+    }
>+
>+#endif // __cplusplus >= 201103L
>+
>   template<typename _CharT, typename _OutIter>
>     _OutIter
>     time_put<_CharT, _OutIter>::
>diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc
>new file mode 100644
>index 0000000..1ad7822
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc
>@@ -0,0 +1,129 @@
>+// { dg-options " -std=gnu++11 " }
>+
>+// 2014-04-14 Rüdiger Sonderfeld  <ruediger@c-plusplus.de>
>+
>+// Copyright (C) 2014 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library.  This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+// GNU General Public License for more details.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3.  If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// 22.4.5.1.1 (C++11) time_get members [locale.time.get.members]
>+
>+#include <locale>
>+#include <sstream>
>+#include <iterator>
>+#include <testsuite_hooks.h>
>+
>+#ifndef _GLIBCXX_ASSERT
>+#  include <iostream>
>+#  define PRINT(x) cout << #x << ": " << x << endl
>+#  define TESTHEAD(x) cout << x << endl
>+#else
>+#  define PRINT(x) do {} while(false)
>+#  define TESTHEAD(x) do {} while(false)
>+#endif
>+
>+void test01()
>+{
>+  using namespace std;
>+  bool test __attribute__((unused)) = true;
>+
>+  locale loc_c = locale::classic();
>+
>+  istringstream iss;
>+  iss.imbue(loc_c);
>+  const time_get<char>& tget = use_facet<time_get<char>>(iss.getloc());
>+  const istreambuf_iterator<char> end;
>+
>+  tm time;
>+  ios_base::iostate err;
>+
>+  // check regular operations with format string
>+  TESTHEAD("regular operations");
>+  iss.str("d 2014-04-14 01:09:35");
>+  string format = "d %Y-%m-%d %H:%M:%S";
>+  auto ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
>+                      format.data(), format.data()+format.size());
>+  PRINT(err);
>+  VERIFY(err == ios_base::goodbit);

libc++ sets eofbit here.

>+  VERIFY(ret == end);
>+  PRINT(time.tm_year);
>+  VERIFY(time.tm_year == 114);
>+  PRINT(time.tm_mon);
>+  VERIFY(time.tm_mon == 3);
>+  PRINT(time.tm_mday);
>+  VERIFY(time.tm_mday == 14);
>+  PRINT(time.tm_hour);
>+  VERIFY(time.tm_hour == 1);
>+  PRINT(time.tm_min);
>+  VERIFY(time.tm_min == 9);
>+  PRINT(time.tm_sec);
>+  VERIFY(time.tm_sec == 35);
>+
>+  TESTHEAD("check eof");
>+  iss.str("2020");
>+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
>+                 format.data(), format.data()+format.size());
>+  VERIFY(err == ios_base::eofbit | ios_base::failbit);
>+  VERIFY(ret == end);

How can this test pass?

Is there a format = "%Y" missing?

You cannot parse "2020" using the format "d %Y-%m-%d %H:%M:%S" so it
should not reach EOF and should not set ret = end.


>+
>+  TESTHEAD("check broken format");
>+  iss.str("2014-04-14 01:09:35");
>+  format = "%";
>+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
>+                 format.data(), format.data()+format.size());
>+  VERIFY(err == ios_base::failbit);
>+
>+  TESTHEAD("check single format letter version");
>+  iss.str("2020");
>+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time, 'Y');
>+  VERIFY(err == ios_base::goodbit);
>+  VERIFY(time.tm_year == 120);
>+  VERIFY(ret == end);

Shouldn't this set err == eofbit too?

The 1.cc tests seem like they can never pass.
Jonathan Wakely Dec. 20, 2014, 1:43 p.m. UTC | #6
On 20/12/14 13:07 +0000, Jonathan Wakely wrote:
>On 15/04/14 23:19 +0200, Rüdiger Sonderfeld wrote:
>>This patch adds support for std::time_get::get and std::time_get::do_get
>>[locale.time.get].  Currently do_get is not virtual because this caused
>>segfault errors for me.
>
>Hi Rüdiger, I'm trying to apply this patch and the tests don't work.
>
>There also seem to be some problems in the new functions.
>
>>* libstdc++-v3/include/bits/locale_facets_nonio.h (time_get::get):
>> (time_get::do_get):  New method (C++11).
>>* libstdc++-v3/include/bits/locale_facets_nonio.tcc (time_get::get):
>> (time_get::do_get):  New method (C++11).
>>* libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc: New file.
>>* libstdc++-v3/testsuite/22_locale/time_get/get/char/2.cc: New file.
>>---
>>libstdc++-v3/include/bits/locale_facets_nonio.h    |  73 ++++++++++++
>>libstdc++-v3/include/bits/locale_facets_nonio.tcc  | 102 ++++++++++++++++
>>.../testsuite/22_locale/time_get/get/char/1.cc     | 129 ++++++++++++++++++++
>>.../testsuite/22_locale/time_get/get/char/2.cc     | 103 ++++++++++++++++
>>.../testsuite/22_locale/time_get/get/wchar_t/1.cc  | 130 +++++++++++++++++++++
>>.../testsuite/22_locale/time_get/get/wchar_t/2.cc  | 104 +++++++++++++++++
>>6 files changed, 641 insertions(+)
>>create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc
>>create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/char/2.cc
>>create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/1.cc
>>create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/2.cc
>>
>>diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.tcc b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
>>index 41d944d..93466b6 100644
>>--- a/libstdc++-v3/include/bits/locale_facets_nonio.tcc
>>+++ b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
>>@@ -1137,6 +1137,108 @@ _GLIBCXX_END_NAMESPACE_LDBL
>>      return __beg;
>>    }
>>
>>+#if __cplusplus >= 201103L
>>+  template<typename _CharT, typename _InIter>
>>+    inline
>>+    _InIter
>>+    time_get<_CharT, _InIter>::
>>+    get(iter_type __s, iter_type __end, ios_base& __io,
>>+        ios_base::iostate& __err, tm* __tm, const char_type *__fmt,
>>+        const char_type *__fmtend) const
>>+    {
>>+      const locale& __loc = __io._M_getloc();
>>+      ctype<_CharT> const& __ctype = use_facet<ctype<_CharT> >(__loc);
>>+      __err = ios_base::goodbit;
>>+      while (__fmt != __fmtend &&
>>+             __err == ios_base::goodbit)
>>+        {
>>+          if (__s == __end)
>>+            {
>>+              __err = ios_base::eofbit | ios_base::failbit;
>>+              break;
>>+            }
>>+          else if (__ctype.narrow(*__fmt, 0) == '%')
>>+            {
>>+              char __format;
>>+              char __mod = 0;
>>+              if (++__fmt == __fmtend)
>>+                {
>>+                  __err = ios_base::failbit;
>>+                  break;
>>+                }
>>+              const char __c = __ctype.narrow(*__fmt, 0);
>>+              if (__c != 'E' && __c != 'O')
>>+                __format = __c;
>>+              else if (++__fmt != __fmtend)
>>+                {
>>+                  __mod = __c;
>>+                  __format = __ctype.narrow(*__fmt, 0);
>>+                }
>>+              else
>>+                {
>>+                  __err = ios_base::failbit;
>>+                  break;
>>+                }
>>+              __s = this->do_get(__s, __end, __io, __err, __tm, __format, __mod);
>>+              ++__fmt;
>>+            }
>>+          else if (__ctype.is(ctype_base::space, *__fmt))
>>+            {
>>+              ++__fmt;
>>+              while (__fmt != __fmtend &&
>>+                     __ctype.is(ctype_base::space, *__fmt))
>>+                ++__fmt;
>>+
>>+              while (__s != __end &&
>>+                     __ctype.is(ctype_base::space, *__s))
>>+                ++__s;
>>+            }
>>+          // TODO real case-insensitive comparison
>>+          else if (__ctype.tolower(*__s) == __ctype.tolower(*__fmt) ||
>>+                   __ctype.toupper(*__s) == __ctype.toupper(*__fmt))
>>+            {
>>+              ++__s;
>>+              ++__fmt;
>>+            }
>>+          else
>>+            {
>>+              __err = ios_base::failbit;
>>+              break;
>>+            }
>>+        }
>>+      return __s;
>>+    }
>>+
>>+  template<typename _CharT, typename _InIter>
>>+    inline
>>+    _InIter
>>+    time_get<_CharT, _InIter>::
>>+    do_get(iter_type __beg, iter_type __end, ios_base& __io,
>>+           ios_base::iostate& __err, tm* __tm,
>>+           char __format, char __mod) const
>>+    {
>>+      const locale& __loc = __io._M_getloc();
>>+      ctype<_CharT> const& __ctype = use_facet<ctype<_CharT> >(__loc);
>
>Shouldn't this set err = goodbit before doing anything?
>
> "Effects: The function starts by evaluating err = ios_base::goodbit."
>
>>+      char_type __fmt[4];
>>+      __fmt[0] = __ctype.widen('%');
>>+      if (!__mod)
>>+        {
>>+          __fmt[1] = __format;
>>+          __fmt[2] = char_type();
>>+        }
>>+      else
>>+        {
>>+          __fmt[1] = __mod;
>>+          __fmt[2] = __format;
>>+          __fmt[3] = char_type();
>>+        }
>>+
>>+      return _M_extract_via_format(__beg, __end, __io, __err, __tm, __fmt);
>
>It also doesn't seem to meet this requirement:
>
> "When s == end evaluates to true after reading a character the
> function evaluates err |= ios_base::eofbit.
>
>>+    }
>>+
>>+#endif // __cplusplus >= 201103L
>>+
>>  template<typename _CharT, typename _OutIter>
>>    _OutIter
>>    time_put<_CharT, _OutIter>::
>>diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc
>>new file mode 100644
>>index 0000000..1ad7822
>>--- /dev/null
>>+++ b/libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc
>>@@ -0,0 +1,129 @@
>>+// { dg-options " -std=gnu++11 " }
>>+
>>+// 2014-04-14 Rüdiger Sonderfeld  <ruediger@c-plusplus.de>
>>+
>>+// Copyright (C) 2014 Free Software Foundation, Inc.
>>+//
>>+// This file is part of the GNU ISO C++ Library.  This library is free
>>+// software; you can redistribute it and/or modify it under the
>>+// terms of the GNU General Public License as published by the
>>+// Free Software Foundation; either version 3, or (at your option)
>>+// any later version.
>>+
>>+// This library is distributed in the hope that it will be useful,
>>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>+// GNU General Public License for more details.
>>+
>>+// You should have received a copy of the GNU General Public License along
>>+// with this library; see the file COPYING3.  If not see
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+// 22.4.5.1.1 (C++11) time_get members [locale.time.get.members]
>>+
>>+#include <locale>
>>+#include <sstream>
>>+#include <iterator>
>>+#include <testsuite_hooks.h>
>>+
>>+#ifndef _GLIBCXX_ASSERT
>>+#  include <iostream>
>>+#  define PRINT(x) cout << #x << ": " << x << endl
>>+#  define TESTHEAD(x) cout << x << endl
>>+#else
>>+#  define PRINT(x) do {} while(false)
>>+#  define TESTHEAD(x) do {} while(false)
>>+#endif
>>+
>>+void test01()
>>+{
>>+  using namespace std;
>>+  bool test __attribute__((unused)) = true;
>>+
>>+  locale loc_c = locale::classic();
>>+
>>+  istringstream iss;
>>+  iss.imbue(loc_c);
>>+  const time_get<char>& tget = use_facet<time_get<char>>(iss.getloc());
>>+  const istreambuf_iterator<char> end;
>>+
>>+  tm time;
>>+  ios_base::iostate err;
>>+
>>+  // check regular operations with format string
>>+  TESTHEAD("regular operations");
>>+  iss.str("d 2014-04-14 01:09:35");
>>+  string format = "d %Y-%m-%d %H:%M:%S";
>>+  auto ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
>>+                      format.data(), format.data()+format.size());
>>+  PRINT(err);
>>+  VERIFY(err == ios_base::goodbit);
>
>libc++ sets eofbit here.
>
>>+  VERIFY(ret == end);
>>+  PRINT(time.tm_year);
>>+  VERIFY(time.tm_year == 114);
>>+  PRINT(time.tm_mon);
>>+  VERIFY(time.tm_mon == 3);
>>+  PRINT(time.tm_mday);
>>+  VERIFY(time.tm_mday == 14);
>>+  PRINT(time.tm_hour);
>>+  VERIFY(time.tm_hour == 1);
>>+  PRINT(time.tm_min);
>>+  VERIFY(time.tm_min == 9);
>>+  PRINT(time.tm_sec);
>>+  VERIFY(time.tm_sec == 35);
>>+
>>+  TESTHEAD("check eof");
>>+  iss.str("2020");
>>+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
>>+                 format.data(), format.data()+format.size());
>>+  VERIFY(err == ios_base::eofbit | ios_base::failbit);

And this is always true, because == has higher precedence than |

>>+  VERIFY(ret == end);
>
>How can this test pass?
>
>Is there a format = "%Y" missing?
>
>You cannot parse "2020" using the format "d %Y-%m-%d %H:%M:%S" so it
>should not reach EOF and should not set ret = end.
diff mbox

Patch

diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.h b/libstdc++-v3/include/bits/locale_facets_nonio.h
index 3e2cc7f..f6cfe36 100644
--- a/libstdc++-v3/include/bits/locale_facets_nonio.h
+++ b/libstdc++-v3/include/bits/locale_facets_nonio.h
@@ -540,6 +540,54 @@  namespace std _GLIBCXX_VISIBILITY(default)
 	       ios_base::iostate& __err, tm* __tm) const
       { return this->do_get_year(__beg, __end, __io, __err, __tm); }
 
+#if __cplusplus >= 201103L
+      /**
+       *  @brief  Parse input string according to format.
+       *
+       *  This function calls time_get::do_get with the provided
+       *  parameters.  @see do_get() and get().
+       *
+       *  @param __s        Start of string to parse.
+       *  @param __end      End of string to parse.
+       *  @param __io       Source of the locale.
+       *  @param __err      Error flags to set.
+       *  @param __tm       Pointer to struct tm to fill in.
+       *  @param __format   Format specifier.
+       *  @param __modifier Format modifier.
+       *  @return  Iterator to first char not parsed.
+       */
+      inline
+      iter_type get(iter_type __s, iter_type __end, ios_base& __io,
+                    ios_base::iostate& __err, tm* __tm, char __format,
+                    char __modifier = 0) const
+      {
+        return this->do_get(__s, __end, __io, __err, __tm, __format,
+                            __modifier);
+      }
+
+      /**
+       *  @brief  Parse input string according to format.
+       *
+       *  This function parses the input string according to a
+       *  provided format string.  It does the inverse of
+       *  time_put::put.  The format string follows the format
+       *  specified for strftime(3)/strptime(3).  The actual parsing
+       *  is done by time_get::do_get.
+       *
+       *  @param __s        Start of string to parse.
+       *  @param __end      End of string to parse.
+       *  @param __io       Source of the locale.
+       *  @param __err      Error flags to set.
+       *  @param __tm       Pointer to struct tm to fill in.
+       *  @param __fmt      Start of the format string.
+       *  @param __fmtend   End of the format string.
+       *  @return  Iterator to first char not parsed.
+       */
+      iter_type get(iter_type __s, iter_type __end, ios_base& __io,
+                    ios_base::iostate& __err, tm* __tm, const char_type *__fmt,
+                    const char_type *__fmtend) const;
+#endif // __cplusplus >= 201103L
+
     protected:
       /// Destructor.
       virtual
@@ -653,6 +701,31 @@  namespace std _GLIBCXX_VISIBILITY(default)
       do_get_year(iter_type __beg, iter_type __end, ios_base& __io,
 		  ios_base::iostate& __err, tm* __tm) const;
 
+#if __cplusplus >= 201103L
+      /**
+       *  @brief  Parse input string according to format.
+       *
+       *  This function parses the string according to the provided
+       *  format and optional modifier.  This function is a hook for
+       *  derived classes to change the value returned.  @see get()
+       *  for more details.
+       *
+       *  @param __s        Start of string to parse.
+       *  @param __end      End of string to parse.
+       *  @param __io       Source of the locale.
+       *  @param __err      Error flags to set.
+       *  @param __tm       Pointer to struct tm to fill in.
+       *  @param __format   Format specifier.
+       *  @param __modifier Format modifier.
+       *  @return  Iterator to first char not parsed.
+       */
+      //virtual
+      iter_type
+      do_get(iter_type __s, iter_type __end, ios_base& __f,
+             ios_base::iostate& __err, tm* __tm,
+             char __format, char __modifier) const;
+#endif // __cplusplus >= 201103L
+
       // Extract numeric component of length __len.
       iter_type
       _M_extract_num(iter_type __beg, iter_type __end, int& __member,
diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.tcc b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
index 41d944d..93466b6 100644
--- a/libstdc++-v3/include/bits/locale_facets_nonio.tcc
+++ b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
@@ -1137,6 +1137,108 @@  _GLIBCXX_END_NAMESPACE_LDBL
       return __beg;
     }
 
+#if __cplusplus >= 201103L
+  template<typename _CharT, typename _InIter>
+    inline
+    _InIter
+    time_get<_CharT, _InIter>::
+    get(iter_type __s, iter_type __end, ios_base& __io,
+        ios_base::iostate& __err, tm* __tm, const char_type *__fmt,
+        const char_type *__fmtend) const
+    {
+      const locale& __loc = __io._M_getloc();
+      ctype<_CharT> const& __ctype = use_facet<ctype<_CharT> >(__loc);
+      __err = ios_base::goodbit;
+      while (__fmt != __fmtend &&
+             __err == ios_base::goodbit)
+        {
+          if (__s == __end)
+            {
+              __err = ios_base::eofbit | ios_base::failbit;
+              break;
+            }
+          else if (__ctype.narrow(*__fmt, 0) == '%')
+            {
+              char __format;
+              char __mod = 0;
+              if (++__fmt == __fmtend)
+                {
+                  __err = ios_base::failbit;
+                  break;
+                }
+              const char __c = __ctype.narrow(*__fmt, 0);
+              if (__c != 'E' && __c != 'O')
+                __format = __c;
+              else if (++__fmt != __fmtend)
+                {
+                  __mod = __c;
+                  __format = __ctype.narrow(*__fmt, 0);
+                }
+              else
+                {
+                  __err = ios_base::failbit;
+                  break;
+                }
+              __s = this->do_get(__s, __end, __io, __err, __tm, __format, __mod);
+              ++__fmt;
+            }
+          else if (__ctype.is(ctype_base::space, *__fmt))
+            {
+              ++__fmt;
+              while (__fmt != __fmtend &&
+                     __ctype.is(ctype_base::space, *__fmt))
+                ++__fmt;
+
+              while (__s != __end &&
+                     __ctype.is(ctype_base::space, *__s))
+                ++__s;
+            }
+          // TODO real case-insensitive comparison
+          else if (__ctype.tolower(*__s) == __ctype.tolower(*__fmt) ||
+                   __ctype.toupper(*__s) == __ctype.toupper(*__fmt))
+            {
+              ++__s;
+              ++__fmt;
+            }
+          else
+            {
+              __err = ios_base::failbit;
+              break;
+            }
+        }
+      return __s;
+    }
+
+  template<typename _CharT, typename _InIter>
+    inline
+    _InIter
+    time_get<_CharT, _InIter>::
+    do_get(iter_type __beg, iter_type __end, ios_base& __io,
+           ios_base::iostate& __err, tm* __tm,
+           char __format, char __mod) const
+    {
+      const locale& __loc = __io._M_getloc();
+      ctype<_CharT> const& __ctype = use_facet<ctype<_CharT> >(__loc);
+
+      char_type __fmt[4];
+      __fmt[0] = __ctype.widen('%');
+      if (!__mod)
+        {
+          __fmt[1] = __format;
+          __fmt[2] = char_type();
+        }
+      else
+        {
+          __fmt[1] = __mod;
+          __fmt[2] = __format;
+          __fmt[3] = char_type();
+        }
+
+      return _M_extract_via_format(__beg, __end, __io, __err, __tm, __fmt);
+    }
+
+#endif // __cplusplus >= 201103L
+
   template<typename _CharT, typename _OutIter>
     _OutIter
     time_put<_CharT, _OutIter>::
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc
new file mode 100644
index 0000000..1ad7822
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get/char/1.cc
@@ -0,0 +1,129 @@ 
+// { dg-options " -std=gnu++11 " }
+
+// 2014-04-14 Rüdiger Sonderfeld  <ruediger@c-plusplus.de>
+
+// Copyright (C) 2014 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// 22.4.5.1.1 (C++11) time_get members [locale.time.get.members]
+
+#include <locale>
+#include <sstream>
+#include <iterator>
+#include <testsuite_hooks.h>
+
+#ifndef _GLIBCXX_ASSERT
+#  include <iostream>
+#  define PRINT(x) cout << #x << ": " << x << endl
+#  define TESTHEAD(x) cout << x << endl
+#else
+#  define PRINT(x) do {} while(false)
+#  define TESTHEAD(x) do {} while(false)
+#endif
+
+void test01()
+{
+  using namespace std;
+  bool test __attribute__((unused)) = true;
+
+  locale loc_c = locale::classic();
+
+  istringstream iss;
+  iss.imbue(loc_c);
+  const time_get<char>& tget = use_facet<time_get<char>>(iss.getloc());
+  const istreambuf_iterator<char> end;
+
+  tm time;
+  ios_base::iostate err;
+
+  // check regular operations with format string
+  TESTHEAD("regular operations");
+  iss.str("d 2014-04-14 01:09:35");
+  string format = "d %Y-%m-%d %H:%M:%S";
+  auto ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
+                      format.data(), format.data()+format.size());
+  PRINT(err);
+  VERIFY(err == ios_base::goodbit);
+  VERIFY(ret == end);
+  PRINT(time.tm_year);
+  VERIFY(time.tm_year == 114);
+  PRINT(time.tm_mon);
+  VERIFY(time.tm_mon == 3);
+  PRINT(time.tm_mday);
+  VERIFY(time.tm_mday == 14);
+  PRINT(time.tm_hour);
+  VERIFY(time.tm_hour == 1);
+  PRINT(time.tm_min);
+  VERIFY(time.tm_min == 9);
+  PRINT(time.tm_sec);
+  VERIFY(time.tm_sec == 35);
+
+  TESTHEAD("check eof");
+  iss.str("2020");
+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
+                 format.data(), format.data()+format.size());
+  VERIFY(err == ios_base::eofbit | ios_base::failbit);
+  VERIFY(ret == end);
+
+  TESTHEAD("check broken format");
+  iss.str("2014-04-14 01:09:35");
+  format = "%";
+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
+                 format.data(), format.data()+format.size());
+  VERIFY(err == ios_base::failbit);
+
+  TESTHEAD("check single format letter version");
+  iss.str("2020");
+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time, 'Y');
+  VERIFY(err == ios_base::goodbit);
+  VERIFY(time.tm_year == 120);
+  VERIFY(ret == end);
+
+  TESTHEAD("check skipping of space");
+  iss.str("2010    07 01");
+  format = "%Y %m %d";
+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
+                 format.data(), format.data()+format.size());
+  VERIFY(err == ios_base::goodbit);
+  VERIFY(time.tm_year == 110);
+  VERIFY(time.tm_mon == 7);
+  VERIFY(time.tm_mday == 1);
+  VERIFY(ret == end);
+
+  TESTHEAD("check mismatch");
+  iss.str("year: 1970");
+  format = "jahr: 1970";
+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
+                 format.data(), format.data()+format.size());
+  VERIFY(err == ios_base::failbit);
+  VERIFY(ret == istreambuf_iterator<char>(iss));
+
+  TESTHEAD("check case insensitive match");
+  iss.str("yEaR: 1980");
+  format = "YeAR: 1980";
+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
+                 format.data(), format.data()+format.size());
+  VERIFY(err == ios_base::goodbit);
+  VERIFY(ret == end);
+  VERIFY(time.tm_year == 80);
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/char/2.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/char/2.cc
new file mode 100644
index 0000000..cbd70a7
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get/char/2.cc
@@ -0,0 +1,103 @@ 
+// { dg-require-namedlocale "de_DE.utf8" }
+// { dg-options " -std=gnu++11 " }
+
+// 2014-04-14 Rüdiger Sonderfeld  <ruediger@c-plusplus.de>
+
+// Copyright (C) 2014 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// 22.4.5.1.1 (C++11) time_get members [locale.time.get.members]
+
+#include <locale>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+#ifndef _GLIBCXX_ASSERT
+#  include <iostream>
+#  define PRINT(x) cout << #x << ": " << x << endl
+#  define TESTHEAD(x) cout << x << endl
+#else
+#  define PRINT(x) do {} while(false)
+#  define TESTHEAD(x) do {} while(false)
+#endif
+
+void test02()
+{
+  using namespace std;
+  bool test __attribute__((unused)) = true;
+
+  locale loc_c = locale::classic();
+  locale loc_de = locale("de_DE.utf8");
+  VERIFY( loc_de != loc_c );
+
+  istringstream iss;
+  iss.imbue(loc_de);
+  const time_get<char>& tget = use_facet<time_get<char>>(iss.getloc());
+  const istreambuf_iterator<char> end;
+
+  ios_base::iostate err;
+  tm time;
+
+  TESTHEAD("German locale test");
+  iss.str("Montag, den 14. April 2014");
+  string format = "%A, den %d. %B %Y";
+  auto ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time,
+                      format.data(), format.data()+format.size());
+  PRINT(err);
+  VERIFY(err == ios_base::goodbit);
+  PRINT(time.tm_year);
+  VERIFY(time.tm_year == 114);
+  PRINT(time.tm_mon);
+  VERIFY(time.tm_mon == 3);
+  PRINT(time.tm_wday);
+  VERIFY(time.tm_wday == 1);
+  PRINT(time.tm_mday);
+  VERIFY(time.tm_mday == 14);
+  VERIFY(end == end);
+
+  TESTHEAD("German locale: Check case-insensitivity");
+  tm time2;
+  iss.str("Montag, den 14. April 2014");
+  format = "%A, DEN %d. %B %Y"; // check case-insensitivity
+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time2,
+                 format.data(), format.data()+format.size());
+  PRINT(err);
+  VERIFY(err == ios_base::goodbit);
+  PRINT(time2.tm_year);
+  VERIFY(time2.tm_year == 114);
+  PRINT(time2.tm_mon);
+  VERIFY(time2.tm_mon == 3);
+  PRINT(time2.tm_wday);
+  VERIFY(time2.tm_wday == 1);
+  PRINT(time2.tm_mday);
+  VERIFY(time2.tm_mday == 14);
+  VERIFY(end == end);
+
+  TESTHEAD("German locale: Check single");
+  iss.str("Mittwoch");
+  ret = tget.get(istreambuf_iterator<char>(iss), end, iss, err, &time, 'A');
+  PRINT(err);
+  VERIFY(err == ios_base::goodbit);
+  PRINT(time.tm_wday);
+  VERIFY(time.tm_wday == 3);
+  VERIFY(end == end);
+}
+
+int main()
+{
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/1.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/1.cc
new file mode 100644
index 0000000..69db25b
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/1.cc
@@ -0,0 +1,130 @@ 
+// { dg-options " -std=gnu++11 " }
+
+// 2014-04-14 Rüdiger Sonderfeld  <ruediger@c-plusplus.de>
+
+// Copyright (C) 2014 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// 22.4.5.1.1 (C++11) time_get members [locale.time.get.members]
+
+#include <locale>
+#include <sstream>
+#include <iterator>
+#include <testsuite_hooks.h>
+
+#ifndef _GLIBCXX_ASSERT
+#  include <iostream>
+#  define PRINT(x) cout << #x << ": " << x << endl
+#  define TESTHEAD(x) cout << x << endl
+#else
+#  define PRINT(x) do {} while(false)
+#  define TESTHEAD(x) do {} while(false)
+#endif
+
+void test01()
+{
+  using namespace std;
+  bool test __attribute__((unused)) = true;
+
+  locale loc_c = locale::classic();
+
+  wistringstream iss;
+  iss.imbue(loc_c);
+  const time_get<wchar_t>& tget = use_facet<time_get<wchar_t>>(iss.getloc());
+  typedef istreambuf_iterator<wchar_t> iter;
+  const iter end;
+
+  tm time;
+  ios_base::iostate err;
+
+  // check regular operations with format string
+  TESTHEAD("regular operations");
+  iss.str(L"d 2014-04-14 01:09:35");
+  wstring format = L"d %Y-%m-%d %H:%M:%S";
+  auto ret = tget.get(istreambuf_iterator<wchar_t>(iss), end, iss, err, &time,
+                      format.data(), format.data()+format.size());
+  PRINT(err);
+  VERIFY(err == ios_base::goodbit);
+  VERIFY(ret == end);
+  PRINT(time.tm_year);
+  VERIFY(time.tm_year == 114);
+  PRINT(time.tm_mon);
+  VERIFY(time.tm_mon == 3);
+  PRINT(time.tm_mday);
+  VERIFY(time.tm_mday == 14);
+  PRINT(time.tm_hour);
+  VERIFY(time.tm_hour == 1);
+  PRINT(time.tm_min);
+  VERIFY(time.tm_min == 9);
+  PRINT(time.tm_sec);
+  VERIFY(time.tm_sec == 35);
+
+  TESTHEAD("check eof");
+  iss.str(L"2020");
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                 format.data(), format.data()+format.size());
+  VERIFY(err == ios_base::eofbit | ios_base::failbit);
+  VERIFY(ret == end);
+
+  TESTHEAD("check broken format");
+  iss.str(L"2014-04-14 01:09:35");
+  format = L"%";
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                 format.data(), format.data()+format.size());
+  VERIFY(err == ios_base::failbit);
+
+  TESTHEAD("check single format letter version");
+  iss.str(L"2020");
+  ret = tget.get(iter(iss), end, iss, err, &time, L'Y');
+  VERIFY(err == ios_base::goodbit);
+  VERIFY(time.tm_year == 120);
+  VERIFY(ret == end);
+
+  TESTHEAD(L"check skipping of space");
+  iss.str(L"2010    07 01");
+  format = L"%Y %m %d";
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                 format.data(), format.data()+format.size());
+  VERIFY(err == ios_base::goodbit);
+  VERIFY(time.tm_year == 110);
+  VERIFY(time.tm_mon == 7);
+  VERIFY(time.tm_mday == 1);
+  VERIFY(ret == end);
+
+  TESTHEAD("check mismatch");
+  iss.str(L"year: 1970");
+  format = L"jahr: 1970";
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                 format.data(), format.data()+format.size());
+  VERIFY(err == ios_base::failbit);
+  VERIFY(ret == iter(iss));
+
+  TESTHEAD("check case insensitive match");
+  iss.str(L"yEaR: 1980");
+  format = L"YeAR: 1980";
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                 format.data(), format.data()+format.size());
+  VERIFY(err == ios_base::goodbit);
+  VERIFY(ret == end);
+  VERIFY(time.tm_year == 80);
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/2.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/2.cc
new file mode 100644
index 0000000..d4e2821
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/2.cc
@@ -0,0 +1,104 @@ 
+// { dg-require-namedlocale "de_DE.utf8" }
+// { dg-options " -std=gnu++11 " }
+
+// 2014-04-14 Rüdiger Sonderfeld  <ruediger@c-plusplus.de>
+
+// Copyright (C) 2014 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// 22.4.5.1.1 (C++11) time_get members [locale.time.get.members]
+
+#include <locale>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+#ifndef _GLIBCXX_ASSERT
+#  include <iostream>
+#  define PRINT(x) cout << #x << ": " << x << endl
+#  define TESTHEAD(x) cout << x << endl
+#else
+#  define PRINT(x) do {} while(false)
+#  define TESTHEAD(x) do {} while(false)
+#endif
+
+void test02()
+{
+  using namespace std;
+  bool test __attribute__((unused)) = true;
+
+  locale loc_c = locale::classic();
+  locale loc_de = locale("de_DE.utf8");
+  VERIFY( loc_de != loc_c );
+
+  wistringstream iss;
+  iss.imbue(loc_de);
+  const time_get<wchar_t>& tget = use_facet<time_get<wchar_t>>(iss.getloc());
+  typedef istreambuf_iterator<wchar_t> iter;
+  const iter end;
+
+  ios_base::iostate err;
+  tm time;
+
+  TESTHEAD("German locale test");
+  iss.str(L"Montag, den 14. April 2014");
+  wstring format = L"%A, den %d. %B %Y";
+  auto ret = tget.get(iter(iss), end, iss, err, &time,
+                      format.data(), format.data()+format.size());
+  PRINT(err);
+  VERIFY(err == ios_base::goodbit);
+  PRINT(time.tm_year);
+  VERIFY(time.tm_year == 114);
+  PRINT(time.tm_mon);
+  VERIFY(time.tm_mon == 3);
+  PRINT(time.tm_wday);
+  VERIFY(time.tm_wday == 1);
+  PRINT(time.tm_mday);
+  VERIFY(time.tm_mday == 14);
+  VERIFY(end == end);
+
+  TESTHEAD("German locale: Check case-insensitivity");
+  tm time2;
+  iss.str(L"Montag, den 14. April 2014");
+  format = L"%A, DEN %d. %B %Y"; // check case-insensitivity
+  ret = tget.get(iter(iss), end, iss, err, &time2,
+                 format.data(), format.data()+format.size());
+  PRINT(err);
+  VERIFY(err == ios_base::goodbit);
+  PRINT(time2.tm_year);
+  VERIFY(time2.tm_year == 114);
+  PRINT(time2.tm_mon);
+  VERIFY(time2.tm_mon == 3);
+  PRINT(time2.tm_wday);
+  VERIFY(time2.tm_wday == 1);
+  PRINT(time2.tm_mday);
+  VERIFY(time2.tm_mday == 14);
+  VERIFY(end == end);
+
+  TESTHEAD("German locale: Check single");
+  iss.str(L"Mittwoch");
+  ret = tget.get(iter(iss), end, iss, err, &time, L'A');
+  PRINT(err);
+  VERIFY(err == ios_base::goodbit);
+  PRINT(time.tm_wday);
+  VERIFY(time.tm_wday == 3);
+  VERIFY(end == end);
+}
+
+int main()
+{
+  test02();
+}