Patchwork [so_7-2] DR 13631 patch

login
register
mail settings
Submitter François Dumont
Date March 1, 2012, 8:43 p.m.
Message ID <4F4FDF58.2000209@gmail.com>
Download mbox | patch
Permalink /patch/144111/
State New
Headers show

Comments

François Dumont - March 1, 2012, 8:43 p.m.
Here is what I have finally commited to libstdcxx_so_7-2 branch.

2012-03-01  François Dumont <fdumont@gcc.gnu.org>

         DR libstdc++/13631
         * config/locale/gnu/messages_member.h, messages_member.cc: Prefer
         dgettext usage to gettext to allow usage of several catalogs at the
         same time. Add an internal cache to map catalog names to 
catalog ids.
         * testsuite/22_locale/messages/13631.cc: New.

I have integrated your remarks Paolo and also:
- Add a destructor to the Catalogs class, an application that correctly 
close its catalogs will make this destructor useless but it is normal to 
have one.
- I change the _MapEntry from pair<catalog, string> to pair<catalog, 
const char*>. This way _M_get do not have a string copy anymore.

Tested under linux x86_64.

François

On 02/29/2012 12:02 PM, Paolo Carlini wrote:
> Hi,
>> Hi
>>
>>     I finally spend some more time to enhance this patch.
>>
>>     I used a sorted array to store the mapping, doing so I do not 
>> need to export the _Rb_Tree instantiation anymore. I also simplified 
>> the test, to reproduce the 13631 we don't need an other valid 
>> catalog, any attempt to open a catalog after having open the 
>> 'libstdc++' one show the issue. So we do not need add any 
>> dg-require-XXX macro to validate a catalog presence.
>>
>>     If no one see any problem with this patch I will commit it to 
>> libstdcxx_so_7-2 branch.
> I'm Ok with the patch, besides a few minor stylistic nits below. You 
> didn't post the ChangeLog entry: remember to have the PR number in it.
>> Index: testsuite/22_locale/messages/13631.cc
>> ===================================================================
>> --- testsuite/22_locale/messages/13631.cc	(revision 0)
>> +++ testsuite/22_locale/messages/13631.cc	(revision 0)
>> @@ -0,0 +1,57 @@
>> +// { dg-require-namedlocale "fr_FR" }
>> +
>> +// Copyright (C) 2012 Free Software Foundation
>> +//
>> +// 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/>.
>> +
>> +#include<locale>
>> +#include<testsuite_hooks.h>
>> +
>> +int main(int argc, char **argv)
>> +{
>> +  bool test __attribute__((unused)) = true;
>> +  // This is defined through CXXFLAGS in scripts/testsuite_flags[.in].
>> +  const char* dir = LOCALEDIR;
>> +
>> +  std::locale l("fr_FR");
>> +
>> +  typedef std::messages<char>  messages;
>> +
>> +  const messages&msgs_facet = std::use_facet<messages>(l);
>> +
>> +  messages::catalog msgs = msgs_facet.open("libstdc++", l, dir);
>> +  VERIFY( msgs>= 0 );
>> +
>> +  const char msgid[] = "please";
>> +  std::string translation1 = msgs_facet.get(msgs, 0, 0, msgid);
>> +
>> +  // Without a real translation this test doesn't mean anything:
>> +  VERIFY( translation1 != msgid );
>> +
>> +  // Opening an other catalog was enough to show the problem, even a fake
>> +  // catalog.
>> +  messages::catalog fake_msgs = msgs_facet.open("fake", l);
>> +
>> +  std::string translation2 = msgs_facet.get(msgs, 0, 0, msgid);
>> +
>> +  // Close catalogs before doing the check to avoid leaks.
>> +  msgs_facet.close(fake_msgs);
>> +  msgs_facet.close(msgs);
>> +
>> +  VERIFY( translation1 == translation2 );
>> +
>> +  return 0;
>> +}
> Normally we have the test proper in a separate funtion called from main.
>> Index: config/locale/gnu/messages_members.cc
>> ===================================================================
>> --- config/locale/gnu/messages_members.cc	(revision 184372)
>> +++ config/locale/gnu/messages_members.cc	(working copy)
>> @@ -1,6 +1,7 @@
>>   // std::messages implementation details, GNU version -*- C++ -*-
>>
>> -// Copyright (C) 2001, 2002, 2005, 2009, 2010 Free Software Foundation, Inc.
>> +// Copyright (C) 2001, 2002, 2005, 2009, 2010, 2012
>> +// 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
>> @@ -31,54 +32,180 @@
>>   #include<locale>
>>   #include<bits/c++locale_internal.h>
>>
>> +#include<algorithm>
>> +#include<utility>
>> +#include<ext/concurrence.h>
>> +
>> +namespace
>> +{
>> +  using namespace std;
>> +
>> +  struct Comp
>> +  {
>> +    typedef messages_base::catalog catalog;
>> +    typedef pair<catalog, string>  _Mapping;
>> +
>> +    bool operator () (catalog __cat, const _Mapping* __pair) const
>> +    { return __cat<  __pair->first; }
> No space after operator
>
>> +
>> +    bool operator () (const _Mapping* __pair, catalog __cat) const
>> +    { return __pair->first<  __cat; }
> Likewise.
>
>> +  };
>> +
>> +  class Catalogs
>> +  {
>> +    typedef messages_base::catalog catalog;
>> +    typedef pair<catalog, string>  _Mapping;
>> +    typedef _Mapping* _MapEntry;
>> +
>> +  public:
>> +    Catalogs() : _M_counter(0), _M_nb_entry(0) { }
>> +
>> +    catalog _M_add(const string&  __s)
>> +    {
>> +      __gnu_cxx::__scoped_lock lock(_M_mutex);
>> +      _MapEntry* __new_map = new _MapEntry[_M_nb_entry + 1];
>> +      _MapEntry __new_entry;
>> +      __try
>> +	{
>> +	  __new_entry = new _Mapping(_M_counter, __s);
>> +	}
>> +      __catch(...)
>> +	{
>> +	  delete[] __new_map;
>> +	  __throw_exception_again;
>> +	}
>> +      copy(_M_map, _M_map + _M_nb_entry, __new_map);
> Normally before a comment we always put a blank line.
>
> In general - I think I noticed something in the unordered_* too - for 
> improved legibility I would recommend packing the code a little less, 
> just add blank lines somewhere, eg, more or less always when an 
> if/for/while/catch curly bracket is closed.
>> +      // The counter is not likely to roll unless catalogs keep on being
>> +      // open/close which is consider as an application mistake for the moment.
>> +      catalog __cat = _M_counter++;
>> +      __new_map[_M_nb_entry] = __new_entry;
>> +      delete[] _M_map;
>> +      _M_map = __new_map;
>> +      ++_M_nb_entry;
>> +      return __cat;
>> +    }
> Thanks,
> Paolo.

Patch

Index: config/locale/gnu/messages_members.cc
===================================================================
--- config/locale/gnu/messages_members.cc	(revision 184758)
+++ config/locale/gnu/messages_members.cc	(working copy)
@@ -1,6 +1,7 @@ 
 // std::messages implementation details, GNU version -*- C++ -*-
 
-// Copyright (C) 2001, 2002, 2005, 2009, 2010 Free Software Foundation, Inc.
+// Copyright (C) 2001, 2002, 2005, 2009, 2010, 2012
+// 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
@@ -31,54 +32,196 @@ 
 #include <locale>
 #include <bits/c++locale_internal.h>
 
+#include <algorithm>
+#include <utility>
+#include <ext/concurrence.h>
+
+namespace
+{
+  using namespace std;
+
+  typedef messages_base::catalog catalog;
+  typedef pair<catalog, const char*> _MapEntry;
+
+  struct Comp
+  {
+    bool operator()(catalog __cat, const _MapEntry& __entry) const
+    { return __cat < __entry.first; }
+
+    bool operator()(const _MapEntry& __entry, catalog __cat) const
+    { return __entry.first < __cat; }
+  };
+
+  class Catalogs
+  {
+  public:
+    Catalogs() : _M_counter(0), _M_nb_entry(0) { }
+
+    ~Catalogs()
+    {
+      if (_M_nb_entry)
+	{
+	  for (size_t i = 0; i != _M_nb_entry; ++i)
+	    delete[] _M_map[i].second;
+	  delete[] _M_map;
+	}
+    }
+
+    catalog _M_add(const string& __s)
+    {
+      __gnu_cxx::__scoped_lock lock(_M_mutex);
+
+      _MapEntry* __new_map = new _MapEntry[_M_nb_entry + 1];
+      __try
+	{
+	  copy(_M_map, _M_map + _M_nb_entry, __new_map);
+	  char* __s_copy = new char[__s.size() + 1];
+	  __s.copy(__s_copy, __s.size());
+	  __s_copy[__s.size()] = 0;
+	  __new_map[_M_nb_entry] = make_pair(_M_counter, __s_copy);
+	}
+      __catch(...)
+	{
+	  delete[] __new_map;
+	  __throw_exception_again;
+	}
+
+
+      // The counter is not likely to roll unless catalogs keep on being
+      // open/close which is consider as an application mistake for the moment.
+      catalog __cat = _M_counter++;
+      delete[] _M_map;
+      _M_map = __new_map;
+      ++_M_nb_entry;
+
+      return __cat;
+    }
+
+    void _M_erase(catalog __c)
+    {
+      __gnu_cxx::__scoped_lock lock(_M_mutex);
+
+      _MapEntry* __entry =
+	lower_bound(_M_map, _M_map + _M_nb_entry, __c, Comp());
+      if (__entry == _M_map + _M_nb_entry || __entry->first != __c)
+	return;
+      
+      _MapEntry* __new_map =
+	_M_nb_entry > 1 ? new _MapEntry[_M_nb_entry - 1] : 0;
+      copy(__entry + 1, _M_map + _M_nb_entry,
+	   copy(_M_map, __entry, __new_map));
+
+      delete[] __entry->second;
+      delete[] _M_map;
+      _M_map = __new_map;
+      --_M_nb_entry;
+    }
+
+    pair<bool, const char*> _M_get(catalog __c) const
+    {
+      __gnu_cxx::__scoped_lock lock(_M_mutex);
+
+      typedef pair<bool, const char*> _Ret;
+
+      const _MapEntry* __entry =
+	lower_bound(_M_map, _M_map + _M_nb_entry, __c, Comp());
+      if (__entry != _M_map + _M_nb_entry && __entry->first == __c)
+	return _Ret(true, __entry->second);
+      return _Ret(false, 0);
+    }
+
+  private:
+    mutable __gnu_cxx::__mutex _M_mutex;
+    catalog _M_counter;
+    _MapEntry* _M_map;
+    size_t _M_nb_entry;
+  };
+
+  Catalogs&
+  get_catalogs()
+  {
+    static Catalogs __catalogs;
+    return __catalogs;
+  }
+
+  const char*
+  get_glibc_msg(__c_locale __attribute__((unused)) __locale_messages,
+		messages_base::catalog __c,
+		const char* __dfault)
+  {
+    pair<bool, const char*> __ret = get_catalogs()._M_get(__c);
+
+    if (!__ret.first)
+      return __dfault;
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
+    std::__c_locale __old = __uselocale(__locale_messages);
+    const char* __msg =
+      const_cast<const char*>(dgettext(__ret.second, __dfault));
+    __uselocale(__old);
+    return __msg;
+#else
+    char* __old = setlocale(LC_ALL, 0);
+    const size_t __len = strlen(__old) + 1;
+    char* __sav = new char[__len];
+    memcpy(__sav, __old, __len);
+    setlocale(LC_ALL, _M_name_messages);
+    const char* __msg = dgettext(__ret.second, __dfault);
+    setlocale(LC_ALL, __sav);
+    delete [] __sav;
+    return __msg;
+#endif
+  }
+}
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
-_GLIBCXX_BEGIN_NAMESPACE_VERSION
+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Specializations.
   template<>
+    typename messages<char>::catalog
+    messages<char>::do_open(const basic_string<char>& __s,
+			    const locale&) const
+    { return get_catalogs()._M_add(__s); }
+
+  template<>
+    void
+    messages<char>::do_close(catalog __c) const
+    { get_catalogs()._M_erase(__c); }
+
+  template<>
     string
-    messages<char>::do_get(catalog, int, int, const string& __dfault) const
+    messages<char>::do_get(catalog __c, int, int,
+			   const string& __dfault) const
     {
-#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
-      __c_locale __old = __uselocale(_M_c_locale_messages);
-      const char* __msg = const_cast<const char*>(gettext(__dfault.c_str()));
-      __uselocale(__old);
-      return string(__msg);
-#else
-      char* __old = setlocale(LC_ALL, 0);
-      const size_t __len = strlen(__old) + 1;
-      char* __sav = new char[__len];
-      memcpy(__sav, __old, __len);
-      setlocale(LC_ALL, _M_name_messages);
-      const char* __msg = gettext(__dfault.c_str());
-      setlocale(LC_ALL, __sav);
-      delete [] __sav;
-      return string(__msg);
-#endif
+      if (__c < 0)
+	return __dfault;
+      return string(get_glibc_msg(_M_c_locale_messages,
+				  __c, __dfault.c_str()));
     }
 
 #ifdef _GLIBCXX_USE_WCHAR_T
   template<>
+    typename messages<wchar_t>::catalog
+    messages<wchar_t>::do_open(const basic_string<char>& __s,
+			       const locale&) const
+    { return get_catalogs()._M_add(__s); }
+
+  template<>
+    void
+    messages<wchar_t>::do_close(catalog __c) const
+    { get_catalogs()._M_erase(__c); }
+
+  template<>
     wstring
-    messages<wchar_t>::do_get(catalog, int, int, const wstring& __dfault) const
+    messages<wchar_t>::do_get(catalog __c, int, int,
+			      const wstring& __dfault) const
     {
-# if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
-      __c_locale __old = __uselocale(_M_c_locale_messages);
-      char* __msg = gettext(_M_convert_to_char(__dfault));
-      __uselocale(__old);
-      return _M_convert_from_char(__msg);
-# else
-      char* __old = setlocale(LC_ALL, 0);
-      const size_t __len = strlen(__old) + 1;
-      char* __sav = new char[__len];
-      memcpy(__sav, __old, __len);
-      setlocale(LC_ALL, _M_name_messages);
-      char* __msg = gettext(_M_convert_to_char(__dfault));
-      setlocale(LC_ALL, __sav);
-      delete [] __sav;
-      return _M_convert_from_char(__msg);
-# endif
+      if (__c < 0)
+	return __dfault;
+      return _M_convert_from_char
+	(const_cast<char*>(get_glibc_msg(_M_c_locale_messages, __c,
+					 _M_convert_to_char(__dfault))));
     }
 #endif
 
Index: config/locale/gnu/messages_members.h
===================================================================
--- config/locale/gnu/messages_members.h	(revision 184758)
+++ config/locale/gnu/messages_members.h	(working copy)
@@ -1,6 +1,6 @@ 
 // std::messages implementation details, GNU version -*- C++ -*-
 
-// Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010
+// Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012
 // Free Software Foundation, Inc.
 //
 // This file is part of the GNU ISO C++ Library.  This library is free
@@ -84,22 +84,6 @@ 
       _S_destroy_c_locale(_M_c_locale_messages); 
     }
 
-  template<typename _CharT>
-    typename messages<_CharT>::catalog 
-    messages<_CharT>::do_open(const basic_string<char>& __s, 
-			      const locale&) const
-    { 
-      // No error checking is done, assume the catalog exists and can
-      // be used.
-      textdomain(__s.c_str());
-      return 0;
-    }
-
-  template<typename _CharT>
-    void    
-    messages<_CharT>::do_close(catalog) const 
-    { }
-
    // messages_byname
    template<typename _CharT>
      messages_byname<_CharT>::messages_byname(const char* __s, size_t __refs)
@@ -127,5 +111,25 @@ 
 	 }
      }
 
