CLI11
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
28namespace CLI {
29// [CLI11:encoding_inl_hpp:verbatim]
30
31namespace detail {
32
33#if !CLI11_HAS_CODECVT
35CLI11_INLINE void set_unicode_locale() {
36 static const std::array<const char *, 2> unicode_locales{{"C.UTF-8", ".UTF-8"}};
37
38 for(const auto &locale_name : unicode_locales) {
39 if(std::setlocale(LC_ALL, locale_name) != nullptr) {
40 return;
41 }
42 }
43 throw std::runtime_error("CLI::narrow: could not set locale to C.UTF-8");
44}
45
46template <typename F> struct scope_guard_t {
47 F closure;
48
49 explicit scope_guard_t(F closure_) : closure(closure_) {}
50 ~scope_guard_t() { closure(); }
51};
52
53template <typename F> CLI11_NODISCARD CLI11_INLINE scope_guard_t<F> scope_guard(F &&closure) {
54 return scope_guard_t<F>{std::forward<F>(closure)};
55}
56
57#endif // !CLI11_HAS_CODECVT
58
59CLI11_DIAGNOSTIC_PUSH
60CLI11_DIAGNOSTIC_IGNORE_DEPRECATED
61
62CLI11_INLINE std::string narrow_impl(const wchar_t *str, std::size_t str_size) {
63#if CLI11_HAS_CODECVT
64#ifdef _WIN32
65 return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(str, str + str_size);
66
67#else
68 return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
69
70#endif // _WIN32
71#else // CLI11_HAS_CODECVT
72 // Copy into a local, NUL-terminated buffer so the conversion is bounded by str_size: a view into the
73 // middle of a larger buffer or a non-NUL-terminated buffer must not be read past str_size. Note that an
74 // embedded NUL inside the first str_size characters will still terminate std::wcsrtombs early; this matches
75 // the behavior of C-style narrow conversion and is treated as a best-effort limitation.
76 std::wstring input(str, str_size);
77 const wchar_t *src = input.c_str();
78 const wchar_t *it = src;
79
80 std::mbstate_t state = std::mbstate_t();
81
82 std::string old_locale = std::setlocale(LC_ALL, nullptr);
83 auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); });
84 set_unicode_locale();
85
86 std::size_t new_size = std::wcsrtombs(nullptr, &it, 0, &state);
87 if(new_size == static_cast<std::size_t>(-1)) {
88 throw std::runtime_error("CLI::narrow: conversion error in std::wcsrtombs at offset " +
89 std::to_string(it - src));
90 }
91 std::string result(new_size, '\0');
92 std::wcsrtombs(const_cast<char *>(result.data()), &src, new_size, &state);
93
94 return result;
95
96#endif // CLI11_HAS_CODECVT
97}
98
99CLI11_INLINE std::wstring widen_impl(const char *str, std::size_t str_size) {
100#if CLI11_HAS_CODECVT
101#ifdef _WIN32
102 return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(str, str + str_size);
103
104#else
105 return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
106
107#endif // _WIN32
108#else // CLI11_HAS_CODECVT
109 // Copy into a local, NUL-terminated buffer so the conversion is bounded by str_size: a view into the
110 // middle of a larger buffer or a non-NUL-terminated buffer must not be read past str_size. Note that an
111 // embedded NUL inside the first str_size characters will still terminate std::mbsrtowcs early; this matches
112 // the behavior of C-style wide conversion and is treated as a best-effort limitation.
113 std::string input(str, str_size);
114 const char *src = input.c_str();
115 const char *it = src;
116
117 std::mbstate_t state = std::mbstate_t();
118
119 std::string old_locale = std::setlocale(LC_ALL, nullptr);
120 auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); });
121 set_unicode_locale();
122
123 std::size_t new_size = std::mbsrtowcs(nullptr, &it, 0, &state);
124 if(new_size == static_cast<std::size_t>(-1)) {
125 throw std::runtime_error("CLI::widen: conversion error in std::mbsrtowcs at offset " +
126 std::to_string(it - src));
127 }
128 std::wstring result(new_size, L'\0');
129 std::mbsrtowcs(const_cast<wchar_t *>(result.data()), &src, new_size, &state);
130
131 return result;
132
133#endif // CLI11_HAS_CODECVT
134}
135
136CLI11_DIAGNOSTIC_POP
137
138} // namespace detail
139
140CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t str_size) { return detail::narrow_impl(str, str_size); }
141CLI11_INLINE std::string narrow(const std::wstring &str) { return detail::narrow_impl(str.data(), str.size()); }
142// Flawfinder: ignore
143CLI11_INLINE std::string narrow(const wchar_t *str) { return detail::narrow_impl(str, std::wcslen(str)); }
144
145CLI11_INLINE std::wstring widen(const char *str, std::size_t str_size) { return detail::widen_impl(str, str_size); }
146CLI11_INLINE std::wstring widen(const std::string &str) { return detail::widen_impl(str.data(), str.size()); }
147// Flawfinder: ignore
148CLI11_INLINE std::wstring widen(const char *str) { return detail::widen_impl(str, std::strlen(str)); }
149
150#ifdef CLI11_CPP17
151CLI11_INLINE std::string narrow(std::wstring_view str) { return detail::narrow_impl(str.data(), str.size()); }
152CLI11_INLINE std::wstring widen(std::string_view str) { return detail::widen_impl(str.data(), str.size()); }
153#endif // CLI11_CPP17
154
155#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
156CLI11_INLINE std::filesystem::path to_path(std::string_view str) {
157 return std::filesystem::path{
158#ifdef _WIN32
159 widen(str)
160#else
161 str
162#endif // _WIN32
163 };
164}
165#endif // CLI11_HAS_FILESYSTEM
166
167// [CLI11:encoding_inl_hpp:end]
168} // namespace CLI