带空格参数的字符串化运算符
Stringize operator on argument with spaces
我有
#define ARG(TEXT , REPLACEMENT) replace(#TEXT, REPLACEMENT)
所以
QString str= QString("%ONE, %TWO").ARG(%ONE, "1").ARG(%TWO, "2");
变成
str= QString("%ONE, %TWO").replace("%ONE", "1").replace("%TWO", "2");
//str = "1, 2"
问题是 VS2019 在格式化代码 (Edit.FormatSelection) 时将 % 符号解释为运算符并添加了一个白色 space
QString str= QString("%ONE, %TWO").ARG(% ONE, "1").ARG(% TWO, "2");
(我认为这是 VS 中的错误)。代码编译时没有警告。
由于我正在处理一些具有此“功能”的古老代码,我担心自动格式化包含此功能的文本会破坏功能。
有没有办法在编译时检测具有 space(s) 的宏的此类参数?
不太可能。
Visual Studio 处理源代码,首先没有 运行 预处理器,也没有执行计算预处理器是否会从根本上改变其格式的行的相当困难的计算。
此外,人们实际上不再以这种方式使用宏,或者不应该(我们有便宜的功能!)。
所以这并不是格式化功能所期望的。
如果您可以修改代码,让用户编写 .ARG("%ONE", "1")
,那么问题不仅会消失,而且会更加一致。
否则,您将不得不坚持手动格式化代码。
显然,在未来十年的某个时候,C++ 将提供更好的解决方案,而且确实可能有比我在下面提供的解决方案更简单的解决方案,但它可能是一个起点。
此版本使用 Boost 预处理器库进行重复,如果 C++ 允许字符串文字作为模板参数,则可以直接使用模板编写,该功能尚未进入动机标准我只能猜测。所以它实际上并没有测试参数是否没有空格;相反,它测试前 64 个字符中没有空格(其中 64 几乎是一个几乎完全任意的数字,可以根据您的需要进行更改)。我使用了 Boost 预处理器库;如果出于某种原因您不想使用 Boost,您可以使用自己的专用宏来执行此操作。
#include <boost/preprocessor/repetition/repeat.hpp>
#define NO_SPACE_AT_N(z, N, s) && (N >= sizeof(s) || s[N] != ' ')
#define NO_SPACE(s) true BOOST_PP_REPEAT(64, NO_SPACE_AT_N, s)
// Arbitrary constant, change as needed---^
// Produce a compile time error if there's a space.
template<bool ok> struct NoSpace {
const char* operator()(const char* s) {
static_assert(ok, "Unexpected space");
return s;
}
};
#define ARG(TEXT, REPL) replace(NoSpace<NO_SPACE(#TEXT)>()(#TEXT), REPL)
(在 gcc.godbolt 上测试。)
Is there a way at compile time to detect such arguments to a macro having space(s)?
我会这样做:
#define ARG(TEXT, REPLACEMENT) \
replace([]{ \
static constexpr char x[] = #TEXT; \
static_assert(x[0] == '%' && x[1] != ' '); \
return x; \
}(), REPLACEMENT)
如果问题是当 ARG
的第一个参数包含 space 时产生编译错误,我设法让它工作:
#include <cstdlib>
template<size_t N>
constexpr int string_validate( const char (&s)[N] )
{
for (int i = 0; i < N; ++i)
if ( s[i] == ' ' )
return 0;
return 1;
}
template<int N> void assert_const() { static_assert(N, "string validation failed"); }
int replace(char const *, char const *) { return 0; } // dummy for example
#define ARG(TEXT , REPLACEMENT) replace((assert_const<string_validate(#TEXT)>(), #TEXT), REPLACEMENT)
int main()
{
auto b = ARG(%TWO, "2");
auto a = ARG(% ONE, "1"); // causes assertion failure
}
毫无疑问,还有一条更短的路。在 C++20 之前,您不能在模板参数中使用字符串文字,因此 constexpr 函数可以从字符串文字中生成一个整数,然后我们可以在编译时通过将其用作模板参数来检查该整数。
我有
#define ARG(TEXT , REPLACEMENT) replace(#TEXT, REPLACEMENT)
所以
QString str= QString("%ONE, %TWO").ARG(%ONE, "1").ARG(%TWO, "2");
变成
str= QString("%ONE, %TWO").replace("%ONE", "1").replace("%TWO", "2");
//str = "1, 2"
问题是 VS2019 在格式化代码 (Edit.FormatSelection) 时将 % 符号解释为运算符并添加了一个白色 space
QString str= QString("%ONE, %TWO").ARG(% ONE, "1").ARG(% TWO, "2");
(我认为这是 VS 中的错误)。代码编译时没有警告。
由于我正在处理一些具有此“功能”的古老代码,我担心自动格式化包含此功能的文本会破坏功能。
有没有办法在编译时检测具有 space(s) 的宏的此类参数?
不太可能。
Visual Studio 处理源代码,首先没有 运行 预处理器,也没有执行计算预处理器是否会从根本上改变其格式的行的相当困难的计算。
此外,人们实际上不再以这种方式使用宏,或者不应该(我们有便宜的功能!)。
所以这并不是格式化功能所期望的。
如果您可以修改代码,让用户编写 .ARG("%ONE", "1")
,那么问题不仅会消失,而且会更加一致。
否则,您将不得不坚持手动格式化代码。
显然,在未来十年的某个时候,C++ 将提供更好的解决方案,而且确实可能有比我在下面提供的解决方案更简单的解决方案,但它可能是一个起点。
此版本使用 Boost 预处理器库进行重复,如果 C++ 允许字符串文字作为模板参数,则可以直接使用模板编写,该功能尚未进入动机标准我只能猜测。所以它实际上并没有测试参数是否没有空格;相反,它测试前 64 个字符中没有空格(其中 64 几乎是一个几乎完全任意的数字,可以根据您的需要进行更改)。我使用了 Boost 预处理器库;如果出于某种原因您不想使用 Boost,您可以使用自己的专用宏来执行此操作。
#include <boost/preprocessor/repetition/repeat.hpp>
#define NO_SPACE_AT_N(z, N, s) && (N >= sizeof(s) || s[N] != ' ')
#define NO_SPACE(s) true BOOST_PP_REPEAT(64, NO_SPACE_AT_N, s)
// Arbitrary constant, change as needed---^
// Produce a compile time error if there's a space.
template<bool ok> struct NoSpace {
const char* operator()(const char* s) {
static_assert(ok, "Unexpected space");
return s;
}
};
#define ARG(TEXT, REPL) replace(NoSpace<NO_SPACE(#TEXT)>()(#TEXT), REPL)
(在 gcc.godbolt 上测试。)
Is there a way at compile time to detect such arguments to a macro having space(s)?
我会这样做:
#define ARG(TEXT, REPLACEMENT) \
replace([]{ \
static constexpr char x[] = #TEXT; \
static_assert(x[0] == '%' && x[1] != ' '); \
return x; \
}(), REPLACEMENT)
如果问题是当 ARG
的第一个参数包含 space 时产生编译错误,我设法让它工作:
#include <cstdlib>
template<size_t N>
constexpr int string_validate( const char (&s)[N] )
{
for (int i = 0; i < N; ++i)
if ( s[i] == ' ' )
return 0;
return 1;
}
template<int N> void assert_const() { static_assert(N, "string validation failed"); }
int replace(char const *, char const *) { return 0; } // dummy for example
#define ARG(TEXT , REPLACEMENT) replace((assert_const<string_validate(#TEXT)>(), #TEXT), REPLACEMENT)
int main()
{
auto b = ARG(%TWO, "2");
auto a = ARG(% ONE, "1"); // causes assertion failure
}
毫无疑问,还有一条更短的路。在 C++20 之前,您不能在模板参数中使用字符串文字,因此 constexpr 函数可以从字符串文字中生成一个整数,然后我们可以在编译时通过将其用作模板参数来检查该整数。