Skip to content

Commit aeecef0

Browse files
committed
[libc++] Remove default definition of std::char_traits
This patch removes the base template implementation for std::char_traits. If my reading of http://eel.is/c++draft/char.traits is correct, the Standard mandates that the library provides specializations for several types like char and wchar_t, but not any implementation in the base template. Indeed, such an implementation is bound to be incorrect for most types anyways, since things like `eof()` and `int_type` will definitely have to be customized. Since the base template implementation should not have worked for anyone, this shouldn't be a breaking change (I expect that anyone defining a custom character type today will already have to provide their own specialization of char_traits). However, since we're aware of some users of char_traits for unsigned char and signed char, we're keeping those two specializations around for two releases to give people some time to migrate. Differential Revision: https://reviews.llvm.org/D138307
1 parent 647ddc0 commit aeecef0

File tree

6 files changed

+459
-136
lines changed

6 files changed

+459
-136
lines changed

libcxx/docs/ReleaseNotes.rst

+11
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,19 @@ Deprecations and Removals
100100
- The ``_LIBCPP_DEBUG`` macro is not honored anymore, and it is an error to try to use it. Please migrate to
101101
``_LIBCPP_ENABLE_DEBUG_MODE`` instead.
102102

103+
- A base template for ``std::char_traits`` is not provided anymore. The Standard mandates that the library
104+
provides specializations for several types like ``char`` and ``wchar_t``, which libc++ does. However, libc++
105+
used to additionally provide a default implementation for ``std::char_traits<T>`` for arbitrary ``T``. Not
106+
only does the Standard not mandate that one is provided, but such an implementation is bound to be incorrect
107+
for some types, so it has been removed. As an exception, ``std::char_traits<unsigned char>`` and
108+
``std::char_traits<signed char>`` are kept for a limited period of time and marked as deprecated to let people
109+
move off of those, since we know there were some users of those. They will be removed in LLVM 18.
110+
103111
Upcoming Deprecations and Removals
104112
----------------------------------
113+
- The specializations of ``std::char_traits`` for ``unsigned char`` and ``signed char`` are provided until
114+
LLVM 18. Those non-standard specializations are provided for a transition period and marked as deprecated
115+
but will be removed in the future.
105116

106117
API Changes
107118
-----------

libcxx/include/__string/char_traits.h

+224-124
Original file line numberDiff line numberDiff line change
@@ -39,132 +39,37 @@ _LIBCPP_PUSH_MACROS
3939
_LIBCPP_BEGIN_NAMESPACE_STD
4040

