CLI11 2.7.2
C++11 Command Line Interface Parser
Loading...
Searching...
No Matches
Encoding_inl.hpp
1// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
2// under NSF AWARD 1414736 and by the respective contributors.
3// All rights reserved.
4//
5// SPDX-License-Identifier: BSD-3-Clause
6
7#pragma once
8
9// IWYU pragma: private, include "CLI/CLI.hpp"
10
11// This include is only needed for IDEs to discover symbols
12#include "../Encoding.hpp"
13#include "../Macros.hpp"
14
15// [CLI11:public_includes:set]
16#include <array>
17#include <clocale>
18#include <cstdlib>
19#include <cstring>
20#include <cwchar>
21#include <locale>
22#include <stdexcept>
23#include <string>
24#include <type_traits>
25#include <utility>
26// [CLI11:public_includes:end]
27
28// [CLI11:encoding_inl_includes:verbatim]
29#if CLI11_HAS_CODECVT
30#include <codecvt>
31#endif
32// [CLI11:encoding_inl_includes:end]
33
34namespace CLI {
35// [CLI11:encoding_inl_hpp:verbatim]
36
37namespace detail {
38
39#if !CLI11_HAS_CODECVT
41CLI11_INLINE void set_unicode_locale() {
42 static const std::array<const char *, 2> unicode_locales{{"C.UTF-8", ".UTF-8"}};
43
44 for(const auto &locale_name : unicode_locales) {
45 if(std::setlocale(LC_ALL, locale_name) != nullptr) {
46 return;
47 }
48 }
49 throw std::runtime_error("CLI::narrow: could not set locale to C.UTF-8");
50}
51
52template <typename F> struct scope_guard_t {
53 F closure;
54
55 explicit scope_guard_t(F closure_) : closure(closure_) {}
56 ~scope_guard_t() { closure(); }
57};
58
59template <typename F> CLI11_NODISCARD CLI11_INLINE scope_guard_t<F> scope_guard(F &&closure) {
60 return scope_guard_t<F>{std::forward<F>(closure)};
61}
62
63#endif // !CLI11_HAS_CODECVT
64
65CLI11_DIAGNOSTIC_PUSH
66CLI11_DIAGNOSTIC_IGNORE_DEPRECATED
67
68CLI11_INLINE std::string narrow_impl(const wchar_t *str, std::size_t str_size) {
69#if CLI11_HAS_CODECVT
70#ifdef _WIN32
71 return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(str, str + str_size);
72
73#else
74 return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
75
76#endif // _WIN32
77#else // CLI11_HAS_CODECVT
78 // Copy into a local, NUL-terminated buffer so the conversion is bounded by str_size: a view into the
79 // middle of a larger buffer or a non-NUL-terminated buffer must not be read past str_size. Note that an
80 // embedded NUL inside the first str_size characters will still terminate std::wcsrtombs early; this matches
81 // the behavior of C-style narrow conversion and is treated as a best-effort limitation.
82 std::wstring input(str, str_size);
83 const wchar_t *src = input.c_str();
84 const wchar_t *it = src;
85
86 std::mbstate_t state = std::mbstate_t();
87
88 std::string old_locale = std::setlocale(LC_ALL, nullptr);
89 auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); });
90 set_unicode_locale();
91
92 std::size_t new_size = std::wcsrtombs(nullptr, &it, 0, &state);
93 if(new_size == static_cast<std::size_t>(-1)) {
94 throw std::runtime_error("CLI::narrow: conversion error in std::wcsrtombs at offset " +
95 std::to_string(it - src));
96 }
97 std::string result(new_size, '\0');
98 std::wcsrtombs(const_cast<char *>(result.data()), &src, new_size, &state);
99
100 return result;
101
102#endif // CLI11_HAS_CODECVT
103}
104
105CLI11_INLINE std::wstring widen_impl(const char *str, std::size_t str_size) {
106#if CLI11_HAS_CODECVT
107#ifdef _WIN32
108 return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(str, str + str_size);
109
110#else
111 return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
112
113#endif // _WIN32
114#else // CLI11_HAS_CODECVT
115 // Copy into a local, NUL-terminated buffer so the conversion is bounded by str_size: a view into the
116 // middle of a larger buffer or a non-NUL-terminated buffer must not be read past str_size. Note that an
117 // embedded NUL inside the first str_size characters will still terminate std::mbsrtowcs early; this matches
118 // the behavior of C-style wide conversion and is treated as a best-effort limitation.
119 std::string input(str, str_size);
120 const char *src = input.c_str();
121 const char *it = src;
122
123 std::mbstate_t state = std::mbstate_t();
124
125 std::string old_locale = std::setlocale(LC_ALL, nullptr);
126 auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); });
127 set_unicode_locale();
128
129 std::size_t new_size = std::mbsrtowcs(nullptr, &it, 0, &state);
130 if(new_size == static_cast<std::size_t>(-1)) {
131 throw std::runtime_error("CLI::widen: conversion error in std::mbsrtowcs at offset " +
132 std::to_string(it - src));
133 }
134 std::wstring result(new_size, L'\0');
135 std::mbsrtowcs(const_cast<wchar_t *>(result.data()), &src, new_size, &state);
136
137 return result;
138
139#endif // CLI11_HAS_CODECVT
140}
141
142CLI11_DIAGNOSTIC_POP
143
144} // namespace detail
145
146CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t str_size) { return detail::narrow_impl(str, str_size); }
147CLI11_INLINE std::string narrow(const std::wstring &str) { return detail::narrow_impl(str.data(), str.size()); }
148// Flawfinder: ignore
149CLI11_INLINE std::string narrow(const wchar_t *str) { return detail::narrow_impl(str, std::wcslen(str)); }
150
151CLI11_INLINE std::wstring widen(const char *str, std::size_t str_size) { return detail::widen_impl(str, str_size); }
152CLI11_INLINE std::wstring widen(const std::string &str) { return detail::widen_impl(str.data(), str.size()); }
153// Flawfinder: ignore
154CLI11_INLINE std::wstring widen(const char *str) { return detail::widen_impl(str, std::strlen(str)); }
155
156#ifdef CLI11_CPP17
157CLI11_INLINE std::string narrow(std::wstring_view str) { return detail::narrow_impl(str.data(), str.size()); }
158CLI11_INLINE std::wstring widen(std::string_view str) { return detail::widen_impl(str.data(), str.size()); }
159#endif // CLI11_CPP17
160
161#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
162CLI11_INLINE std::filesystem::path to_path(std::string_view str) {
163 return std::filesystem::path{
164#ifdef _WIN32
165 widen(str)
166#else
167 str
168#endif // _WIN32
169 };
170}
171#endif // CLI11_HAS_FILESYSTEM
172
173// [CLI11:encoding_inl_hpp:end]
174} // namespace CLI