codecvt_utf8 removed in C++26
https://github.com/CLIUtils/CLI11/blob/063b2c911cea8bd4b908d6187c6a007ad320cb1a/include/CLI/Macros.hpp#L98-L104
Using llvm 19, in C++26 mode, can't compile
In file included from /Users/julien/.conan2/p/cli1176dd194db6a54/p/include/CLI/CLI.hpp:18:
In file included from /Users/julien/.conan2/p/cli1176dd194db6a54/p/include/CLI/Encoding.hpp:54:
/Users/julien/.conan2/p/cli1176dd194db6a54/p/include/CLI/impl/Encoding_inl.hpp:68:17: error: no member named 'wstring_convert' in namespace 'std'
68 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
| ~~~~~^
/Users/julien/.conan2/p/cli1176dd194db6a54/p/include/CLI/impl/Encoding_inl.hpp:68:38: error: no member named 'codecvt_utf8' in namespace 'std'
68 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
| ~~~~~^
/Users/julien/.conan2/p/cli1176dd194db6a54/p/include/CLI/impl/Encoding_inl.hpp:68:58: error: expected '(' for function-style cast or type construction
68 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
| ~~~~~~~^
/Users/julien/.conan2/p/cli1176dd194db6a54/p/include/CLI/impl/Encoding_inl.hpp:68:61: error: expected expression
68 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
| ^
/Users/julien/.conan2/p/cli1176dd194db6a54/p/include/CLI/impl/Encoding_inl.hpp:99:17: error: no member named 'wstring_convert' in namespace 'std'
99 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
| ~~~~~^
/Users/julien/.conan2/p/cli1176dd194db6a54/p/include/CLI/impl/Encoding_inl.hpp:99:38: error: no member named 'codecvt_utf8' in namespace 'std'
99 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
| ~~~~~^
/Users/julien/.conan2/p/cli1176dd194db6a54/p/include/CLI/impl/Encoding_inl.hpp:99:58: error: expected '(' for function-style cast or type construction
99 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
| ~~~~~~~^
/Users/julien/.conan2/p/cli1176dd194db6a54/p/include/CLI/impl/Encoding_inl.hpp:99:61: error: expected expression
99 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
https://github.com/llvm/llvm-project/blob/eae30a240e34e1fd31b57096a1b8bdbd8b84352d/libcxx/docs/UserDocumentation.rst#c26-specific-configuration-macros
Seems like at least there are two defines: _LIBCPP_ENABLE_CXX26_REMOVED_CODECVT and _LIBCPP_ENABLE_CXX26_REMOVED_WSTRING_CONVERT
(Also worth noting that I didn't have to do that on GCC 14 with C++26)
I've worked around it like so:
# On mac make sure you `brew install tcl-tk@8` first and `brew install pkgconf`
export PKG_CONFIG="$(brew --prefix pkgconf)/bin/pkgconf"
# ensure tcl 8.6 is found, not 9.0
export PKG_CONFIG_PATH=$(brew --prefix tcl-tk@8)/lib/pkgconfig:$(brew --prefix)/lib/pkgconfig
PYTHON_CONFIGURE_OPTS="--enable-shared --with-tcltk-libs=" pyenv install --verbose 3.12.2
Based on the anticipated standard for C++26, it seems like the appropriate thing to do is set CLI11_HAS_CODECVT=0 if we are in C++26, as that function call is deprecated due to it not meeting current unicode standards, so might not work right anyway.
hmmm I am not getting a clang19 c++26 test to fail like indicated. in #1100. I made some potential modifications in macros.hpp. Would it be possible to try those modifications (uncomment them) in your setup. Also can you provide more details on your setup to see if it can be replicated in a CI build job?
@phlptp .
cd CLI11
git checkout main && git pull
mkdir -p ../build-CLI11 && cd ../build-CLI11
export CC="/opt/homebrew/opt/llvm@19/bin/clang"
export CXX="/opt/homebrew/opt/llvm@19/bin/clang++"
export CPPFLAGS="-I/opt/homebrew/opt/llvm@19/include"
export LDFLAGS="-L/opt/homebrew/opt/llvm@19/lib/c++ -Wl,-rpath,/opt/homebrew/opt/llvm@19/lib/c++"
cmake -G Ninja -DCMAKE_CXX_STANDARD=26 -DCMAKE_CXX_EXTENSIONS:BOOL=OFF -DCMAKE_CXX_STANDARD_REQUIRED:BOOL=ON -DCMAKE_CXX_FLAGS_INIT:STRING="-stdlib=libc++" ../CLI11
ninja
[ 50%][116/228] Building CXX object tests/CMakeFiles/ensure_utf8.dir/applications/ensure_utf8.cpp.o
FAILED: tests/CMakeFiles/ensure_utf8.dir/applications/ensure_utf8.cpp.o
/opt/homebrew/opt/llvm@19/bin/clang++ -I/Users/julien/Software/Others/CLI11/include -stdlib=libc++ -std=c++26 -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -mmacosx-version-min=12.1 -MD -MT tests/CMakeFiles/ensure_utf8.dir/applications/ensure_utf8.cpp.o -MF tests/CMakeFiles/ensure_utf8.dir/applications/ensure_utf8.cpp.o.d @tests/CMakeFiles/ensure_utf8.dir/applications/ensure_utf8.cpp.o.modmap -o tests/CMakeFiles/ensure_utf8.dir/applications/ensure_utf8.cpp.o -c /Users/julien/Software/Others/CLI11/tests/applications/ensure_utf8.cpp
In file included from /Users/julien/Software/Others/CLI11/tests/applications/ensure_utf8.cpp:7:
In file included from /Users/julien/Software/Others/CLI11/include/CLI/CLI.hpp:18:
In file included from /Users/julien/Software/Others/CLI11/include/CLI/Encoding.hpp:54:
/Users/julien/Software/Others/CLI11/include/CLI/impl/Encoding_inl.hpp:68:17: error: no member named 'wstring_convert' in namespace 'std'
68 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
| ~~~~~^
/Users/julien/Software/Others/CLI11/include/CLI/impl/Encoding_inl.hpp:68:38: error: no member named 'codecvt_utf8' in namespace 'std'
68 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
| ~~~~~^
/Users/julien/Software/Others/CLI11/include/CLI/impl/Encoding_inl.hpp:68:58: error: expected '(' for function-style cast or type construction
68 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
| ~~~~~~~^
/Users/julien/Software/Others/CLI11/include/CLI/impl/Encoding_inl.hpp:68:61: error: expected expression
68 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
| ^
/Users/julien/Software/Others/CLI11/include/CLI/impl/Encoding_inl.hpp:99:17: error: no member named 'wstring_convert' in namespace 'std'
99 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
| ~~~~~^
/Users/julien/Software/Others/CLI11/include/CLI/impl/Encoding_inl.hpp:99:38: error: no member named 'codecvt_utf8' in namespace 'std'
99 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
| ~~~~~^
/Users/julien/Software/Others/CLI11/include/CLI/impl/Encoding_inl.hpp:99:58: error: expected '(' for function-style cast or type construction
99 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
| ~~~~~~~^
/Users/julien/Software/Others/CLI11/include/CLI/impl/Encoding_inl.hpp:99:61: error: expected expression
99 | return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
| ^
8 errors generated.
System info:
This is an M1 Max.
$ brew --prefix llvm@19
/opt/homebrew/opt/llvm
$ /opt/homebrew/opt/llvm@19/bin/clang --version
Homebrew clang version 19.1.4
Target: arm64-apple-darwin24.1.0
Thread model: posix
InstalledDir: /opt/homebrew/Cellar/llvm/19.1.4/bin
Configuration file: /opt/homebrew/etc/clang/arm64-apple-darwin24.cf
$ sw_vers
ProductName: macOS
ProductVersion: 15.1.1
BuildVersion: 24B91
I checked out 464306bf4ddb855ccb940b1fd322d825adc50ef7 from #1100 (before you commented the macros out).
I get a different build error
[ 53%][122/228] Building CXX object tests/CMakeFiles/HelpersTest.dir/HelpersTest.cpp.o
FAILED: tests/CMakeFiles/HelpersTest.dir/HelpersTest.cpp.o
/opt/homebrew/opt/llvm@19/bin/clang++ -DCLI11_ENSURE_UTF8_EXE=\"/Users/julien/Software/Others/build-CLI11/tests/ensure_utf8\" -DCLI11_ENSURE_UTF8_TWICE_EXE=\"/Users/julien/Software/Others/build-CLI11/tests/ensure_utf8_twice\" -I/Users/julien/Software/Others/CLI11/include -I/Users/julien/Software/Others/CLI11/tests -I/Users/julien/Software/Others/build-CLI11/tests -stdlib=libc++ -std=c++26 -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -mmacosx-version-min=12.1 -Wall -Wextra -pedantic -Wshadow -Wsign-conversion -Wswitch-enum -Wcast-align -Wimplicit-atomic-properties -Wmissing-declarations -Woverlength-strings -Wstrict-selector-match -Wundeclared-selector -MD -MT tests/CMakeFiles/HelpersTest.dir/HelpersTest.cpp.o -MF tests/CMakeFiles/HelpersTest.dir/HelpersTest.cpp.o.d @tests/CMakeFiles/HelpersTest.dir/HelpersTest.cpp.o.modmap -o tests/CMakeFiles/HelpersTest.dir/HelpersTest.cpp.o -c /Users/julien/Software/Others/CLI11/tests/HelpersTest.cpp
/Users/julien/Software/Others/CLI11/tests/HelpersTest.cpp:1623:16: error: no matching function for call to 'lexical_conversion'
1623 | bool res = CLI::detail::lexical_conversion<std::complex<double>, std::array<double, 2>>(input, x);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1470:6: note: candidate template ignored: requirement 'classify_object<std::array<double, 2>, void>::value <= object_category::other' was not satisfied [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1470 | bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1481:6: note: candidate template ignored: substitution failure [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1481 | bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1501:6: note: candidate template ignored: requirement 'is_mutable_container<std::complex<double>, void>::value' was not satisfied [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1501 | bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1529:6: note: candidate template ignored: requirement 'is_complex<std::array<double, 2>>::value' was not satisfied [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1529 | bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1553:6: note: candidate template ignored: requirement 'is_mutable_container<std::complex<double>, void>::value' was not satisfied [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1553 | bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1602:6: note: candidate template ignored: requirement '!is_tuple_like<std::complex<double>>::value' was not satisfied [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1602 | bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1689:6: note: candidate template ignored: requirement 'is_mutable_container<std::complex<double>, void>::value' was not satisfied [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1689 | bool lexical_conversion(std::vector<std::string> strings, AssignTo &output) {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1715:6: note: candidate template ignored: requirement 'type_count_base<std::array<double, 2>, void>::value != type_count<std::array<double, 2>, void>::value || type_count<std::array<double, 2>, void>::value > 2' was not satisfied [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1715 | bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1730:6: note: candidate template ignored: requirement 'is_mutable_container<std::complex<double>, void>::value' was not satisfied [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1730 | bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1766:6: note: candidate template ignored: requirement 'classify_object<std::array<double, 2>, void>::value == object_category::wrapper_value' was not satisfied [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1766 | bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:1785:6: note: candidate template ignored: requirement 'classify_object<std::array<double, 2>, void>::value == object_category::wrapper_value' was not satisfied [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]
1785 | bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
| ^
1 error generated.
[ 54%][124/228] Building CXX object tests/CMakeFiles/AppTest.dir/AppTest.cpp.o
FAILED: tests/CMakeFiles/AppTest.dir/AppTest.cpp.o
/opt/homebrew/opt/llvm@19/bin/clang++ -DCLI11_ENSURE_UTF8_EXE=\"/Users/julien/Software/Others/build-CLI11/tests/ensure_utf8\" -DCLI11_ENSURE_UTF8_TWICE_EXE=\"/Users/julien/Software/Others/build-CLI11/tests/ensure_utf8_twice\" -I/Users/julien/Software/Others/CLI11/include -I/Users/julien/Software/Others/CLI11/tests -I/Users/julien/Software/Others/build-CLI11/tests -stdlib=libc++ -std=c++26 -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -mmacosx-version-min=12.1 -Wall -Wextra -pedantic -Wshadow -Wsign-conversion -Wswitch-enum -Wcast-align -Wimplicit-atomic-properties -Wmissing-declarations -Woverlength-strings -Wstrict-selector-match -Wundeclared-selector -MD -MT tests/CMakeFiles/AppTest.dir/AppTest.cpp.o -MF tests/CMakeFiles/AppTest.dir/AppTest.cpp.o.d @tests/CMakeFiles/AppTest.dir/AppTest.cpp.o.modmap -o tests/CMakeFiles/AppTest.dir/AppTest.cpp.o -c /Users/julien/Software/Others/CLI11/tests/AppTest.cpp
In file included from /Users/julien/Software/Others/CLI11/tests/AppTest.cpp:7:
In file included from /Users/julien/Software/Others/CLI11/tests/app_helper.hpp:12:
In file included from /Users/julien/Software/Others/CLI11/include/CLI/CLI.hpp:38:
/Users/julien/Software/Others/CLI11/include/CLI/App.hpp:558:32: error: ambiguous partial specializations of 'type_count_min<std::complex<double>>'
558 | opt->type_size(detail::type_count_min<ConvertTo>::value, (std::max)(Tcount, XCcount));
| ^
/Users/julien/Software/Others/CLI11/tests/AppTest.cpp:1030:9: note: in instantiation of function template specialization 'CLI::App::add_option<std::complex<double>, std::complex<double>, (CLI::detail::enabler)0>' requested here
1030 | app.add_option("--long", val)->take_first()->allow_extra_args();
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:570:30: note: partial specialization matches [with T = std::complex<double>]
570 | template <typename T> struct type_count_min<T, typename std::enable_if<is_complex<T>::value>::type> {
| ^
/Users/julien/Software/Others/CLI11/include/CLI/TypeTools.hpp:595:30: note: partial specialization matches [with T = std::complex<double>]
595 | template <typename T> struct type_count_min<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
| ^
1 error generated.
[ 58%][133/228] Building CXX object tests/CMakeFiles/OptionTypeTest.dir/OptionTypeTest.cpp.o
ninja: build stopped: subcommand failed.
I will keep trying, thanks
as best as I can tell that second compiler error is some other change in llvm 19. That seems specific to M1 Macs. I don't have access to that platform and don't know how to replicate on a CI system. Going to need some help from someone who can get into the templates and has access to the specific platform to help debug.
@phlptp see this compiler explorer: https://godbolt.org/z/Toes7rhM9
I took the single include CLI11.hpp from e43ff02c1142bf6c20ae3e3befb0e39ec2d1cdbc, and recreated the test in main.
The key is passing -stdlib=libc++ as a flag, it doesn't have the problem with the regular c++ lib
I reduced it down to a 240 lines example https://godbolt.org/z/jroxPcxbr , the error is more informative and says ambiguous partial specializations of 'type_count<std::complex<double>>'
<source>:237:16: error: no matching function for call to 'lexical_conversion'
237 | bool res = lexical_conversion<std::complex<double>, std::array<double, 2>>(input, x);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:214:6: note: candidate template ignored: substitution failure [with AssignTo = std::complex<double>, ConvertTo = std::array<double, 2>]: ambiguous partial specializations of 'type_count<std::complex<double>>'
209 | enable_if_t<(type_count<AssignTo>::value <= 2) &&
| ~~~~~~~~~~
210 | expected_count<AssignTo>::value == 1 &&
211 | is_tuple_like<ConvertTo>::value &&
212 | type_count_base<ConvertTo>::value == 2,
213 | detail::enabler> = detail::dummy>
214 | bool lexical_conversion(const std::vector<std ::string> &strings,
Which I can indeed reproduce with the full CLI11.hpp and just putting this
static_assert(CLI::detail::type_count<std::complex<double>>::value == 2);
<source>:234:19: error: ambiguous partial specializations of 'type_count<std::complex<double>>'
234 | static_assert(type_count<std::complex<double>>::value == 2);
| ^
<source>:143:30: note: partial specialization matches [with T = std::complex<double>]
143 | template <typename T> struct type_count<T, typename std::enable_if<is_complex<T>::value>::type> {
| ^
<source>:173:30: note: partial specialization matches [with T = std::complex<double>]
173 | template <typename T> struct type_count<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
| ^
1 error generated.
Compiler returned: 1
Ok, so the difference is that is_tuple_like<std::complex<double>> is false generally, but with C++26 and libc++ from LLVM it is true. Here is the shortest example demonstrating it (24 lines): https://godbolt.org/z/aMWKsoGWP
#include <complex>
#include <type_traits>
// Check for tuple like types, as in classes with a tuple_size type trait
template <typename S>
class is_tuple_like {
template <typename SS>
// static auto test(int)
// -> decltype(std::conditional<(std::tuple_size<SS>::value > 0),
// std::true_type, std::false_type>::type());
static auto test(int)
-> decltype(std::tuple_size<typename std::decay<SS>::type>::value,
std::true_type{});
template <typename>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<S>(0))::value;
};
int main() {
static_assert(!is_tuple_like<std::complex<double>>::value);
}
Ok found it: https://en.cppreference.com/w/cpp/utility/tuple_size
https://en.cppreference.com/w/cpp/numeric/complex/tuple_size
It was added by P2819R2 : https://wg21.link/P2819R2
as best as I can tell that second compiler error is some other change in llvm 19. That seems specific to M1 Macs. I don't have access to that platform and don't know how to replicate on a CI system. Going to need some help from someone who can get into the templates and has access to the specific platform to help debug.
This is not specific to M1 macs. It is specific to the change being bleeding edge C++26, and it was only implemented in LLVM's libc++. Compiler explorer clang compilers are on linux, and even the x86-64 19.1.0 will produce the error (cf https://godbolt.org/z/E1dqKPaoj )
That being said, just FYI if you want to run something on apple silicon, you can use github actions with the "macos-14" or "macos-15" runners which are M1s.