4141
template <class _CharT>
42-
struct _LIBCPP_TEMPLATE_VIS char_traits
42+
struct char_traits;
43+
/*
44+
The Standard does not define the base template for char_traits because it is impossible to provide
45+
a correct definition for arbitrary character types. Instead, it requires implementations to provide
46+
specializations for predefined character types like `char`, `wchar_t` and others. We provide this as
47+
exposition-only to document what members a char_traits specialization should provide:
4348
{
4449
using char_type = _CharT;
45-
using int_type = int;
46-
using off_type = streamoff;
47-
using pos_type = streampos;
48-
using state_type = mbstate_t;
49-
50-
static inline void _LIBCPP_CONSTEXPR_SINCE_CXX17
51-
assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
52-
static inline _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT
53-
{return __c1 == __c2;}
54-
static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
55-
{return __c1 < __c2;}
56-
57-
static _LIBCPP_CONSTEXPR_SINCE_CXX17
58-
int compare(const char_type* __s1, const char_type* __s2, size_t __n);
59-
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
60-
size_t length(const char_type* __s);
61-
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
62-
const char_type* find(const char_type* __s, size_t __n, const char_type& __a);
63-
static _LIBCPP_CONSTEXPR_SINCE_CXX20
64-
char_type* move(char_type* __s1, const char_type* __s2, size_t __n);
65-
_LIBCPP_INLINE_VISIBILITY
66-
static _LIBCPP_CONSTEXPR_SINCE_CXX20
67-
char_type* copy(char_type* __s1, const char_type* __s2, size_t __n);
68-
_LIBCPP_INLINE_VISIBILITY
69-
static _LIBCPP_CONSTEXPR_SINCE_CXX20
70-
char_type* assign(char_type* __s, size_t __n, char_type __a);
71-
72-
static inline _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT
73-
{return eq_int_type(__c, eof()) ? ~eof() : __c;}
74-
static inline _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT
75-
{return char_type(__c);}
76-
static inline _LIBCPP_CONSTEXPR int_type to_int_type(char_type __c) _NOEXCEPT
77-
{return int_type(__c);}
78-
static inline _LIBCPP_CONSTEXPR bool eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT
79-
{return __c1 == __c2;}
80-
static inline _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT
81-
{return int_type(EOF);}
50+
using int_type = ...;
51+
using off_type = ...;
52+
using pos_type = ...;
53+
using state_type = ...;
54+
55+
static void assign(char_type&, const char_type&);
56+
static bool eq(char_type, char_type);
57+
static bool lt(char_type, char_type);
58+
59+
static int compare(const char_type*, const char_type*, size_t);
60+
static size_t length(const char_type*);
61+
static const char_type* find(const char_type*, size_t, const char_type&);
62+
static char_type* move(char_type*, const char_type*, size_t);
63+
static char_type* copy(char_type*, const char_type* __s2, size_t);
64+
static char_type* assign(char_type*, size_t, char_type);
65+
66+
static int_type not_eof(int_type);
67+
static char_type to_char_type(int_type);
68+
static int_type to_int_type(char_type);
69+
static bool eq_int_type(int_type, int_type);
70+
static int_type eof();
8271
};
83-
84-
template <class _CharT>
85-
_LIBCPP_CONSTEXPR_SINCE_CXX17 int
86-
char_traits<_CharT>::compare(const char_type* __s1, const char_type* __s2, size_t __n)
87-
{
88-
for (; __n; --__n, ++__s1, ++__s2)
89-
{
90-
if (lt(*__s1, *__s2))
91-
return -1;
92-
if (lt(*__s2, *__s1))
93-
return 1;
94-
}
95-
return 0;
96-
}
97-
98-
template <class _CharT>
99-
inline
100-
_LIBCPP_CONSTEXPR_SINCE_CXX17 size_t
101-
char_traits<_CharT>::length(const char_type* __s)
102-
{
103-
size_t __len = 0;
104-
for (; !eq(*__s, char_type(0)); ++__s)
105-
++__len;
106-
return __len;
107-
}
108-
109-
template <class _CharT>
110-
inline
111-
_LIBCPP_CONSTEXPR_SINCE_CXX17 const _CharT*
112-
char_traits<_CharT>::find(const char_type* __s, size_t __n, const char_type& __a)
113-
{
114-
for (; __n; --__n)
115-
{
116-
if (eq(*__s, __a))
117-
return __s;
118-
++__s;
119-
}
120-
return nullptr;
121-
}
122-
123-
template <class _CharT>
124-
_LIBCPP_CONSTEXPR_SINCE_CXX20 _CharT*
125-
char_traits<_CharT>::move(char_type* __s1, const char_type* __s2, size_t __n)
126-
{
127-
if (__n == 0) return __s1;
128-
char_type* __r = __s1;
129-
if (__s1 < __s2)
130-
{
131-
for (; __n; --__n, ++__s1, ++__s2)
132-
assign(*__s1, *__s2);
133-
}
134-
else if (__s2 < __s1)
135-
{
136-
__s1 += __n;
137-
__s2 += __n;
138-
for (; __n; --__n)
139-
assign(*--__s1, *--__s2);
140-
}
141-
return __r;
142-
}
143-
144-
template <class _CharT>
145-
inline _LIBCPP_CONSTEXPR_SINCE_CXX20
146-
_CharT*
147-
char_traits<_CharT>::copy(char_type* __s1, const char_type* __s2, size_t __n)
148-
{
149-
if (!__libcpp_is_constant_evaluated()) {
150-
_LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range");
151-
}
152-
char_type* __r = __s1;
153-
for (; __n; --__n, ++__s1, ++__s2)
154-
assign(*__s1, *__s2);
155-
return __r;
156-
}
157-
158-
template <class _CharT>
159-
inline _LIBCPP_CONSTEXPR_SINCE_CXX20
160-
_CharT*
161-
char_traits<_CharT>::assign(char_type* __s, size_t __n, char_type __a)
162-
{
163-
char_type* __r = __s;
164-
for (; __n; --__n, ++__s)
165-
assign(*__s, __a);
166-
return __r;
167-
}
72+
*/
16873

