Summary:
This patch has been a long time coming (Thanks @eugenis). It changes `_LIBCPP_INLINE_VISIBILITY` to use `__attribute__((internal_linkage))` instead of `__attribute__((visibility("hidden"), always_inline))`.
The point of `_LIBCPP_INLINE_VISIBILITY` is to prevent inline functions from being exported from both the libc++ library and from user libraries. This helps libc++ better manage it's ABI.
Previously this was done by forcing inlining and modifying the symbols visibility. However inlining isn't guaranteed and symbol visibility only affects shared libraries making this an imperfect solution. `internal_linkage` improves this situation by making all symbols local to the TU they are emitted in, regardless of inlining or visibility. IIRC the effect of applying `__attribute__((internal_linkage))` to an inline function is the same as applying `static`.
For more information about the attribute see: http://lists.llvm.org/pipermail/cfe-dev/2015-October/045580.html
Most of the work for this patch was done by @eugenis.
Reviewers: mclow.lists, eugenis
Subscribers: eugenis, cfe-commits
Differential Revision: https://reviews.llvm.org/D24642
llvm-svn: 282345
552 lines
13 KiB
C++
552 lines
13 KiB
C++
//===------------------------- string.cpp ---------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
|
// Source Licenses. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "string"
|
|
#include "cstdlib"
|
|
#include "cwchar"
|
|
#include "cerrno"
|
|
#include "limits"
|
|
#include "stdexcept"
|
|
#ifdef _LIBCPP_MSVCRT
|
|
#include "support/win32/support.h"
|
|
#endif // _LIBCPP_MSVCRT
|
|
#include <stdio.h>
|
|
|
|
_LIBCPP_BEGIN_NAMESPACE_STD
|
|
|
|
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS __basic_string_common<true>;
|
|
|
|
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_string<char>;
|
|
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_string<wchar_t>;
|
|
|
|
template
|
|
string
|
|
operator+<char, char_traits<char>, allocator<char> >(char const*, string const&);
|
|
|
|
// These external instantiations are required to maintain dylib compatibility
|
|
// for ABI v1 when using __attribute__((internal_linkage)) as opposed to
|
|
// __attribute__((visibility("hidden"), always_inline)).
|
|
#if _LIBCPP_ABI_EXTERN_TEMPLATE_SYMBOLS_VERSION == 1
|
|
template basic_string<char>::iterator
|
|
basic_string<char>::insert(basic_string<char>::const_iterator,
|
|
char const *, char const *);
|
|
|
|
template basic_string<wchar_t>::iterator
|
|
basic_string<wchar_t>::insert(basic_string<wchar_t>::const_iterator,
|
|
wchar_t const *, wchar_t const *);
|
|
|
|
template basic_string<char> &
|
|
basic_string<char>::replace(basic_string<char>::const_iterator,
|
|
basic_string<char>::const_iterator,
|
|
char const *, char const *);
|
|
|
|
template basic_string<wchar_t> &
|
|
basic_string<wchar_t>::replace(basic_string<wchar_t>::const_iterator,
|
|
basic_string<wchar_t>::const_iterator,
|
|
wchar_t const *, wchar_t const *);
|
|
#endif // _LIBCPP_ABI_EXTERN_TEMPLATE_SYMBOLS_VERSION
|
|
|
|
namespace
|
|
{
|
|
|
|
template<typename T>
|
|
inline
|
|
void throw_helper( const string& msg )
|
|
{
|
|
#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
throw T( msg );
|
|
#else
|
|
fprintf(stderr, "%s\n", msg.c_str());
|
|
_VSTD::abort();
|
|
#endif
|
|
}
|
|
|
|
inline
|
|
void throw_from_string_out_of_range( const string& func )
|
|
{
|
|
throw_helper<out_of_range>(func + ": out of range");
|
|
}
|
|
|
|
inline
|
|
void throw_from_string_invalid_arg( const string& func )
|
|
{
|
|
throw_helper<invalid_argument>(func + ": no conversion");
|
|
}
|
|
|
|
// as_integer
|
|
|
|
template<typename V, typename S, typename F>
|
|
inline
|
|
V
|
|
as_integer_helper(const string& func, const S& str, size_t* idx, int base, F f)
|
|
{
|
|
typename S::value_type* ptr = nullptr;
|
|
const typename S::value_type* const p = str.c_str();
|
|
typename remove_reference<decltype(errno)>::type errno_save = errno;
|
|
errno = 0;
|
|
V r = f(p, &ptr, base);
|
|
swap(errno, errno_save);
|
|
if (errno_save == ERANGE)
|
|
throw_from_string_out_of_range(func);
|
|
if (ptr == p)
|
|
throw_from_string_invalid_arg(func);
|
|
if (idx)
|
|
*idx = static_cast<size_t>(ptr - p);
|
|
return r;
|
|
}
|
|
|
|
template<typename V, typename S>
|
|
inline
|
|
V
|
|
as_integer(const string& func, const S& s, size_t* idx, int base);
|
|
|
|
// string
|
|
template<>
|
|
inline
|
|
int
|
|
as_integer(const string& func, const string& s, size_t* idx, int base )
|
|
{
|
|
// Use long as no Standard string to integer exists.
|
|
long r = as_integer_helper<long>( func, s, idx, base, strtol );
|
|
if (r < numeric_limits<int>::min() || numeric_limits<int>::max() < r)
|
|
throw_from_string_out_of_range(func);
|
|
return static_cast<int>(r);
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
long
|
|
as_integer(const string& func, const string& s, size_t* idx, int base )
|
|
{
|
|
return as_integer_helper<long>( func, s, idx, base, strtol );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
unsigned long
|
|
as_integer( const string& func, const string& s, size_t* idx, int base )
|
|
{
|
|
return as_integer_helper<unsigned long>( func, s, idx, base, strtoul );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
long long
|
|
as_integer( const string& func, const string& s, size_t* idx, int base )
|
|
{
|
|
return as_integer_helper<long long>( func, s, idx, base, strtoll );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
unsigned long long
|
|
as_integer( const string& func, const string& s, size_t* idx, int base )
|
|
{
|
|
return as_integer_helper<unsigned long long>( func, s, idx, base, strtoull );
|
|
}
|
|
|
|
// wstring
|
|
template<>
|
|
inline
|
|
int
|
|
as_integer( const string& func, const wstring& s, size_t* idx, int base )
|
|
{
|
|
// Use long as no Stantard string to integer exists.
|
|
long r = as_integer_helper<long>( func, s, idx, base, wcstol );
|
|
if (r < numeric_limits<int>::min() || numeric_limits<int>::max() < r)
|
|
throw_from_string_out_of_range(func);
|
|
return static_cast<int>(r);
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
long
|
|
as_integer( const string& func, const wstring& s, size_t* idx, int base )
|
|
{
|
|
return as_integer_helper<long>( func, s, idx, base, wcstol );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
unsigned long
|
|
as_integer( const string& func, const wstring& s, size_t* idx, int base )
|
|
{
|
|
return as_integer_helper<unsigned long>( func, s, idx, base, wcstoul );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
long long
|
|
as_integer( const string& func, const wstring& s, size_t* idx, int base )
|
|
{
|
|
return as_integer_helper<long long>( func, s, idx, base, wcstoll );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
unsigned long long
|
|
as_integer( const string& func, const wstring& s, size_t* idx, int base )
|
|
{
|
|
return as_integer_helper<unsigned long long>( func, s, idx, base, wcstoull );
|
|
}
|
|
|
|
// as_float
|
|
|
|
template<typename V, typename S, typename F>
|
|
inline
|
|
V
|
|
as_float_helper(const string& func, const S& str, size_t* idx, F f )
|
|
{
|
|
typename S::value_type* ptr = nullptr;
|
|
const typename S::value_type* const p = str.c_str();
|
|
typename remove_reference<decltype(errno)>::type errno_save = errno;
|
|
errno = 0;
|
|
V r = f(p, &ptr);
|
|
swap(errno, errno_save);
|
|
if (errno_save == ERANGE)
|
|
throw_from_string_out_of_range(func);
|
|
if (ptr == p)
|
|
throw_from_string_invalid_arg(func);
|
|
if (idx)
|
|
*idx = static_cast<size_t>(ptr - p);
|
|
return r;
|
|
}
|
|
|
|
template<typename V, typename S>
|
|
inline
|
|
V as_float( const string& func, const S& s, size_t* idx = nullptr );
|
|
|
|
template<>
|
|
inline
|
|
float
|
|
as_float( const string& func, const string& s, size_t* idx )
|
|
{
|
|
return as_float_helper<float>( func, s, idx, strtof );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
double
|
|
as_float(const string& func, const string& s, size_t* idx )
|
|
{
|
|
return as_float_helper<double>( func, s, idx, strtod );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
long double
|
|
as_float( const string& func, const string& s, size_t* idx )
|
|
{
|
|
return as_float_helper<long double>( func, s, idx, strtold );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
float
|
|
as_float( const string& func, const wstring& s, size_t* idx )
|
|
{
|
|
return as_float_helper<float>( func, s, idx, wcstof );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
double
|
|
as_float( const string& func, const wstring& s, size_t* idx )
|
|
{
|
|
return as_float_helper<double>( func, s, idx, wcstod );
|
|
}
|
|
|
|
template<>
|
|
inline
|
|
long double
|
|
as_float( const string& func, const wstring& s, size_t* idx )
|
|
{
|
|
return as_float_helper<long double>( func, s, idx, wcstold );
|
|
}
|
|
|
|
} // unnamed namespace
|
|
|
|
int
|
|
stoi(const string& str, size_t* idx, int base)
|
|
{
|
|
return as_integer<int>( "stoi", str, idx, base );
|
|
}
|
|
|
|
int
|
|
stoi(const wstring& str, size_t* idx, int base)
|
|
{
|
|
return as_integer<int>( "stoi", str, idx, base );
|
|
}
|
|
|
|
long
|
|
stol(const string& str, size_t* idx, int base)
|
|
{
|
|
return as_integer<long>( "stol", str, idx, base );
|
|
}
|
|
|
|
long
|
|
stol(const wstring& str, size_t* idx, int base)
|
|
{
|
|
return as_integer<long>( "stol", str, idx, base );
|
|
}
|
|
|
|
unsigned long
|
|
stoul(const string& str, size_t* idx, int base)
|
|
{
|
|
return as_integer<unsigned long>( "stoul", str, idx, base );
|
|
}
|
|
|
|
unsigned long
|
|
stoul(const wstring& str, size_t* idx, int base)
|
|
{
|
|
return as_integer<unsigned long>( "stoul", str, idx, base );
|
|
}
|
|
|
|
long long
|
|
stoll(const string& str, size_t* idx, int base)
|
|
{
|
|
return as_integer<long long>( "stoll", str, idx, base );
|
|
}
|
|
|
|
long long
|
|
stoll(const wstring& str, size_t* idx, int base)
|
|
{
|
|
return as_integer<long long>( "stoll", str, idx, base );
|
|
}
|
|
|
|
unsigned long long
|
|
stoull(const string& str, size_t* idx, int base)
|
|
{
|
|
return as_integer<unsigned long long>( "stoull", str, idx, base );
|
|
}
|
|
|
|
unsigned long long
|
|
stoull(const wstring& str, size_t* idx, int base)
|
|
{
|
|
return as_integer<unsigned long long>( "stoull", str, idx, base );
|
|
}
|
|
|
|
float
|
|
stof(const string& str, size_t* idx)
|
|
{
|
|
return as_float<float>( "stof", str, idx );
|
|
}
|
|
|
|
float
|
|
stof(const wstring& str, size_t* idx)
|
|
{
|
|
return as_float<float>( "stof", str, idx );
|
|
}
|
|
|
|
double
|
|
stod(const string& str, size_t* idx)
|
|
{
|
|
return as_float<double>( "stod", str, idx );
|
|
}
|
|
|
|
double
|
|
stod(const wstring& str, size_t* idx)
|
|
{
|
|
return as_float<double>( "stod", str, idx );
|
|
}
|
|
|
|
long double
|
|
stold(const string& str, size_t* idx)
|
|
{
|
|
return as_float<long double>( "stold", str, idx );
|
|
}
|
|
|
|
long double
|
|
stold(const wstring& str, size_t* idx)
|
|
{
|
|
return as_float<long double>( "stold", str, idx );
|
|
}
|
|
|
|
// to_string
|
|
|
|
namespace
|
|
{
|
|
|
|
// as_string
|
|
|
|
template<typename S, typename P, typename V >
|
|
inline
|
|
S
|
|
as_string(P sprintf_like, S s, const typename S::value_type* fmt, V a)
|
|
{
|
|
typedef typename S::size_type size_type;
|
|
size_type available = s.size();
|
|
while (true)
|
|
{
|
|
int status = sprintf_like(&s[0], available + 1, fmt, a);
|
|
if ( status >= 0 )
|
|
{
|
|
size_type used = static_cast<size_type>(status);
|
|
if ( used <= available )
|
|
{
|
|
s.resize( used );
|
|
break;
|
|
}
|
|
available = used; // Assume this is advice of how much space we need.
|
|
}
|
|
else
|
|
available = available * 2 + 1;
|
|
s.resize(available);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
template <class S, class V, bool = is_floating_point<V>::value>
|
|
struct initial_string;
|
|
|
|
template <class V, bool b>
|
|
struct initial_string<string, V, b>
|
|
{
|
|
string
|
|
operator()() const
|
|
{
|
|
string s;
|
|
s.resize(s.capacity());
|
|
return s;
|
|
}
|
|
};
|
|
|
|
template <class V>
|
|
struct initial_string<wstring, V, false>
|
|
{
|
|
wstring
|
|
operator()() const
|
|
{
|
|
const size_t n = (numeric_limits<unsigned long long>::digits / 3)
|
|
+ ((numeric_limits<unsigned long long>::digits % 3) != 0)
|
|
+ 1;
|
|
wstring s(n, wchar_t());
|
|
s.resize(s.capacity());
|
|
return s;
|
|
}
|
|
};
|
|
|
|
template <class V>
|
|
struct initial_string<wstring, V, true>
|
|
{
|
|
wstring
|
|
operator()() const
|
|
{
|
|
wstring s(20, wchar_t());
|
|
s.resize(s.capacity());
|
|
return s;
|
|
}
|
|
};
|
|
|
|
typedef int (*wide_printf)(wchar_t* __restrict, size_t, const wchar_t*__restrict, ...);
|
|
|
|
inline
|
|
wide_printf
|
|
get_swprintf()
|
|
{
|
|
#ifndef _LIBCPP_MSVCRT
|
|
return swprintf;
|
|
#else
|
|
return static_cast<int (__cdecl*)(wchar_t* __restrict, size_t, const wchar_t*__restrict, ...)>(swprintf);
|
|
#endif
|
|
}
|
|
|
|
} // unnamed namespace
|
|
|
|
string to_string(int val)
|
|
{
|
|
return as_string(snprintf, initial_string<string, int>()(), "%d", val);
|
|
}
|
|
|
|
string to_string(unsigned val)
|
|
{
|
|
return as_string(snprintf, initial_string<string, unsigned>()(), "%u", val);
|
|
}
|
|
|
|
string to_string(long val)
|
|
{
|
|
return as_string(snprintf, initial_string<string, long>()(), "%ld", val);
|
|
}
|
|
|
|
string to_string(unsigned long val)
|
|
{
|
|
return as_string(snprintf, initial_string<string, unsigned long>()(), "%lu", val);
|
|
}
|
|
|
|
string to_string(long long val)
|
|
{
|
|
return as_string(snprintf, initial_string<string, long long>()(), "%lld", val);
|
|
}
|
|
|
|
string to_string(unsigned long long val)
|
|
{
|
|
return as_string(snprintf, initial_string<string, unsigned long long>()(), "%llu", val);
|
|
}
|
|
|
|
string to_string(float val)
|
|
{
|
|
return as_string(snprintf, initial_string<string, float>()(), "%f", val);
|
|
}
|
|
|
|
string to_string(double val)
|
|
{
|
|
return as_string(snprintf, initial_string<string, double>()(), "%f", val);
|
|
}
|
|
|
|
string to_string(long double val)
|
|
{
|
|
return as_string(snprintf, initial_string<string, long double>()(), "%Lf", val);
|
|
}
|
|
|
|
wstring to_wstring(int val)
|
|
{
|
|
return as_string(get_swprintf(), initial_string<wstring, int>()(), L"%d", val);
|
|
}
|
|
|
|
wstring to_wstring(unsigned val)
|
|
{
|
|
return as_string(get_swprintf(), initial_string<wstring, unsigned>()(), L"%u", val);
|
|
}
|
|
|
|
wstring to_wstring(long val)
|
|
{
|
|
return as_string(get_swprintf(), initial_string<wstring, long>()(), L"%ld", val);
|
|
}
|
|
|
|
wstring to_wstring(unsigned long val)
|
|
{
|
|
return as_string(get_swprintf(), initial_string<wstring, unsigned long>()(), L"%lu", val);
|
|
}
|
|
|
|
wstring to_wstring(long long val)
|
|
{
|
|
return as_string(get_swprintf(), initial_string<wstring, long long>()(), L"%lld", val);
|
|
}
|
|
|
|
wstring to_wstring(unsigned long long val)
|
|
{
|
|
return as_string(get_swprintf(), initial_string<wstring, unsigned long long>()(), L"%llu", val);
|
|
}
|
|
|
|
wstring to_wstring(float val)
|
|
{
|
|
return as_string(get_swprintf(), initial_string<wstring, float>()(), L"%f", val);
|
|
}
|
|
|
|
wstring to_wstring(double val)
|
|
{
|
|
return as_string(get_swprintf(), initial_string<wstring, double>()(), L"%f", val);
|
|
}
|
|
|
|
wstring to_wstring(long double val)
|
|
{
|
|
return as_string(get_swprintf(), initial_string<wstring, long double>()(), L"%Lf", val);
|
|
}
|
|
_LIBCPP_END_NAMESPACE_STD
|