// Locale support -*- C++ -*-
// Copyright (C) 2014-2019 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.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// .
//
// ISO C++ 14882: 22.1 Locales
//
// This file defines classes that behave like the standard predefined locale
// facets (collate, money_get etc.) except that they forward all virtual
// functions to another facet which uses a different std::string ABI,
// converting between string types as needed.
// When a user replaces one of the relevant facets the corresponding shim in
// this file is used so that the replacement facet can be used (via the shim)
// in code that uses the other std::string ABI from the replacing code.
#ifndef _GLIBCXX_USE_CXX11_ABI
# define _GLIBCXX_USE_CXX11_ABI 1
#endif
#include
#if ! _GLIBCXX_USE_DUAL_ABI
# error This file should not be compiled for this configuration.
#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
// Base class of facet shims, holds a reference to the underlying facet
// that the shim forwards to.
class locale::facet::__shim
{
public:
const facet* _M_get() const { return _M_facet; }
__shim(const __shim&) = delete;
__shim& operator=(const __shim&) = delete;
protected:
explicit
__shim(const facet* __f) : _M_facet(__f) { __f->_M_add_reference(); }
~__shim() { _M_facet->_M_remove_reference(); }
private:
const facet* _M_facet;
};
namespace __facet_shims
{
namespace // unnamed
{
template
void __destroy_string(void* p)
{
static_cast*>(p)->~basic_string();
}
} // namespace
// Manages a buffer of uninitialized memory that can store a std::string
// or std::wstring, using either ABI, and convert to the other ABI.
class __any_string
{
struct __attribute__((may_alias)) __str_rep
{
union {
const void* _M_p;
char* _M_pc;
#ifdef _GLIBCXX_USE_WCHAR_T
wchar_t* _M_pwc;
#endif
};
size_t _M_len;
char _M_unused[16];
operator const char*() const { return _M_pc; }
#ifdef _GLIBCXX_USE_WCHAR_T
operator const wchar_t*() const { return _M_pwc; }
#endif
};
union {
__str_rep _M_str;
char _M_bytes[sizeof(__str_rep)];
};
using __dtor_func = void(*)(void*);
__dtor_func _M_dtor = nullptr;
#if _GLIBCXX_USE_CXX11_ABI
// SSO strings overlay the entire __str_rep structure.
static_assert(sizeof(std::string) == sizeof(__str_rep),
"std::string changed size!");
#else
// COW strings overlay just the pointer, the length is stored manually.
static_assert(sizeof(std::string) == sizeof(__str_rep::_M_p),
"std::string changed size!");
#endif
# ifdef _GLIBCXX_USE_WCHAR_T
static_assert(sizeof(std::wstring) == sizeof(std::string),
"std::wstring and std::string are different sizes!");
# endif
public:
__any_string() = default;
~__any_string() { if (_M_dtor) _M_dtor(_M_bytes); }
__any_string(const __any_string&) = delete;
__any_string& operator=(const __any_string&) = delete;
// Store a string (and its length if needed) in the buffer and
// set _M_dtor to the function that runs the right destructor.
template
__any_string&
operator=(const basic_string& s)
{
if (_M_dtor)
_M_dtor(_M_bytes);
::new(_M_bytes) basic_string(s);
#if ! _GLIBCXX_USE_CXX11_ABI
_M_str._M_len = s.length();
#endif
_M_dtor = __destroy_string;
return *this;
}
// Create a new string with a copy of the characters in the stored string.
// The returned object will match the caller's string ABI, even when the
// stored string doesn't.
template
_GLIBCXX_DEFAULT_ABI_TAG
operator basic_string() const
{
if (!_M_dtor)
__throw_logic_error("uninitialized __any_string");
return basic_string(static_cast(_M_str), _M_str._M_len);
}
};
// This file is compiled twice, with and without this macro defined.
// Define tag types to distinguish between the two cases and to allow
// overloading on the tag.
using current_abi = __bool_constant<_GLIBCXX_USE_CXX11_ABI>;
using other_abi = __bool_constant;
using facet = locale::facet;
// Declare the functions that shims defined in this file will call to
// perform work in the context of the other ABI.
// These will be defined when this file is recompiled for the other ABI
// (at which point what is now "current_abi" will become "other_abi").
template
void
__numpunct_fill_cache(other_abi, const facet*, __numpunct_cache*);
template
int
__collate_compare(other_abi, const facet*, const C*, const C*,
const C*, const C*);
template
void
__collate_transform(other_abi, const facet*, __any_string&,
const C*, const C*);
template
time_base::dateorder
__time_get_dateorder(other_abi, const facet* f);
template
istreambuf_iterator
__time_get(other_abi, const facet* f,
istreambuf_iterator beg, istreambuf_iterator end,
ios_base& io, ios_base::iostate& err, tm* t, char which);
template
void
__moneypunct_fill_cache(other_abi, const facet*,
__moneypunct_cache*);
template
istreambuf_iterator
__money_get(other_abi, const facet*,
istreambuf_iterator, istreambuf_iterator,
bool, ios_base&, ios_base::iostate&,
long double*, __any_string*);
template
ostreambuf_iterator
__money_put(other_abi, const facet*, ostreambuf_iterator, bool,
ios_base&, C, long double, const __any_string*);
template
messages_base::catalog
__messages_open(other_abi, const facet*, const char*, size_t,
const locale&);
template
void
__messages_get(other_abi, const facet*, __any_string&,
messages_base::catalog, int, int, const C*, size_t);
template
void
__messages_close(other_abi, const facet*, messages_base::catalog);
#pragma GCC diagnostic push
// Suppress -Wabi=2 warnings due to empty struct argument passing changes.
// TODO This should use -Wabi=12 but that currently fails (PR c++/87611).
#pragma GCC diagnostic ignored "-Wabi"
namespace // unnamed
{
struct __shim_accessor : facet
{
using facet::__shim; // Redeclare protected member as public.
};
using __shim = __shim_accessor::__shim;
template
struct numpunct_shim : std::numpunct<_CharT>, __shim
{
typedef typename numpunct<_CharT>::__cache_type __cache_type;
// f must point to a type derived from numpunct[abi:other]
numpunct_shim(const facet* f, __cache_type* c = new __cache_type)
: std::numpunct<_CharT>(c), __shim(f), _M_cache(c)
{
__numpunct_fill_cache(other_abi{}, f, c);
}
~numpunct_shim()
{
// Stop GNU locale's ~numpunct() from freeing the cached string.
_M_cache->_M_grouping_size = 0;
}
// No need to override any virtual functions, the base definitions
// will return the cached data.
__cache_type* _M_cache;
};
template
struct collate_shim : std::collate<_CharT>, __shim
{
typedef basic_string<_CharT> string_type;
// f must point to a type derived from collate[abi:other]
collate_shim(const facet* f) : __shim(f) { }
virtual int
do_compare(const _CharT* lo1, const _CharT* hi1,
const _CharT* lo2, const _CharT* hi2) const
{
return __collate_compare(other_abi{}, _M_get(),
lo1, hi1, lo2, hi2);
}
virtual string_type
do_transform(const _CharT* lo, const _CharT* hi) const
{
__any_string st;
__collate_transform(other_abi{}, _M_get(), st, lo, hi);
return st;
}
};
template
struct time_get_shim : std::time_get<_CharT>, __shim
{
typedef typename std::time_get<_CharT>::iter_type iter_type;
typedef typename std::time_get<_CharT>::char_type char_type;
// f must point to a type derived from time_get[abi:other]
time_get_shim(const facet* f) : __shim(f) { }
virtual time_base::dateorder
do_date_order() const
{ return __time_get_dateorder<_CharT>(other_abi{}, _M_get()); }
virtual iter_type
do_get_time(iter_type beg, iter_type end, ios_base& io,
ios_base::iostate& err, tm* t) const
{
return __time_get(other_abi{}, _M_get(), beg, end, io, err, t,
't');
}
virtual iter_type
do_get_date(iter_type beg, iter_type end, ios_base& io,
ios_base::iostate& err, tm* t) const
{
return __time_get(other_abi{}, _M_get(), beg, end, io, err, t,
'd');
}
virtual iter_type
do_get_weekday(iter_type beg, iter_type end, ios_base& io,
ios_base::iostate& err, tm* t) const
{
return __time_get(other_abi{}, _M_get(), beg, end, io, err, t,
'w');
}
virtual iter_type
do_get_monthname(iter_type beg, iter_type end, ios_base& io,
ios_base::iostate& err, tm* t) const
{
return __time_get(other_abi{}, _M_get(), beg, end, io, err, t,
'm');
}
virtual iter_type
do_get_year(iter_type beg, iter_type end, ios_base& io,
ios_base::iostate& err, tm* t) const
{
return __time_get(other_abi{}, _M_get(), beg, end, io, err, t,
'y');
}
};
template
struct moneypunct_shim : std::moneypunct<_CharT, _Intl>, __shim
{
typedef typename moneypunct<_CharT, _Intl>::__cache_type __cache_type;
// f must point to a type derived from moneypunct[abi:other]
moneypunct_shim(const facet* f, __cache_type* c = new __cache_type)
: std::moneypunct<_CharT, _Intl>(c), __shim(f), _M_cache(c)
{
__moneypunct_fill_cache(other_abi{}, f, c);
}
~moneypunct_shim()
{
// Stop GNU locale's ~moneypunct() from freeing the cached strings.
_M_cache->_M_grouping_size = 0;
_M_cache->_M_curr_symbol_size = 0;
_M_cache->_M_positive_sign_size = 0;
_M_cache->_M_negative_sign_size = 0;
}
// No need to override any virtual functions, the base definitions
// will return the cached data.
__cache_type* _M_cache;
};
template
struct money_get_shim : std::money_get<_CharT>, __shim
{
typedef typename std::money_get<_CharT>::iter_type iter_type;
typedef typename std::money_get<_CharT>::char_type char_type;
typedef typename std::money_get<_CharT>::string_type string_type;
// f must point to a type derived from money_get[abi:other]
money_get_shim(const facet* f) : __shim(f) { }
virtual iter_type
do_get(iter_type s, iter_type end, bool intl, ios_base& io,
ios_base::iostate& err, long double& units) const
{
ios_base::iostate err2 = ios_base::goodbit;
long double units2;
s = __money_get(other_abi{}, _M_get(), s, end, intl, io, err2,
&units2, nullptr);
if (err2 == ios_base::goodbit)
units = units2;
else
err = err2;
return s;
}
virtual iter_type
do_get(iter_type s, iter_type end, bool intl, ios_base& io,
ios_base::iostate& err, string_type& digits) const
{
__any_string st;
ios_base::iostate err2 = ios_base::goodbit;
s = __money_get(other_abi{}, _M_get(), s, end, intl, io, err2,
nullptr, &st);
if (err2 == ios_base::goodbit)
digits = st;
else
err = err2;
return s;
}
};
template
struct money_put_shim : std::money_put<_CharT>, __shim
{
typedef typename std::money_put<_CharT>::iter_type iter_type;
typedef typename std::money_put<_CharT>::char_type char_type;
typedef typename std::money_put<_CharT>::string_type string_type;
// f must point to a type derived from money_put[abi:other]
money_put_shim(const facet* f) : __shim(f) { }
virtual iter_type
do_put(iter_type s, bool intl, ios_base& io,
char_type fill, long double units) const
{
return __money_put(other_abi{}, _M_get(), s, intl, io, fill, units,
nullptr);
}
virtual iter_type
do_put(iter_type s, bool intl, ios_base& io,
char_type fill, const string_type& digits) const
{
__any_string st;
st = digits;
return __money_put(other_abi{}, _M_get(), s, intl, io, fill, 0.L,
&st);
}
};
template
struct messages_shim : std::messages<_CharT>, __shim
{
typedef messages_base::catalog catalog;
typedef basic_string<_CharT> string_type;
// f must point to a type derived from messages[abi:other]
messages_shim(const facet* f) : __shim(f) { }
virtual catalog
do_open(const basic_string& s, const locale& l) const
{
return __messages_open<_CharT>(other_abi{}, _M_get(),
s.c_str(), s.size(), l);
}
virtual string_type
do_get(catalog c, int set, int msgid, const string_type& dfault) const
{
__any_string st;
__messages_get(other_abi{}, _M_get(), st, c, set, msgid,
dfault.c_str(), dfault.size());
return st;
}
virtual void
do_close(catalog c) const
{
__messages_close<_CharT>(other_abi{}, _M_get(), c);
}
};
template class numpunct_shim;
template class collate_shim;
template class moneypunct_shim;
template class moneypunct_shim;
template class money_get_shim;
template class money_put_shim;
template class messages_shim;
#ifdef _GLIBCXX_USE_WCHAR_T
template class numpunct_shim;
template class collate_shim;
template class moneypunct_shim;
template class moneypunct_shim;
template class money_get_shim;
template class money_put_shim;
template class messages_shim;
#endif
template
inline size_t
__copy(const C*& dest, const basic_string& s)
{
auto len = s.length();
C* p = new C[len+1];
s.copy(p, len);
p[len] = '\0';
dest = p;
return len;
}
} // namespace
// Now define and instantiate the functions that will be called by the
// shim facets defined when this file is recompiled for the other ABI.
// Cache the values returned by the numpunct facet f.
// Sets c->_M_allocated so that the __numpunct_cache destructor will
// delete[] the strings allocated by this function.
template
void
__numpunct_fill_cache(current_abi, const facet* f, __numpunct_cache* c)
{
auto* m = static_cast*>(f);
c->_M_decimal_point = m->decimal_point();
c->_M_thousands_sep = m->thousands_sep();
c->_M_grouping = nullptr;
c->_M_truename = nullptr;
c->_M_falsename = nullptr;
// set _M_allocated so that if any allocation fails the previously
// allocated strings will be deleted in ~__numpunct_cache()
c->_M_allocated = true;
c->_M_grouping_size = __copy(c->_M_grouping, m->grouping());
c->_M_truename_size = __copy(c->_M_truename, m->truename());
c->_M_falsename_size = __copy(c->_M_falsename, m->falsename());
}
template void
__numpunct_fill_cache(current_abi, const facet*, __numpunct_cache*);
#ifdef _GLIBCXX_USE_WCHAR_T
template void
__numpunct_fill_cache(current_abi, const facet*, __numpunct_cache*);
#endif
template
int
__collate_compare(current_abi, const facet* f, const C* lo1, const C* hi1,
const C* lo2, const C* hi2)
{
return static_cast*>(f)->compare(lo1, hi1, lo2, hi2);
}
template int
__collate_compare(current_abi, const facet*, const char*, const char*,
const char*, const char*);
#ifdef _GLIBCXX_USE_WCHAR_T
template int
__collate_compare(current_abi, const facet*, const wchar_t*, const wchar_t*,
const wchar_t*, const wchar_t*);
#endif
template
void
__collate_transform(current_abi, const facet* f, __any_string& st,
const C* __lo, const C* __hi)
{
auto* c = static_cast*>(f);
st = c->transform(__lo, __hi);
}
template void
__collate_transform(current_abi, const facet*, __any_string&,
const char*, const char*);
#ifdef _GLIBCXX_USE_WCHAR_T
template void
__collate_transform(current_abi, const facet*, __any_string&,
const wchar_t*, const wchar_t*);
#endif
// Cache the values returned by the moneypunct facet, f.
// Sets c->_M_allocated so that the __moneypunct_cache destructor will
// delete[] the strings allocated by this function.
template
void
__moneypunct_fill_cache(current_abi, const facet* f,
__moneypunct_cache* c)
{
auto* m = static_cast*>(f);
c->_M_decimal_point = m->decimal_point();
c->_M_thousands_sep = m->thousands_sep();
c->_M_frac_digits = m->frac_digits();
c->_M_grouping = nullptr;
c->_M_curr_symbol = nullptr;
c->_M_positive_sign = nullptr;
c->_M_negative_sign = nullptr;
// Set _M_allocated so that if any allocation fails the previously
// allocated strings will be deleted in ~__moneypunct_cache().
c->_M_allocated = true;
c->_M_grouping_size = __copy(c->_M_grouping, m->grouping());
c->_M_curr_symbol_size = __copy(c->_M_curr_symbol, m->curr_symbol());
c->_M_positive_sign_size
= __copy(c->_M_positive_sign, m->positive_sign());
c->_M_negative_sign_size
= __copy(c->_M_negative_sign, m->negative_sign());
c->_M_pos_format = m->pos_format();
c->_M_neg_format = m->neg_format();
}
template void
__moneypunct_fill_cache(current_abi, const facet*,
__moneypunct_cache*);
template void
__moneypunct_fill_cache(current_abi, const facet*,
__moneypunct_cache*);
#ifdef _GLIBCXX_USE_WCHAR_T
template void
__moneypunct_fill_cache(current_abi, const facet*,
__moneypunct_cache*);
template void
__moneypunct_fill_cache(current_abi, const facet*,
__moneypunct_cache*);
#endif
template
messages_base::catalog
__messages_open(current_abi, const facet* f, const char* s, size_t n,
const locale& l)
{
auto* m = static_cast*>(f);
string str(s, n);
return m->open(str, l);
}
template messages_base::catalog
__messages_open(current_abi, const facet*, const char*, size_t,
const locale&);
#ifdef _GLIBCXX_USE_WCHAR_T
template messages_base::catalog
__messages_open(current_abi, const facet*, const char*, size_t,
const locale&);
#endif
template
void
__messages_get(current_abi, const facet* f, __any_string& st,
messages_base::catalog c, int set, int msgid,
const C* s, size_t n)
{
auto* m = static_cast*>(f);
st = m->get(c, set, msgid, basic_string(s, n));
}
template void
__messages_get(current_abi, const facet*, __any_string&,
messages_base::catalog, int, int, const char*, size_t);
#ifdef _GLIBCXX_USE_WCHAR_T
template void
__messages_get(current_abi, const facet*, __any_string&,
messages_base::catalog, int, int, const wchar_t*, size_t);
#endif
template
void
__messages_close(current_abi, const facet* f, messages_base::catalog c)
{
static_cast*>(f)->close(c);
}
template void
__messages_close(current_abi, const facet*, messages_base::catalog c);
#ifdef _GLIBCXX_USE_WCHAR_T
template void
__messages_close(current_abi, const facet*,
messages_base::catalog c);
#endif
template
time_base::dateorder
__time_get_dateorder(current_abi, const facet* f)
{ return static_cast*>(f)->date_order(); }
template time_base::dateorder
__time_get_dateorder(current_abi, const facet*);
#ifdef _GLIBCXX_USE_WCHAR_T
template time_base::dateorder
__time_get_dateorder(current_abi, const facet*);
#endif
template
istreambuf_iterator
__time_get(current_abi, const facet* f,
istreambuf_iterator beg, istreambuf_iterator end,
ios_base& io, ios_base::iostate& err, tm* t, char which)
{
auto* g = static_cast*>(f);
switch(which)
{
case 't':
return g->get_time(beg, end, io, err, t);
case 'd':
return g->get_date(beg, end, io, err, t);
case 'w':
return g->get_weekday(beg, end, io, err, t);
case 'm':
return g->get_monthname(beg, end, io, err, t);
case 'y':
return g->get_year(beg, end, io, err, t);
default:
__builtin_unreachable();
}
}
template istreambuf_iterator
__time_get(current_abi, const facet*,
istreambuf_iterator, istreambuf_iterator,
ios_base&, ios_base::iostate&, tm*, char);
#ifdef _GLIBCXX_USE_WCHAR_T
template istreambuf_iterator
__time_get(current_abi, const facet*,
istreambuf_iterator, istreambuf_iterator,
ios_base&, ios_base::iostate&, tm*, char);
#endif
template
istreambuf_iterator
__money_get(current_abi, const facet* f,
istreambuf_iterator s, istreambuf_iterator end,
bool intl, ios_base& str, ios_base::iostate& err,
long double* units, __any_string* digits)
{
auto* m = static_cast*>(f);
if (units)
return m->get(s, end, intl, str, err, *units);
basic_string digits2;
s = m->get(s, end, intl, str, err, digits2);
if (err == ios_base::goodbit)
*digits = digits2;
return s;
}
template istreambuf_iterator
__money_get(current_abi, const facet*,
istreambuf_iterator, istreambuf_iterator,
bool, ios_base&, ios_base::iostate&,
long double*, __any_string*);
#ifdef _GLIBCXX_USE_WCHAR_T
template istreambuf_iterator
__money_get(current_abi, const facet*,
istreambuf_iterator, istreambuf_iterator,
bool, ios_base&, ios_base::iostate&,
long double*, __any_string*);
#endif
template
ostreambuf_iterator
__money_put(current_abi, const facet* f, ostreambuf_iterator s,
bool intl, ios_base& io, C fill, long double units,
const __any_string* digits)
{
auto* m = static_cast*>(f);
if (digits)
return m->put(s, intl, io, fill, *digits);
else
return m->put(s, intl, io, fill, units);
}
#pragma GCC diagnostic pop
template ostreambuf_iterator
__money_put(current_abi, const facet*, ostreambuf_iterator,
bool, ios_base&, char, long double, const __any_string*);
#ifdef _GLIBCXX_USE_WCHAR_T
template ostreambuf_iterator
__money_put(current_abi, const facet*, ostreambuf_iterator,
bool, ios_base&, wchar_t, long double, const __any_string*);
#endif
} // namespace __facet_shims
// Create a new shim facet of type WHICH that forwards calls to F.
// F is the replacement facet provided by the user, WHICH is the ID of
// F's "other ABI twin" which we are replacing with a shim.
const locale::facet*
#if _GLIBCXX_USE_CXX11_ABI
locale::facet::_M_sso_shim(const locale::id* which) const
#else
locale::facet::_M_cow_shim(const locale::id* which) const
#endif
{
using namespace __facet_shims;
#if __cpp_rtti
// If this is already a shim just use its underlying facet.
if (auto* p = dynamic_cast(this))
return p->_M_get();
#endif
if (which == &numpunct::id)
return new numpunct_shim{this};
if (which == &std::collate::id)
return new collate_shim{this};
if (which == &time_get::id)
return new time_get_shim{this};
if (which == &money_get::id)
return new money_get_shim{this};
if (which == &money_put::id)
return new money_put_shim{this};
if (which == &moneypunct::id)
return new moneypunct_shim{this};
if (which == &moneypunct::id)
return new moneypunct_shim{this};
if (which == &std::messages::id)
return new messages_shim{this};
#ifdef _GLIBCXX_USE_WCHAR_T
if (which == &numpunct::id)
return new numpunct_shim{this};
if (which == &std::collate::id)
return new collate_shim{this};
if (which == &time_get::id)
return new time_get_shim{this};
if (which == &money_get::id)
return new money_get_shim{this};
if (which == &money_put::id)
return new money_put_shim{this};
if (which == &moneypunct::id)
return new moneypunct_shim{this};
if (which == &moneypunct::id)
return new moneypunct_shim{this};
if (which == &std::messages::id)
return new messages_shim{this};
#endif
__throw_logic_error("cannot create shim for unknown locale::facet");
}
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std