Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass arbitrary code block to C macro as a param

Tags:

c++

macros

comma

I want to create a macro that will accept arbitrary code block as its parameter like

FOR_VECTOR( type, vect, code_block ) \
  for( vector<type>::iterator i=vect.begin(); i!=vect.end(); ++i ) { \
    code_block; \
  }

The problem is the code block in parameter, which may contain arbitrary number of , and ) characters.

Is there any good solution?

like image 220
user2732454 Avatar asked Aug 31 '13 21:08

user2732454


2 Answers

There are a number of possible solutions.

If you need only one expression (not a full-fashioned code block) - you can just enclose it in ( and )

FOR_VECTOR( int, v, (func(i,1)) )

will work - (func(i,1)) is treated like single macro argument

Another partial solution is variadic macros, if your preprocessor supports them.

You can define macros

#define COMMA ,
#define LPAR (
#define RPAR )

and use them to form your code block insted of real ( , and )

FOR_VECTOR( int, v, func LPAR i COMMA 1 RPAR )

It is not very readable though.

Or you can do a trick with commenting out quotes of a string literal after macro substitution:

FOR_VECTOR( type, vect, code_block ) \
  for( vector<type>::iterator i=vect.begin(); i!=vect.end(); ++i ) { \
    /code_block/; \
  }

FOR_VECTOR( int, v, *"*/ func(i,1); proc(i,2); /*"* )
like image 76
mas.morozov Avatar answered Sep 29 '22 13:09

mas.morozov


As @mas.morozov mentioned, you can use variadic macros:

#include <iostream>
#include <vector>

#define FOR_VECTOR( type, vect, ... ) \
  for( std::vector<type>::iterator i=vect.begin(); i!=vect.end(); ++i ) { \
    __VA_ARGS__ \
  }

int main()
{
    std::vector<int> v = {1, 2, 3, 4, 5, 6};
    FOR_VECTOR(int, v, {
        std::cout << *i << std::endl;
    })
}

You can play with it online here: https://godbolt.org/z/oLWV-z

I found that solution here: https://mort.coffee/home/obscure-c-features/

You can also make a more generic FOR_CONTAINER macro:

#include <iostream>
#include <vector>

#define FOR_CONTAINER( container, ... ) \
  for( decltype(container)::iterator i=container.begin(); i!=container.end(); ++i ) { \
    __VA_ARGS__ \
  }

int main()
{
    std::vector<int> v = {1, 2, 3, 4, 5, 6};
    FOR_CONTAINER(v, {
        std::cout << *i << std::endl;
    })
}

Try it here: https://godbolt.org/z/9Gzqja

like image 43
Michal Fapso Avatar answered Sep 29 '22 12:09

Michal Fapso