use*_*292 50 c c++ macros c-preprocessor
我想知道我们是否可以在C/C++中使用递归宏?如果是,请提供示例.
第二件事:为什么我无法执行以下代码?我在做什么错?是因为递归宏吗?
# define pr(n) ((n==1)? 1 : pr(n-1))
void main ()
{
int a=5;
cout<<"result: "<< pr(5) <<endl;
getch();
}
Run Code Online (Sandbox Code Playgroud)
Pau*_* II 109
宏不会直接递归扩展,但有一些解决方法.当预处理器扫描并扩展时pr(5)
:
pr(5)
^
Run Code Online (Sandbox Code Playgroud)
它创建一个禁用上下文,以便在它pr
再次看到时:
((5==1)? 1 : pr(5-1))
^
Run Code Online (Sandbox Code Playgroud)
无论我们尝试什么,它都会变成蓝色,无法再展开.但是我们可以通过使用延迟表达式和一些间接来防止我们的宏变成蓝色:
# define EMPTY(...)
# define DEFER(...) __VA_ARGS__ EMPTY()
# define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)()
# define EXPAND(...) __VA_ARGS__
# define pr_id() pr
# define pr(n) ((n==1)? 1 : DEFER(pr_id)()(n-1))
Run Code Online (Sandbox Code Playgroud)
所以现在它会像这样扩展:
pr(5) // Expands to ((5==1)? 1 : pr_id ()(5 -1))
Run Code Online (Sandbox Code Playgroud)
这是完美的,因为pr
从未涂成蓝色.我们只需要应用另一个扫描来进一步扩展:
EXPAND(pr(5)) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : pr_id ()(5 -1 -1)))
Run Code Online (Sandbox Code Playgroud)
我们可以应用两次扫描以进一步扩展:
EXPAND(EXPAND(pr(5))) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : ((5 -1 -1==1)? 1 : pr_id ()(5 -1 -1 -1))))
Run Code Online (Sandbox Code Playgroud)
但是,由于没有终止条件,我们永远不能应用足够的扫描.我不确定你想要完成什么,但如果你对如何创建递归宏感到好奇,这里有一个如何创建递归重复宏的例子.
首先应用大量扫描的宏:
#define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__
Run Code Online (Sandbox Code Playgroud)
接下来,一个用于模式匹配的concat宏:
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
Run Code Online (Sandbox Code Playgroud)
增量和减量计数器:
#define INC(x) PRIMITIVE_CAT(INC_, x)
#define INC_0 1
#define INC_1 2
#define INC_2 3
#define INC_3 4
#define INC_4 5
#define INC_5 6
#define INC_6 7
#define INC_7 8
#define INC_8 9
#define INC_9 9
#define DEC(x) PRIMITIVE_CAT(DEC_, x)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define DEC_4 3
#define DEC_5 4
#define DEC_6 5
#define DEC_7 6
#define DEC_8 7
#define DEC_9 8
Run Code Online (Sandbox Code Playgroud)
一些对条件有用的宏:
#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
#define NOT_0 ~, 1,
#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
#define BOOL(x) COMPL(NOT(x))
#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t
#define IF(c) IIF(BOOL(c))
#define EAT(...)
#define EXPAND(...) __VA_ARGS__
#define WHEN(c) IF(c)(EXPAND, EAT)
Run Code Online (Sandbox Code Playgroud)
总而言之,我们可以创建一个重复宏:
#define REPEAT(count, macro, ...) \
WHEN(count) \
( \
OBSTRUCT(REPEAT_INDIRECT) () \
( \
DEC(count), macro, __VA_ARGS__ \
) \
OBSTRUCT(macro) \
( \
DEC(count), __VA_ARGS__ \
) \
)
#define REPEAT_INDIRECT() REPEAT
//An example of using this macro
#define M(i, _) i
EVAL(REPEAT(8, M, ~)) // 0 1 2 3 4 5 6 7
Run Code Online (Sandbox Code Playgroud)
所以,是的,通过一些变通方法,您可以在C/C++中使用递归宏.
ver*_*ald 20
您的编译器可能提供了一个只预处理,而不是实际编译的选项.如果您尝试在宏中查找问题,这非常有用.例如使用g++ -E
:
> g++ -E recursiveMacro.c
# 1 "recursiveMacro.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "recursiveMacro.c"
void main ()
{
int a=5;
cout<<"result: "<< ((5==1)? 1 : pr(5 -1)) <<endl;
getch();
}
Run Code Online (Sandbox Code Playgroud)
如您所见,它不是递归的.pr(x)
只在预处理过程中更换一次.需要记住的重要一点是,所有预处理器都会盲目地将一个文本字符串替换为另一个文本字符串,它实际上并不会像表达式那样评估表达式(x == 1)
.
您的代码无法编译的原因是它pr(5 -1)
没有被预处理器替换,因此它最终在源代码中作为对未定义函数的调用.
Dav*_*men 18
您不应该在C或C++中使用递归宏.
C++标准中的相关语言,第16.3.4节第2节:
如果在替换列表的扫描期间找到要替换的宏的名称(不包括源文件的其余预处理标记),则不会替换它.此外,如果任何嵌套替换遇到要替换的宏的名称,则不会替换它.这些未替换的宏名称预处理令牌不再可用于进一步替换,即使它们稍后(重新)检查在其中否则将替换该宏名称预处理令牌的上下文中.
这种语言有一些摆动空间.有多个宏相互调用,有一个灰色区域,其中的措辞并不能说明应该做什么.关于这种语言律师问题的C++标准存在一个积极的问题; 见http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#268.
忽略该语言律师问题,每个编译器供应商都理解意图:
C或C++中不允许使用递归宏.
Zde*_*vic 11
很可能你无法执行它,因为你无法编译它.此外,如果它可以正确编译,它将始终返回1.你的意思是(n==1)? 1 : n * pr(n-1)
.
宏不能递归.根据第16.3.4.2章(感谢Loki Astari),如果在替换列表中找到当前宏,它将保持原样,因此您pr
在定义中不会更改:
如果在替换列表的扫描期间找到要替换的宏的名称(不包括源文件的其余预处理标记),则不替换它.此外,如果任何嵌套替换遇到要替换的宏的名称,则不会替换它.这些未替换的宏名称预处理令牌不再可用于进一步替换,即使它们稍后(重新)检查在其中否则将替换该宏名称预处理令牌的上下文中.
你的来电:
cout<<"result: "<< pr(5) <<endl;
Run Code Online (Sandbox Code Playgroud)
由预处理器转换为:
cout<<"result: "<< (5==1)? 1 : pr(5-1) <<endl;
Run Code Online (Sandbox Code Playgroud)
在此期间,pr
宏的定义是"丢失",并且编译器显示错误,例如"'pr'未在此范围(事实)中声明",因为没有命名的函数pr
.
在C++中不鼓励使用宏.你为什么不写一个函数?
在这种情况下,您甚至可以编写模板函数,以便在编译时解析它,并将表现为常量值:
template <int n>
int pr() { pr<n-1>(); }
template <>
int pr<1>() { return 1; }
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
36746 次 |
最近记录: |