variadic macros: can i control the arguments?

see these 2 macros:
1
2
3
4
#define event(event1) void event1
#define events(...) event(__VA_ARGS__)
//see the sample:
events(hello(),hey(int x));

i get an error about using 2 arguments instead 1.
how can i control the number of arguments?
The problem is that what you're trying to do is not possible.
You want this:
 
events(hello(),hey(int x))
to expand into this:
1
2
event(hello())
event(hey(int x))

There's just no way to do this with precompiler macros.
I am still learing c++, and like to have someone input.
I usually don't use macros, it is confusing me.

If I want to pass in functions as arguments, I do as below:


#include <iostream>
#include <functional>
using namespace std;

void test1()
{

cout << "func1 call, no arg." << endl;
}

void test2(std::string str)
{
cout << "func2 call with arg: " << str << endl;
}


void functionCall( std::function< void() > function1, std::function<void(std::string) > function2)
{
function1();
function2("hello");
}

int main()
{

functionCall( test1, test2);

}


Don't hijack other people's threads. Start your own thread to ask your question.
helios: i can do macros for 1 argument, other for 2 arguments.... and so on..
then can i combine them to be just 1 macro used?
It depends on the particular compiler. According to the standard, the preprocessor domesn't need to allow macro overloading. So something like
1
2
#define F(x) do something
#define F(x, y) do something else 
may or may not work.
don't work.
but see my sample:
1
2
3
4
5
6
7
8
9
#define events1(event1) void event1
#define events2(event1, event2) void event1; void event2
#define events3(event1, event2, event3) void event1; void event2; void event3
#define events4(event1, event2, event3, event4) void event1; void event2; void event3; void event4
#define events5(event1, event2, event3, event4, event5) void event1; void event2; void event3; void event4; void event5

#define events(...) events3(__VA_ARGS__)

events(hello(),hey(int x), world());

can i change the 'events' for be compatible with all 'eventsx'?
No. Like I said, there's no way to do that using just the preprocessor.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#define EXPAND_0(...) __VA_ARGS__
#define EXPAND_1(...) EXPAND_0(EXPAND_0(__VA_ARGS__))
#define EXPAND_2(...) EXPAND_1(EXPAND_1(__VA_ARGS__))
#define EXPAND(...) EXPAND_2(EXPAND_2(__VA_ARGS__))

#define NOTHING
#define STOP(...)
#define IS_END_OF_LIST(...) _, STOP

#define NEXT(test, next) NEXT1(IS_END_OF_LIST test, next)
#define NEXT0(test, next, ...) next
#define NEXT1(test, next) NEXT0(test, next, _)

#define TRANSFORM0(f, x, next, ...)                 \
  f(x) NEXT(next, TRANSFORM1) NOTHING (f, next, __VA_ARGS__)
#define TRANSFORM1(f, x, next, ...)                 \
  f(x) NEXT(next, TRANSFORM0) NOTHING (f, next, __VA_ARGS__)

#define TRANSFORM(f, ...)                                   \
  EXPAND(TRANSFORM1(f, __VA_ARGS__, (end of arguments), _))

#define EVENT(x) void x;
#define DECLARE_EVENTS(...) TRANSFORM(EVENT, __VA_ARGS__)

DECLARE_EVENTS(hello(), hey(int x), world())

void hello();
void hey(int x);
void world();
Last edited on
mbozzi: thank you so much.
but can you explain how it works, please?
It may be worthwhile to read Chapter 3 (The chapter named "Macros") of one of the CPP (C Preprocessor) manuals here:
https://gcc.gnu.org/onlinedocs/

Anyways I'm going to walk through a macroexpansion. Here's the program again:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define EXPAND_0(...) __VA_ARGS__
#define EXPAND_1(...) EXPAND_0(EXPAND_0(__VA_ARGS__))
#define EXPAND_2(...) EXPAND_1(EXPAND_1(__VA_ARGS__))
#define EXPAND(...) EXPAND_2(EXPAND_2(__VA_ARGS__))

