opustags icon indicating copy to clipboard operation
opustags copied to clipboard

Build broken with clang-19: implicit instantiation of undefined template 'std::char_traits<unsigned char>'

Open yurivict opened this issue 1 year ago • 3 comments

In file included from /wrkdirs/usr/ports/audio/opustags/work/opustags-1.10.1/src/base64.cc:12:
In file included from /wrkdirs/usr/ports/audio/opustags/work/opustags-1.10.1/src/opustags.h:34:
In file included from /usr/include/c++/v1/functional:552:
In file included from /usr/include/c++/v1/__functional/boyer_moore_searcher.h:27:
In file included from /usr/include/c++/v1/vector:325:
In file included from /usr/include/c++/v1/__format/formatter_bool.h:19:
In file included from /usr/include/c++/v1/__format/formatter_integral.h:21:
In file included from /usr/include/c++/v1/__format/formatter_output.h:22:
In file included from /usr/include/c++/v1/__format/parser_std_format_spec.h:39:
/usr/include/c++/v1/string:820:42: error: implicit instantiation of undefined template 'std::char_traits<unsigned char>'
  820 |   static_assert(is_same<_CharT, typename traits_type::char_type>::value,
      |                                          ^
/wrkdirs/usr/ports/audio/opustags/work/opustags-1.10.1/src/opustags.h:383:14: note: in instantiation of template class 'std::basic_string<unsigned char>' requested here
  383 |         byte_string extra_data;
      |                     ^
/usr/include/c++/v1/__fwd/string.h:23:29: note: template is declared here
   23 | struct _LIBCPP_TEMPLATE_VIS char_traits;
      |                             ^
In file included from /wrkdirs/usr/ports/audio/opustags/work/opustags-1.10.1/src/base64.cc:12:
In file included from /wrkdirs/usr/ports/audio/opustags/work/opustags-1.10.1/src/opustags.h:34:
In file included from /usr/include/c++/v1/functional:552:
In file included from /usr/include/c++/v1/__functional/boyer_moore_searcher.h:27:
In file included from /usr/include/c++/v1/vector:325:
In file included from /usr/include/c++/v1/__format/formatter_bool.h:16:
In file included from /usr/include/c++/v1/__format/concepts.h:16:
In file included from /usr/include/c++/v1/__format/format_parse_context.h:16:
/usr/include/c++/v1/string_view:300:42: error: implicit instantiation of undefined template 'std::char_traits<unsigned char>'
  300 |   static_assert(is_same<_CharT, typename traits_type::char_type>::value,
      |                                          ^
/usr/include/c++/v1/__type_traits/is_convertible.h:30:99: note: in instantiation of template class 'std::basic_string_view<unsigned char>' requested here
   30 | struct _LIBCPP_TEMPLATE_VIS is_convertible : public integral_constant<bool, __is_convertible(_T1, _T2)> {};
      |                                                                                                   ^
/usr/include/c++/v1/string:745:29: note: in instantiation of template class 'std::is_convertible<const std::basic_string<unsigned char> &, std::basic_string_view<unsigned char>>' requested here
  745 |     : public _BoolConstant< is_convertible<const _Tp&, basic_string_view<_CharT, _Traits> >::value &&
      |                             ^
/usr/include/c++/v1/string:1151:27: note: in instantiation of template class 'std::__can_be_converted_to_string_view<unsigned char, std::char_traits<unsigned char>, std::basic_string<unsigned char>>' requested here
 1151 |             __enable_if_t<__can_be_converted_to_string_view<_CharT, _Traits, _Tp>::value &&
      |                           ^
/usr/include/c++/v1/string:1154:93: note: while substituting prior template arguments into non-type template parameter [with _Tp = std::basic_string<unsigned char>]
 1154 |   _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit basic_string(const _Tp& __t)
      |                                                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1155 |       : __r_(__default_init_tag(), __default_init_tag()) {
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1156 |     __self_view __sv = __t;
      |     ~~~~~~~~~~~~~~~~~~~~~~~
 1157 |     __init(__sv.data(), __sv.size());
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1158 |   }
      |   ~
/usr/include/c++/v1/string:752:7: note: while substituting deduced template arguments into function template 'basic_string' [with _Tp = std::basic_string<unsigned char>, $1 = (no value)]
  752 | class basic_string {
      |       ^
/wrkdirs/usr/ports/audio/opustags/work/opustags-1.10.1/src/opustags.h:357:8: note: while declaring the implicit copy constructor for 'opus_tags'
  357 | struct opus_tags {
      |        ^
/usr/include/c++/v1/__fwd/string.h:23:29: note: template is declared here
   23 | struct _LIBCPP_TEMPLATE_VIS char_traits;
      |                             ^
In file included from /wrkdirs/usr/ports/audio/opustags/work/opustags-1.10.1/src/base64.cc:12:
In file included from /wrkdirs/usr/ports/audio/opustags/work/opustags-1.10.1/src/opustags.h:34:
In file included from /usr/include/c++/v1/functional:552:
In file included from /usr/include/c++/v1/__functional/boyer_moore_searcher.h:27:
In file included from /usr/include/c++/v1/vector:325:
In file included from /usr/include/c++/v1/__format/formatter_bool.h:19:
In file included from /usr/include/c++/v1/__format/formatter_integral.h:21:
In file included from /usr/include/c++/v1/__format/formatter_output.h:22:
In file included from /usr/include/c++/v1/__format/parser_std_format_spec.h:39:
/usr/include/c++/v1/string:2535:5: error: implicit instantiation of undefined template 'std::char_traits<unsigned char>'
 2535 |     traits_type::copy(std::__to_address(__p), std::__to_address(__old_p), __n_copy);
      |     ^
/usr/include/c++/v1/string:2556:3: note: in instantiation of member function 'std::basic_string<unsigned char>::__grow_by' requested here
 2556 |   __grow_by(__old_cap, __delta_cap, __old_sz, __n_copy, __n_del, __n_add);
      |   ^
/usr/include/c++/v1/string:2858:7: note: in instantiation of member function 'std::basic_string<unsigned char>::__grow_by_without_replace' requested here
 2858 |       __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
      |       ^
/usr/include/c++/v1/string:3302:5: note: in instantiation of member function 'std::basic_string<unsigned char>::append' requested here
 3302 |     append(__n - __sz, __c);
      |     ^
/usr/include/c++/v1/string:1303:84: note: in instantiation of member function 'std::basic_string<unsigned char>::resize' requested here
 1303 |   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void resize(size_type __n) { resize(__n, value_type()); }
      |                                                                                    ^
/wrkdirs/usr/ports/audio/opustags/work/opustags-1.10.1/src/base64.cc:70:6: note: in instantiation of member function 'std::basic_string<unsigned char>::resize' requested here
   70 |         out.resize(olen);
      |             ^
/usr/include/c++/v1/__fwd/string.h:23:29: note: template is declared here
   23 | struct _LIBCPP_TEMPLATE_VIS char_traits;
      |                             ^

log

Version: 1.10.1 FreeBSD 14.1

yurivict avatar Nov 03 '24 04:11 yurivict

That’s a tough error. Rewriting the code to use vectors instead of strings would be a work-around but that’s tedious work.

According to https://en.cppreference.com/w/cpp/string/char_traits and https://en.cppreference.com/w/c/string/multibyte/char8_t I expect that changing set(CMAKE_CXX_STANDARD 20) to set(CMAKE_CXX_STANDARD 23) in CMakeLists.txt would eventually work. Could you try that?

fmang avatar Nov 06 '24 11:11 fmang

there is nothing on those pages indicating that

using basic_string with non-character types is undefined, and the only way to fix it is to actually not do that

q66 avatar Jan 02 '25 18:01 q66

std::basic_string can be made to work with other types by supplying a custom char_traits.

The standard library provides std::char_traits<char8_t> and, esoteric platforms aside, char8_t is equivalent to unsigned char which is equivalent to uint8_t. opustags’s using byte_string = std::basic_string<uint8_t> makes sense in that aspect. However, C++ defines char8_t as a distinct type, which explains the types are not interchangeable regarding templates.

That leaves us 3 options:

  1. Redefining opustags’s byte_string as std::u8string. That’s ugly, but assuming char8_t casts implicitly to unsigned char, it would require few changes, if any. Some casts might be required though, and I’d rather avoid that.
  2. Defining a custom char_traits for uint8_t so that the current implementation works as-is. Maybe there’s a way to reuse most of char_traits<char8_t>.
  3. Use a different container type and rewrite the code that uses it.

2 and 3 are both good options in my opinion. We should pick the most convenient one.

fmang avatar Jan 03 '25 01:01 fmang

Done. std::basic_string<uint8_t> is no more. 82a5124f14142b63b99ba3716d8c677e721bb900

I ended up not overthinking it and using std::string naively. Using char* or equivalent for working with byte strings is not unusual as far as I know.

fmang avatar Jun 22 '25 07:06 fmang