为了账号安全,请及时绑定邮箱和手机立即绑定

我们可以有递归宏吗?

/ 猿问

我们可以有递归宏吗?

C++ C
BIG阳 2019-09-21 11:19:19

我想知道我们是否可以在C / C ++中使用递归宏?如果是,请提供示例示例。


第二件事:为什么我无法执行以下代码?我在做什么错?是因为递归宏吗?


# define pr(n) ((n==1)? 1 : pr(n-1))

void main ()

{

    int a=5;

    cout<<"result: "<< pr(5) <<endl;

    getch();

}


查看完整描述

3 回答

?
HUWWW

您的编译器可能提供了仅预处理而不实际编译的选项。如果您要在宏中查找问题,这将很有用。例如使用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();

}

如您所见,它不是递归的。pr(x)在预处理期间仅被替换一次。要记住的重要一点是,预处理器所做的全部工作就是盲目地将一个文本字符串替换为另一个文本字符串,而实际上并没有计算类似的表达式(x == 1)。


您的代码无法编译的原因pr(5 -1)是没有被预处理器替换,因此它最终作为对未定义函数的调用而出现在源代码中。


查看完整回答
反对 回复 2019-09-21
?
交互式爱情

宏不直接递归扩展,但是有解决方法。当预处理器扫描并扩展时pr(5):


pr(5)

^

它创建了一个禁用的上下文,因此当它pr再次出现时:


((5==1)? 1 : pr(5-1))

             ^

无论我们尝试什么,它都会变成蓝色,并且无法再扩展。但是我们可以通过使用延迟表达式和一些间接方式来防止宏变成蓝色。


# 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))

所以现在它将像这样扩展:


pr(5) // Expands to ((5==1)? 1 : pr_id ()(5 -1))

这是完美的,因为pr从未涂成蓝色。我们只需要应用另一个扫描即可使其进一步扩展:


EXPAND(pr(5)) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : pr_id ()(5 -1 -1)))

我们可以应用两次扫描以使其进一步扩展:


EXPAND(EXPAND(pr(5))) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : ((5 -1 -1==1)? 1 : pr_id ()(5 -1 -1 -1))))

但是,由于没有终止条件,因此我们永远无法应用足够的扫描。我不确定您要完成什么,但是如果您对如何创建递归宏感到好奇,下面是一个如何创建递归重复宏的示例。


首先是要应用大量扫描的宏:


#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__

接下来,一个concat宏,对模式匹配很有用:


#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)

#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

递增和递减计数器:


#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

一些对条件有用的宏:


#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)

综上所述,我们可以创建一个repeat宏:


#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

因此,是的,通过一些变通办法,您可以在C / C ++中具有递归宏。


查看完整回答
反对 回复 2019-09-21
?
慕妹3242003

您不应该在C或C ++中具有递归宏。

C ++标准第16.3.4节第2段中的相关语言:

如果在替换列表的扫描期间找到了要替换的宏的名称(不包括源文件的其余预处理令牌),则不会替换该宏。此外,如果任何嵌套的替换遇到要替换的宏的名称,则不会替换它。这些未替换的宏名称预处理令牌不再可用于进一步替换,即使它们后来在本来会被替换的宏名称预处理令牌的上下文中被(重新)检查了也是如此。

这种语言有一些摆动的空间。在多个宏相互调用的情况下,有一个灰色区域,在该区域中措辞并未完全说明应该做什么。关于该语言律师问题,有一个针对C ++标准的活跃问题;参见http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#268。

忽略该语言律师问题,每个编译器供应商都了解其意图:

在C或C ++中不允许使用递归宏。


查看完整回答
反对 回复 2019-09-21

添加回答

回复

举报

0/150
提交
取消
意见反馈 邀请有奖 帮助中心 APP下载
官方微信