#define NOTHING
#define STOP(...)
#define IS_END_OF_LIST(...) _, STOP

#define NEXT(test, next) NEXT1(IS_END_OF_LIST test, next)
#define NEXT0(test, next, ...) next
#define NEXT1(test, next) NEXT0(test, next, _)

#define TRANSFORM0(f, x, next, ...)                 \
  f(x) NEXT(next, TRANSFORM1) NOTHING (f, next, __VA_ARGS__)
#define TRANSFORM1(f, x, next, ...)                 \
  f(x) NEXT(next, TRANSFORM0) NOTHING (f, next, __VA_ARGS__)

#define TRANSFORM(f, ...)                                   \
  EXPAND(TRANSFORM1(f, __VA_ARGS__, (end of arguments), _)) 


First, note the behaviour of NEXT(A, B) in the following cases. First, in the common case where A isn't parenthesized, NEXT(A, B) simply results in B:
1
2
3
4
  NEXT(x, y)
      → NEXT1(IS_END_OF_LIST x, y)
      → NEXT0(IS_END_OF_LIST x, y, _)
      → y

In the base case, where the first argument A is parenthesized, substitution creates a function-like macro invocation, and results in STOP:
1
2
3
4
  NEXT((end of arguments), y)
      → NEXT1(IS_END_OF_LIST (end of arguments), y)
      → NEXT0(_, STOP, y, _)
      → STOP


So given
#define F(x) f(x)
Then
TRANSFORM(F, 1, 2, 3)
Macroexpands into
1
2
3
4
  TRANSFORM(F, 1, 2, 3) →
    EXPAND(   
      T
    )

Where
1
2
3
4
  T ≡ TRANSFORM1(F, 1, 2, 3, (end of arguments), _)
    → F(1)
      NEXT(2, TRANSFORM0) NOTHING (F, 2, 3, (end of arguments), _)
    → F(1) TRANSFORM0(F, 2, 3, (end of arguments), _)

But no more expansion occurs on its own. This is why we passed T to EXPAND,
which macroexpands its arguments repeatedly:
1
2
3
  EXPAND(T) ≡ F(1) TRANSFORM0(F, 2, 3, (end of arguments) _) 
    → f(1) f(2) NEXT(3, TRANSFORM1) NOTHING (F, 3, (end of arguments), _)
    → f(1) f(2) TRANSFORM1(F, 3, (end of arguments), _)

Which is expanded again:
1
2
3
4
  EXPAND(T) ≡ f(1) f(2) TRANSFORM1(F, 3, (end of arguments), _)
    → f(1) f(2) f(3)
      NEXT((end of arguments), TRANSFORM0) NOTHING (F, (end of arguments), _)
    → f(1) f(2) f(3) STOP(F, (end of arguments), _)

And once again:
1
2
  EXPAND(T) ≡ f(1) f(2) f(3) STOP(F, (end of arguments), _)
    → f(1) f(2) f(3)

The result is expanded further, but there are no more macros to expand; this is a fixed point - further expansions are idempotent.

The pitfalls here are that
1.) no argument to TRANSFORM (save the first and second) may be parenthesized
2.) EXPAND needs to expand its operands enough times. Extend the definition as required.
Last edited on
2.) EXPAND needs to expand its operands enough times. Extend the definition as required.
So no amount of code is able to handle arbitrarily many arguments, right? The amount of code grows linearly with the maximum number of arguments. I figured that'd be the case, as it would otherwise be equivalent to Turing-completeness, IINM.
thank you so much for all to all
thank you
The amount of code grows linearly with the maximum number of arguments.

EXPAND() generates a tree of calls - a binary tree, in this case.

Some testing suggests that for the GNU CPP, adding ~5000 arguments will cause you to run out of memory.
thank you so much for all.
now it a little out off topic: can you give me another link for i learn much more about macros?
i only know the basics, because the manuals don't have so much about it
Topic archived. No new replies allowed.