16974
template <class _CharT>
17075
_LIBCPP_HIDE_FROM_ABI static inline _LIBCPP_CONSTEXPR_SINCE_CXX20
@@ -300,7 +205,6 @@ char_traits<char>::find(const char_type* __s, size_t __n, const char_type& __a)
300205
#endif
301206
}
302207

303-
304208
// char_traits<wchar_t>
305209

306210
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
@@ -713,6 +617,202 @@ char_traits<char32_t>::find(const char_type* __s, size_t __n, const char_type& _
713617
return nullptr;
714618
}
715619

620+
//
621+
// Temporary extensions for std::char_traits<unsigned char> and std::char_traits<signed char>.
622+
// TODO: Remove those in LLVM 18.
623+
//
624+
template <>
625+
struct _LIBCPP_TEMPLATE_VIS
626+
_LIBCPP_DEPRECATED_("char_traits<unsigned char> is non-standard and is provided for a temporary period. It will be removed in LLVM 18, so please migrate off of it.")
627+
char_traits<unsigned char>
628+
{
629+
using char_type = unsigned char;
630+
using int_type = int;
631+
using off_type = streamoff;
632+
using pos_type = streampos;
633+
using state_type = mbstate_t;
634+
635+
static inline void _LIBCPP_CONSTEXPR_SINCE_CXX17
636+
assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
637+
static inline _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT
638+
{return __c1 == __c2;}
639+
static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
640+
{return __c1 < __c2;}
641+
642+
static _LIBCPP_CONSTEXPR_SINCE_CXX17
643+
int compare(const char_type* __s1, const char_type* __s2, size_t __n) {
644+
for (; __n; --__n, ++__s1, ++__s2)
645+
{
646+
if (lt(*__s1, *__s2))
647+
return -1;
648+
if (lt(*__s2, *__s1))
649+
return 1;
650+
}
651+
return 0;
652+
}
653+
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
654+
size_t length(const char_type* __s) {
655+
size_t __len = 0;
656+
for (; !eq(*__s, char_type(0)); ++__s)
657+
++__len;
658+
return __len;
659+
}
660+
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
661+
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) {
662+
for (; __n; --__n)
663+
{
664+
if (eq(*__s, __a))
665+
return __s;
666+
++__s;
667+
}
668+
return nullptr;
669+
}
670+
static _LIBCPP_CONSTEXPR_SINCE_CXX20
671+
char_type* move(char_type* __s1, const char_type* __s2, size_t __n) {
672+
if (__n == 0) return __s1;
673+
char_type* __r = __s1;
674+
if (__s1 < __s2)
675+
{
676+
for (; __n; --__n, ++__s1, ++__s2)
677+
assign(*__s1, *__s2);
678+
}
679+
else if (__s2 < __s1)
680+
{
681+
__s1 += __n;
682+
__s2 += __n;
683+
for (; __n; --__n)
684+
assign(*--__s1, *--__s2);
685+
}
686+
return __r;
687+
}
688+
_LIBCPP_INLINE_VISIBILITY
689+
static _LIBCPP_CONSTEXPR_SINCE_CXX20
690+
char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) {
691+
if (!__libcpp_is_constant_evaluated()) {
692+
_LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range");
693+
}
694+
char_type* __r = __s1;
695+
for (; __n; --__n, ++__s1, ++__s2)
696+
assign(*__s1, *__s2);
697+
return __r;
698+
}
699+
_LIBCPP_INLINE_VISIBILITY
700+
static _LIBCPP_CONSTEXPR_SINCE_CXX20
701+
char_type* assign(char_type* __s, size_t __n, char_type __a) {
702+
char_type* __r = __s;
703+
for (; __n; --__n, ++__s)
704+
assign(*__s, __a);
705+
return __r;
706+
}
707+
708+
static inline _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT
709+
{return eq_int_type(__c, eof()) ? ~eof() : __c;}
710+
static inline _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT
711+
{return char_type(__c);}
712+
static inline _LIBCPP_CONSTEXPR int_type to_int_type(char_type __c) _NOEXCEPT
713+
{return int_type(__c);}
714+
static inline _LIBCPP_CONSTEXPR bool eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT
715+
{return __c1 == __c2;}
716+
static inline _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT
717+
{return int_type(EOF);}
718+
};
719+
720+
template <>
721+
struct _LIBCPP_TEMPLATE_VIS
722+
_LIBCPP_DEPRECATED_("char_traits<signed char> is non-standard and is provided for a temporary period. It will be removed in LLVM 18, so please migrate off of it.")
723+
char_traits<signed char>
724+
{
725+
using char_type = signed char;
726+
using int_type = int;
727+
using off_type = streamoff;
728+
using pos_type = streampos;
729+
using state_type = mbstate_t;
730+
731+
static inline void _LIBCPP_CONSTEXPR_SINCE_CXX17
732+
assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
733+
static inline _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT
734+
{return __c1 == __c2;}
735+
static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
736+
{return __c1 < __c2;}
737+
738+
static _LIBCPP_CONSTEXPR_SINCE_CXX17
739+
int compare(const char_type* __s1, const char_type* __s2, size_t __n) {
740+
for (; __n; --__n, ++__s1, ++__s2)
741+
{
742+
if (lt(*__s1, *__s2))
743+
return -1;
744+
if (lt(*__s2, *__s1))
745+
return 1;
746+
}
747+
return 0;
748+
}
749+
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
750+
size_t length(const char_type* __s) {
751+
size_t __len = 0;
752+
for (; !eq(*__s, char_type(0)); ++__s)
753+
++__len;
754+
return __len;
755+
}
756+
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
757+
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) {
758+
for (; __n; --__n)
759+
{
760+
if (eq(*__s, __a))
761+
return __s;
762+
++__s;
763+
}
764+
return nullptr;
765+
}
766+
static _LIBCPP_CONSTEXPR_SINCE_CXX20
767+
char_type* move(char_type* __s1, const char_type* __s2, size_t __n) {
768+
if (__n == 0) return __s1;
769+
char_type* __r = __s1;
770+
if (__s1 < __s2)
771+
{
772+
for (; __n; --__n, ++__s1, ++__s2)
773+
assign(*__s1, *__s2);
774+
}
775+
else if (__s2 < __s1)
776+
{
777+
__s1 += __n;
778+
__s2 += __n;
779+
for (; __n; --__n)
780+
assign(*--__s1, *--__s2);
781+
}
782+
return __r;
783+
}
784+
_LIBCPP_INLINE_VISIBILITY
785+
static _LIBCPP_CONSTEXPR_SINCE_CXX20
786+
char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) {
787+
if (!__libcpp_is_constant_evaluated()) {
788+
_LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range");
789+
}
790+
char_type* __r = __s1;
791+
for (; __n; --__n, ++__s1, ++__s2)
792+
assign(*__s1, *__s2);
793+
return __r;
794+
}
795+
_LIBCPP_INLINE_VISIBILITY
796+
static _LIBCPP_CONSTEXPR_SINCE_CXX20
797+
char_type* assign(char_type* __s, size_t __n, char_type __a) {
798+
char_type* __r = __s;
799+
for (; __n; --__n, ++__s)
800+
assign(*__s, __a);
801+
return __r;
802+
}
803+
804+
static inline _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT
805+
{return eq_int_type(__c, eof()) ? ~eof() : __c;}
806+
static inline _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT
807+
{return char_type(__c);}
808+
static inline _LIBCPP_CONSTEXPR int_type to_int_type(char_type __c) _NOEXCEPT
809+
{return int_type(__c);}
810+
static inline _LIBCPP_CONSTEXPR bool eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT
811+
{return __c1 == __c2;}
812+
static inline _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT
813+
{return int_type(EOF);}
814+
};
815+
716816
// helper fns for basic_string and string_view
717817

718818
// __str_find

0 commit comments

Comments
 (0)