+    template<>
+      typename messages<char>::catalog
+      messages<char>::do_open(const basic_string<char>&,
+			      const locale&) const;
+
+    template<>
+      void
+      messages<char>::do_close(catalog) const;
+
+#ifdef _GLIBCXX_USE_WCHAR_T
+    template<>
+      typename messages<wchar_t>::catalog
+      messages<wchar_t>::do_open(const basic_string<char>&,
+				 const locale&) const;
+
+    template<>
+      void
+      messages<wchar_t>::do_close(catalog) const;
+#endif
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
Index: testsuite/22_locale/messages/13631.cc
===================================================================
--- testsuite/22_locale/messages/13631.cc	(revision 0)
+++ testsuite/22_locale/messages/13631.cc	(revision 0)
@@ -0,0 +1,61 @@ 
+// { dg-require-namedlocale "fr_FR" }
+
+// Copyright (C) 2012 Free Software Foundation
+//
+// 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/>.
+
+#include <locale>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  bool test __attribute__((unused)) = true;
+  // This is defined through CXXFLAGS in scripts/testsuite_flags[.in].
+  const char* dir = LOCALEDIR;
+
+  std::locale l("fr_FR");
+
+  typedef std::messages<char> messages;
+
+  const messages &msgs_facet = std::use_facet<messages>(l);
+
+  messages::catalog msgs = msgs_facet.open("libstdc++", l, dir);
+  VERIFY( msgs >= 0 );
+
+  const char msgid[] = "please";
+  std::string translation1 = msgs_facet.get(msgs, 0, 0, msgid);
+
+  // Without a real translation this test doesn't mean anything:
+  VERIFY( translation1 != msgid );
+
+  // Opening an other catalog was enough to show the problem, even a fake
+  // catalog.
+  messages::catalog fake_msgs = msgs_facet.open("fake", l);
+
+  std::string translation2 = msgs_facet.get(msgs, 0, 0, msgid);
+
+  // Close catalogs before doing the check to avoid leaks.
+  msgs_facet.close(fake_msgs);
+  msgs_facet.close(msgs);
+
+  VERIFY( translation1 == translation2 );
+}
+
+int main()
+{
+  test01();
+  return 0;
+}