我发现Microsoft Visual Studio编译器和gcc以不同的方式预处理以下小片段:
# define M3(x, y, z) x + y + z
# define M2(x, y) M3(x, y)
# define P(x, y) {x, y}
# define M(x, y) M2(x, P(x, y))
M(a, b)
Run Code Online (Sandbox Code Playgroud)
'gcc -E'给出以下内容:
a + {a + b}
Run Code Online (Sandbox Code Playgroud)
,'cl/E'发出关于缺少宏参数的警告并产生以下输出:
a + {a, b} +
Run Code Online (Sandbox Code Playgroud)
似乎来自嵌套宏扩展的逗号不被视为参数分隔符.不幸的是,我没有找到cl预处理器中实现的算法的描述,所以我不确定我的建议是否正确.有谁知道cl预处理器是如何工作的,它的算法和gcc之间有什么区别?以及如何解释观察到的行为?
考虑一个简单的宏:
#define ECHO(x) x
ECHO(foo(1, 2))
Run Code Online (Sandbox Code Playgroud)
这会产生我们期望的确切输出:
foo(1, 2)
Run Code Online (Sandbox Code Playgroud)
上面的示例有效,因为预处理器可以识别与函数调用相邻的括号.
现在考虑如果我使用模板而不是函数调用会发生什么:
ECHO(template<int, bool>)
Run Code Online (Sandbox Code Playgroud)
这会导致错误,因为预处理器会将宏template<int和bool>作为两个单独的参数解释为宏.预处理器无法识别<>范围!
无论如何在宏中使用这样的模板?
我有一些代码基本上可以浓缩为
#define FOO(a)
FOO(std::map<int, int>);
Run Code Online (Sandbox Code Playgroud)
但它会发出编译错误(宏的实际参数太多FOO)。
显然,预处理器认为我已经提供了std::map<int和int>作为参数。
有没有办法解决这个问题?预处理器不会以这种方式处理带逗号的带引号的字符串。
tree.h中
template<typename Functor, char Operator>
class binary_operation : public node
{
// ... unimportant details ...
unsigned evaluate() const;
void print(std::ostream& os) const;
};
typedef binary_operation<std::plus<unsigned>, '+'> addition;
typedef binary_operation<std::multiplies<unsigned>, '*'> multiplication;
// ...
Run Code Online (Sandbox Code Playgroud)
tree.cpp
template<typename Functor, char Operator>
unsigned binary_operation<Functor, Operator>::evaluate() const
{
// ... unimportant details ...
}
template<typename Functor, char Operator>
void binary_operation<Functor, Operator>::print(std::ostream& os) const
{
// ... unimportant details ...
}
template class binary_operation<std::plus<unsigned>, '+'>;
template class binary_operation<std::multiplies<unsigned>, '*'>;
// ...
Run Code Online (Sandbox Code Playgroud)
如您所见,头文件中的typedef与实现文件中的显式类模板实例之间存在一些代码重复.有没有办法摆脱不需要像往常一样在头文件中放置"所有"的重复?
如图所示,如果将具有多个参数的模板实例化作为参数传递给宏,则C++预处理器将失败.
请参阅下面的示例.
#include <stdio.h>
#define FOO(v) printf("%d\n",v::val())
template<int N>
struct bar {
static int val() { return N; }
};
template<int N, int M>
struct baz {
static int val() { return N+M; }
};
int main() {
printf("%d\n",bar<1>::val());
printf("%d\n",baz<1,2>::val());
FOO(bar<10>); // OK
FOO(baz<20,30>); // error: too many arguments provided to function-like macro invocation
FOO((baz<20,30>)); // error: '::val' has not been declared
}
Run Code Online (Sandbox Code Playgroud)
用clang ++和g ++ 测试
它应该被视为一个错误吗?
在编译时-Wnullable-to-nonnull-conversion,我们会使用以下代码获得正确的警告:
NSString * _Nullable maybeFoo = @"foo";
^(NSString * _Nonnull bar) {
}(maybeFoo);
Tests.m:32:7: error: implicit conversion from nullable pointer 'NSString * _Nullable' to non-nullable pointer type 'NSString * _Nonnull' [-Werror,-Wnullable-to-nonnull-conversion]
}(maybeFoo);
^
1 error generated.
Run Code Online (Sandbox Code Playgroud)
我如何安全地foo从一个演员阵容NSString * _Nullable到一个NSString * _Nonnull?
到目前为止我有最好的解决方案
我想出的最好的就是这个宏:
#define ForceUnwrap(type, nullableExpression) ^type _Nonnull () { \
type _Nullable maybeValue___ = nullableExpression; \
if (maybeValue___) { \
return (type _Nonnull) maybeValue___; \
} else { \
NSLog(@"Attempted to …Run Code Online (Sandbox Code Playgroud) 在后C 预处理器递归宏中使用 Paul Fultz II 的解决方案,我想扩展无限数量的括号宏参数,例如
#define MY_CHAIN (alpha) (beta) (gamma)
Run Code Online (Sandbox Code Playgroud)
到一个逗号分隔的列表中,该列表可以传递给可变参数宏,例如
CHAIN_COMMA(MY_CHAIN) // alpha, beta, gamma
Run Code Online (Sandbox Code Playgroud)
在下面的示例中[alpha] [beta] [gamma],我可以扩展为大括号并使用我尝试过的所有内容分隔列表,但逗号除外alpha :: beta :: gamma。
这是我的完整(编译)代码:
#include <iostream>
using namespace std;
// unrelated macro utilities
#define SEE(expression) cout << #expression ": " << STR(expression) << endl;
#define CMD(function, ...) function(__VA_ARGS__)
#define STR(s) CMD(STR_, s)
#define STR_(s) #s
// concatenation
#define CAT(x, y) CAT_(x, y)
#define CAT_(x,y) x ## y // error from CHAIN_COMMA: passed 4 …Run Code Online (Sandbox Code Playgroud) 以下代码编译正常.
#define CMD_MACRO(pp, cmd) \
{ \
if (pp)\
{ cmd; } \
}
template<class T> void operate_on(T &data, char c) {
data=data+1;
};
int main() {
int book=4;
char c;
CMD_MACRO(book, {
operate_on<int>(book, c);
});
};
Run Code Online (Sandbox Code Playgroud)
请注意,我的代码中的实际宏更复杂,我给出了一个简化版本,这可能没有太大的逻辑意义
现在,如果我在函数中添加另一个模板参数,它会给出编译错误(代码注释中解释的问题):
template<class T, bool b> void operate_on(T &data, char c) {
data=data+1;
};
int main() {
int book=4;
char c;
CMD_MACRO(book, {
operate_on<int, false>(book, c); /* here the "," between int and
false is being treated
as separating arguments to CMD_MACRO,
instead …Run Code Online (Sandbox Code Playgroud) 这是在编译时返回的异常错误,仅限于一些编译器参数.
好的 g++ -std=c++11 -m64 -O3 -DNDEBUG
但是g++ -std=c++11 -m64 -Wall -g,出现此问题:
宏"htonl"传递了7个参数,但只占用了1个
码:
const unsigned int h = htonl(hash::CompileTime<'A', 'S', 't', 'r', 'i', 'n', 'g'>::hash);
Run Code Online (Sandbox Code Playgroud)
我不确定问题是来自调用htonl还是来自我的模板化的哈希.
你知道怎么解决这个问题吗?
其他信息:
template<const char C0, const char C1 = '\0', const char C2 = '\0',
const char C3 = '\0', const char C4 = '\0', const char C5 = '\0',
const char C6 = '\0', const char C7 = '\0', const char C8 = '\0',
const char C9 = …Run Code Online (Sandbox Code Playgroud) 我在一个函数中有一个宏来实例化给定类型的变量(并做一些其他不相关的事情).
基本上MACRO(foo, f)扩展到foo f;
但是如果foo说是a std::map<int, int>那么由于额外的逗号,扩展失败了.
我通过写作typedef std::map<int, int> bar;然后解决这个问题MACRO(bar, b).
我很担心,我正在泄漏typedef程序源,这可能会在将来引起我的问题.
那么,typedefs持续多久了?