- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
有没有办法让原始字符串文字知道缩进?
例如
{
std::string_view str(
R"(
Hello
World
)");
std::cout << "ref\n" << str;
}
打印
ref
Hello
World
但我愿意
ref
Hello
World
我看到 this answer解决了这个问题,但它是运行时。对于 c23,我认为 #embed
可能会解决这个问题。
但是有没有办法在编译时做到这一点,最好是使用 c++17,c++20 也可以。
最佳答案
std::cout << R"(
Hello
World
)"_M << std::endl;
constexpr auto str = unindent(R"(
Hello
World!
)"); // type of str would be std::array<char, N>
std::cout << str.data() << std::endl;
在这种情况下,C++17 的最大问题是我们需要修改字符串(删除其中的空格)- 因此我们需要将修改后的字符串存储在某处。
在 C++20 中,我们可以使用 string literal operator template 获取字符串作为模板参数。 ,例如:
template<class _char_type, std::size_t size>
struct string_wrapper {
using char_type = _char_type;
consteval string_wrapper(const char_type (&arr)[size]) {
std::ranges::copy(arr, str);
}
char_type str[size];
};
template<string_wrapper str>
consteval decltype(auto) operator"" _M() {
return do_unindent<str>();
}
// R"(foo)"_M
// would be interpreted as:
// operator"" _M<string_wrapper{R"(foo)"}>()
这允许我们将未缩进的字符串存储在模板参数中,因此它的生命周期永远不会成为问题;我们可以直接返回对存储在模板参数中的数组的引用。
另一方面,在 C++17 中,我们不能将类类型作为模板参数,字符串文字运算符模板也不可用。所以没有巧妙的方法来全局存储修改后的字符串(常量表达式中不允许使用 static
变量,所以这不是一个选项)
所以在 C++17 中实现它的唯一方法是返回一个 std::array
。 (或类似的东西):
template<std::size_t size>
constexpr auto unindent(const char (&str)[size]) {
return std::array<char, size>{ /* ... */ };
}
然而,这有一些主要缺点:
.data()
转换 std::array
回到const char*
: std::cout << unindent(R"(foo)").data() << std::endl;
std::array
活: // BAD: Woops: dangling pointer!
const char* str = unindent(R"(foo)").data();
// GOOD: keep array alive
auto arr = unindent(R"(foo)");
const char* str = arr.data();
std::array<char, size>
)。由于 str
作为函数的参数,它不是常量表达式,所以我们不能用它来确定未缩进字符串的实际大小(因此我们只能使用原始字符串的长度): constexpr std::array<char, 8> arr = unindent(" a\n b");
// arr would be {'a', '\n', 'b', '\0', '\0', '\0', '\0', '\0'};
所以这很可能会导致很多额外的 '\0'
在你的最终二进制文件中,除了相当困惑( arr.size()
不是字符串的真实长度)constexpr /*...*/ operator"" _M(const char* str, std::size_t len) {
return std::array<char, 1000> { /* ... */ };
}
...但是str
并且它的长度不再是常量表达式,因此我们不能将两者都用于 std::array
的大小;所以唯一可能的选择是使用固定大小的数组——这不是很有用。出于这些原因,我将只提供 C++20 实现。
这是完整的 C++20 实现:
#include <algorithm>
#include <string_view>
#include <vector>
#include <ranges>
namespace multiline_raw_string {
template<class char_type>
using string_view = std::basic_string_view<char_type>;
// characters that are considered space
// we need this because std::isspace is not constexpr
template<class char_type>
constexpr string_view<char_type> space_chars = std::declval<string_view<char_type>>();
template<>
constexpr string_view<char> space_chars<char> = " \f\n\r\t\v";
template<>
constexpr string_view<wchar_t> space_chars<wchar_t> = L" \f\n\r\t\v";
template<>
constexpr string_view<char8_t> space_chars<char8_t> = u8" \f\n\r\t\v";
template<>
constexpr string_view<char16_t> space_chars<char16_t> = u" \f\n\r\t\v";
template<>
constexpr string_view<char32_t> space_chars<char32_t> = U" \f\n\r\t\v";
// list of all potential line endings that could be encountered
template<class char_type>
constexpr string_view<char_type> potential_line_endings[] = std::declval<string_view<char_type>[]>();
template<>
constexpr string_view<char> potential_line_endings<char>[] = {
"\r\n",
"\r",
"\n"
};
template<>
constexpr string_view<wchar_t> potential_line_endings<wchar_t>[] = {
L"\r\n",
L"\r",
L"\n"
};
template<>
constexpr string_view<char8_t> potential_line_endings<char8_t>[] = {
u8"\r\n",
u8"\r",
u8"\n"
};
template<>
constexpr string_view<char16_t> potential_line_endings<char16_t>[] = {
u"\r\n",
u"\r",
u"\n"
};
template<>
constexpr string_view<char32_t> potential_line_endings<char32_t>[] = {
U"\r\n",
U"\r",
U"\n"
};
// null-terminator for the different character types
template<class char_type>
constexpr char_type null_char = std::declval<char_type>();
template<>
constexpr char null_char<char> = '\0';
template<>
constexpr wchar_t null_char<wchar_t> = L'\0';
template<>
constexpr char8_t null_char<char8_t> = u8'\0';
template<>
constexpr char16_t null_char<char16_t> = u'\0';
template<>
constexpr char32_t null_char<char32_t> = U'\0';
// detects the line ending used within a string.
// e.g. detect_line_ending("foo\nbar\nbaz") -> "\n"
template<class char_type>
consteval string_view<char_type> detect_line_ending(string_view<char_type> str) {
return *std::ranges::max_element(
potential_line_endings<char_type>,
{},
[str](string_view<char_type> line_ending) {
// count the number of lines we would get with line_ending
auto view = std::views::split(str, line_ending);
return std::ranges::distance(view);
}
);
}
// returns a view to the leading sequence of space characters within a string
// e.g. get_leading_space_sequence(" \t foo") -> " \t "
template<class char_type>
consteval string_view<char_type> get_leading_space_sequence(string_view<char_type> line) {
return line.substr(0, line.find_first_not_of(space_chars<char_type>));
}
// checks if a line consists purely out of space characters
// e.g. is_line_empty(" \t") -> true
// is_line_empty(" foo") -> false
template<class char_type>
consteval bool is_line_empty(string_view<char_type> line) {
return get_leading_space_sequence(line).size() == line.size();
}
// splits a string into individual lines
// and removes the first & last line if they are empty
// e.g. split_lines("\na\nb\nc\n", "\n") -> {"a", "b", "c"}
template<class char_type>
consteval std::vector<string_view<char_type>> split_lines(
string_view<char_type> str,
string_view<char_type> line_ending
) {
std::vector<string_view<char_type>> lines;
for (auto line : std::views::split(str, line_ending)) {
lines.emplace_back(line.begin(), line.end());
}
// remove first/last lines in case they are completely empty
if(lines.size() > 1 && is_line_empty(lines[0])) {
lines.erase(lines.begin());
}
if(lines.size() > 1 && is_line_empty(lines[lines.size()-1])) {
lines.erase(lines.end()-1);
}
return lines;
}
// determines the longest possible sequence of space characters
// that we can remove from each line.
// e.g. determine_common_space_prefix_sequence({" \ta", " foo", " \t\ŧbar"}) -> " "
template<class char_type>
consteval string_view<char_type> determine_common_space_prefix_sequence(
std::vector<string_view<char_type>> const& lines
) {
std::vector<string_view<char_type>> space_sequences = {
string_view<char_type>{} // empty string
};
for(string_view<char_type> line : lines) {
string_view<char_type> spaces = get_leading_space_sequence(line);
for(std::size_t len = 1; len <= spaces.size(); len++) {
space_sequences.emplace_back(spaces.substr(0, len));
}
// remove duplicates
std::ranges::sort(space_sequences);
auto [first, last] = std::ranges::unique(space_sequences);
space_sequences.erase(first, last);
}
// only consider space prefix sequences that apply to all lines
auto shared_prefixes = std::views::filter(
space_sequences,
[&lines](string_view<char_type> prefix) {
return std::ranges::all_of(
lines,
[&prefix](string_view<char_type> line) {
return line.starts_with(prefix);
}
);
}
);
// select the longest possible space prefix sequence
return *std::ranges::max_element(
shared_prefixes,
{},
&string_view<char_type>::size
);
}
// unindents the individual lines of a raw string literal
// e.g. unindent_string(" \n a\n b\n c\n") -> "a\nb\nc"
template<class char_type>
consteval std::vector<char_type> unindent_string(string_view<char_type> str) {
string_view<char_type> line_ending = detect_line_ending(str);
std::vector<string_view<char_type>> lines = split_lines(str, line_ending);
string_view<char_type> common_space_sequence = determine_common_space_prefix_sequence(lines);
std::vector<char_type> new_string;
bool is_first = true;
for(auto line : lines) {
// append newline
if(is_first) {
is_first = false;
} else {
new_string.insert(new_string.end(), line_ending.begin(), line_ending.end());
}
// append unindented line
auto unindented = line.substr(common_space_sequence.size());
new_string.insert(new_string.end(), unindented.begin(), unindented.end());
}
// add null terminator
new_string.push_back(null_char<char_type>);
return new_string;
}
// returns the size required for the unindented string
template<class char_type>
consteval std::size_t unindent_string_size(string_view<char_type> str) {
return unindent_string(str).size();
}
// simple type that stores a raw string
// we need this to get around the limitation that string literals
// are not considered valid non-type template arguments.
template<class _char_type, std::size_t size>
struct string_wrapper {
using char_type = _char_type;
consteval string_wrapper(const char_type (&arr)[size]) {
std::ranges::copy(arr, str);
}
char_type str[size];
};
// used for sneakily creating and storing
// the unindented string in a template parameter.
template<string_wrapper sw>
struct unindented_string_wrapper {
using char_type = typename decltype(sw)::char_type;
static constexpr std::size_t buffer_size = unindent_string_size<char_type>(sw.str);
using array_ref = const char_type (&)[buffer_size];
consteval unindented_string_wrapper(int) {
auto newstr = unindent_string<char_type>(sw.str);
std::ranges::copy(newstr, buffer);
}
consteval array_ref get() const {
return buffer;
}
char_type buffer[buffer_size];
};
// uses a defaulted template argument that depends on the str
// to initialize the unindented string within a template parameter.
// this enables us to return a reference to the unindented string.
template<string_wrapper str, unindented_string_wrapper<str> unindented = 0>
consteval decltype(auto) do_unindent() {
return unindented.get();
}
// the actual user-defined string literal operator
template<string_wrapper str>
consteval decltype(auto) operator"" _M() {
return do_unindent<str>();
}
}
using multiline_raw_string::operator"" _M;
使用示例:godbolt
std::cout << R"(
a
b
c
d
)"_M << std::endl;
/* Will print the following:
a
b
c
d
*/
// The type of R"(...)"_M is still const char (&)[N],
// so it can be used like a normal string literal:
std::cout << std::size(R"(asdf)"_M) << std::endl;
// (will print 5)
// Lifetime is not a problem; can be stored in a std::string_view:
constexpr std::string_view str = R"(
foo
bar
)"_M;
// also works with wchar_t, char8_t, char16_t and char32_t literals:
std::wcout << LR"(foo)"_M << std::endl;
std::isspace()
不幸的是不是 constexpr,所以我们必须推出自己的:(为了取消缩进,您可以添加您希望被视为“空格”的其他字符): template<>
constexpr string_view<char> space_chars<char> = " \f\n\r\t\v";
\r\n
, \n
和 \r
作为潜在的行结尾,这些应该涵盖大多数用例: template<>
constexpr string_view<char> potential_line_endings<char>[] = {
"\r\n",
"\r",
"\n"
};
// mixed indentation (indentation will remain)
constexpr std::string_view str = R"(
<tab>foo
bar
)"_M;
但这将: // all lines have the same indentation pattern
constexpr std::string_view str = R"(
<tab> foo
<tab> bar
)"_M;
关于c++ - 缩进感知原始字符串文字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75066606/
晚安,我正在与一些合作伙伴使用 javaFx 制作一个应用程序;我们的想法是它将在 Windows 和 Linux 中使用。我们进行了一些测试,发现应用程序在两种操作系统中的显示有所不同。 我们正在使
我有一项服务,可通过 CreateProcessAsUser 将可执行文件启动到用户 session 中,并在 STARTUPINFO 参数中指定桌面。它运行良好。 我的可执行文件没有显示出来,也没有
当每个文件写入集群时,HDFS 会创建一个复制管道。假设有两个 Rack 1 和 5。根据 Rack 感知,第一个 block 将被保存到 Rack 1,其他两个复制 block 将被插入 Rack
我正在做一个 Django 项目,我对时区感到困惑。 我有一个事件对象,它有 publish_start 和 publish_end 日期。 控制台输出示例; campaingObject.publi
我在下面有一个函数,它通过将字体 (.ttf) 复制到 Windows 字体文件夹然后触发 WM_FONTCHANGE 消息将其安装到 Windows 中。但是,该字体不会立即在 Windows 资源
是否有类似 grep 的 Unix/Linux 命令行工具可以理解由 log4j 或 logback 打印的日志文件中的 Java 堆栈跟踪?该工具应该理解堆栈跟踪由多行组成。 典型的用例是在查看存储
每次我在我的 SCM 中看到诸如导入或方法签名更改(例如变量的重命名)之类的冲突时,我想知道是否有类似语言感知的 diff/merge 方法可以处理更烦人的小更改发生在共享项目上。有什么东西可以在 U
我使用 astyanax 连接池定义如下: ipSeeds = "LOAD_BALANCER_HOST:9160"; conPool.setSeeds(ipSeeds) .setDiscoveryTy
据我所知,OCaml 中的字符串只是简单的字节序列。他们没有编码的概念。 这对于大多数用途来说都很好。但是,标准库的某些部分对以单字节字符集编码的字符串做出了假设,例如 printf 的对齐功能: #
据我所知,OCaml 中的字符串只是简单的字节序列。他们没有编码的概念。 这对于大多数用途来说都很好。但是,标准库的某些部分对以单字节字符集编码的字符串做出了假设,例如 printf 的对齐功能: #
我正在使用 this enhanced version of WebClient登录网站: public class CookieAwareWebClient : WebClient {
我正在尝试将 Awareness API 集成到一个新项目中,但我遇到了一条错误消息:ACL_ACCESS_DENIED 状态代码:7503。 我也在其他项目中集成了 Awareness API,但上
有什么方法可以定义一个 spring bean,当 session 中的数据发生变化时,它会得到通知? 如果可能的话,我还想知道纯 Java 解决方案。我想要的只是当我在 httpsession 中添
有没有方便有效的方式以 NUMA 感知方式使用 cpp 标准容器 API? 我想在 cpp 环境中执行 OpenMP 并行稀疏矩阵 vector 乘法。要分配和初始化与 NUMA 域有关的 vecto
我正在创建一个程序,它使用 SetWindowPos() 从另一个进程移动/调整窗口大小。我自己的程序是 PROCESS_PER_MONITOR_DPI_AWARE。其他程序可以是 PROCESS_D
我一直在研究许多 JDBC 连接池,但我有一个特定的要求,即池需要是 JTA 感知的,这给我留下了 Apache DBCP 和 OW2 XAPool 的简短列表。我查看的其他池(c3p0、Proxoo
我有一个 php 脚本,可以在服务器上发出一系列请求。第一个请求将是登录请求。 问题是 file_get_contents 似乎每次都创建一个新 session ,那么我怎样才能让它感知 sessio
我有一个整数,表示 unix 纪元之后的微秒数。 (格林威治标准时间) 如何使用 astype 将 1349863207154117 转换为 pandas.Timestamp("2012-10-10T
我有一个 Web 服务,我正在尝试将变量 Autowiring 到其中。这是类(class): package com.xetius.isales.pr7.service; import java.u
再会! 我已经在 WPF 应用程序上工作了一段时间(作为一种学习体验,哦,天哪,这是一种学习体验),它终于可以发布了。发布意味着将其安装在我的 HTPC 上,用于浏览我的电影收藏。 我在运行 1920
我是一名优秀的程序员,十分优秀!