Home  >  Article  >  Backend Development  >  How Can We Implement Recursive Macros in C?

How Can We Implement Recursive Macros in C?

Barbara Streisand
Barbara StreisandOriginal
2024-11-17 19:49:02139browse

How Can We Implement Recursive Macros in C?

Understanding Macro Recursion for Macro Arguments

In C programming, macros offer a powerful tool for text manipulation. One intriguing aspect is the ability to use macros on the arguments of other macros. However, this presents a technical challenge, as recursive macros are generally not allowed in the language.

The Problem: Recursive Macros

Consider the scenario where we wish to create a foreach macro, named PRINT_ALL, that applies a given macro, PRINT, to a list of arguments. For instance:

int a = 1, b = 3, d = 0;
PRINT_ALL(a,b,d);

This would invoke the PRINT macro on each of the variables a, b, and d. The naïve approach might employ a recursive macro, as follows:

#define FIRST_ARG(arg,...) arg
#define AFTER_FIRST_ARG(arg,...) , ##__VA_ARGS__
#define PRINT(a) printf(#a": %d", a)
#define PRINT_ALL PRINT(FIRST_ARG(__VA_ARGS__)); PRINT_ALL(AFTER_FIRST_ARG(__VA_ARGS__))

However, this approach poses two problems: macros cannot call themselves recursively, and it lacks a stopping condition to halt the recursion.

A Recursive Workaround

To overcome these hurdles, a clever workaround leverages a technique known as macro eval-recursion. The key idea is to emit macro text that simulates a macro call without actually invoking the macro itself.

Consider the following macro:

#define MAP_OUT

If we have the following macros:

#define A(x) x B MAP_OUT (x)
#define B(x) x A MAP_OUT (x)

Evaluating the macro A(blah) produces the output text:

blah B (blah)

This text serves as a macro replacement placeholder. It can be passed back into the preprocessor to be further expanded, continuing the macro evaluation process.

To facilitate this recursion, a series of EVAL macros are defined:

#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL(...)EVAL4(EVAL4(EVAL4(__VA_ARGS__)))

Each macro applies multiple levels of evaluation, thus amplifying the effect of the macros being applied.

Stopping the Recursion

To control the recursion, a special macro, MAP_END, is defined:

#define MAP_END(...)

Evaluating this macro does nothing, effectively terminating the recursion.

The next challenge is to determine when to use MAP_END instead of continuing the recursion. To achieve this, a MAP_NEXT macro compares a list item against a special end-of-list marker. If they match, it returns MAP_END; otherwise, it returns the next parameter:

#define MAP_GET_END() 0, MAP_END
#define MAP_NEXT0(item, next, ...) next MAP_OUT
#define MAP_NEXT1(item, next) MAP_NEXT0(item, next,0)
#define MAP_NEXT(item, next) MAP_NEXT1(MAP_GET_END item, next)

By carefully constructing the MAP_NEXT macro, we can control whether the recursion continues or ends.

Final Implementation

Combining these building blocks, we can create the MAP macro that iterates over a list and applies a given macro to each item:

#define MAP(f,...)EVAL(MAP1(f,__VA_ARGS__,(),0))

This macro works by placing an end-of-list marker at the end of the list, along with an extra argument to ensure ANSI compliance. It then passes the list through multiple EVAL macro calls and returns the result.

This technique provides a creative solution to the problem of using macros on macro arguments. It enables sophisticated macro manipulation capabilities, allowing programmers to extend the functionality of the preprocessor in novel ways.

The above is the detailed content of How Can We Implement Recursive Macros in C?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn