1

I have a C++ program where a certain pattern of blocks of code keeps repeating, and I am wondering if I can use C++ MACROS for the preprocessor to auto-generate this code. To be more precise I have blocks of code which look something like this:

for(std::size_t i=0;i< lc_.size(); i++)
{
  std::string str;
  state::MsData lc = data->lc(i);
  convert<data::ClassForLC>(lc.data(), str);
  lc_[i] = data::ClassForLC(str);
}

I then may have another block that looks like this:

for(std::size_t i=0;i< mmop_.size(); i++)
{
  std::string str;
  state::MvData mmop = data->mmop(i);
  convert<data::ClassForMMOP>(mmop.data(), str);
  mmop_[i] = data::ClassForMMOP(str);
}

As you can see the general pattern is something like this:

  for(std::size_t i=0;i< X_.size(); i++)
    {
      std::string str;
      Y X = data->X(i);
      convert<Z>(X.data(), str);
      X_[i] = Z(str);
    }

I was wondering if it is possible to define a macro REPLACE(X,Y,Z) which replaces X,Y,Z in the code above with whatever text I pass as parameter? Note: I use C++11. Thanks

5
  • Does this answer your question? Multi line preprocessor macros Commented Aug 13, 2021 at 18:26
  • 4
    It's possible - but don't do it ! It's a maintenance pain !. Also for(std::size_t i=0;i< lc_.size(); i++) length doesn't change in the loop so don't grab it each time through - depending on the collection .size() might be an expensive operation. Commented Aug 13, 2021 at 18:27
  • 3
    Just to add on to what @John3136 said, debugging becomes a huge nightmare. Instead, consider templated functions or passing in a function to do the non-generic part. Commented Aug 13, 2021 at 18:29
  • 2
    It's possible to generalize code like this so it only has to be written once. Function templates are a common solution. It's also possible to write code with macros, but you then risk all the pain that comes from using macros. Commented Aug 13, 2021 at 18:47
  • 2
    What happened to the ancient art of placing duplicate code into a function or subroutine? Rather than creating macros, try defining the code in a function, place into a header file and tag it with "inline". Commented Aug 13, 2021 at 18:48

3 Answers 3

5

Don't do that.

template<class Z, class X, class F>
void do_stuff( X&& x, F&& f ) {
  for(std::size_t i=0;i< x.size(); i++)
  {
    std::string str;
    auto tmp = f(i);
    convert<Z>(tmp.data(), str);
    x[i] = Z(str);
  }
}

write a template. We can use it like this:

do_stuff<data::ClassForMMOP>(
  mmop_,
  [&](std::size_t i){ return data->mmop(i); }
);

doing this with macros is possible, it is just a bad idea.

It is a bad idea for a number of reasons:

  1. The errors you get will be text substitution based.

  2. Debugging will often be impossible.

  3. There are lots of subtle bugs that can crop up in macro based generation that are very hard to spot.

With templates, you get something that the C++ compiler and debugger fully understands, and a bunch of "early" checks on correctness. The errors generated by templates are difficult, but infinitely easier than the macro errors.

But to actually do it in a macro:

#define MACRO(X,Y,Z) \
  X x = Y(); \
  Z(Y)

use \ before the newlines to make "everything on the same line" as far as the C++ preprocessor (well, more than that) is concerned.

In effect you are generating one extremely long bit of C++ code. As C++ does not rely on newlines, the macro works.

Sign up to request clarification or add additional context in comments.

5 Comments

Of all the reasons to not use simple macros, bad error reporting is really not the one. First of, most modern compilers are preprocessors themselves and they usually accurately report errors in macros (with text like "While expanding macro..."), second, nothing beats errors generated by templated code. The points about debugging and subtle bugs stand.
@SergeyA I find that simple template error messages are easy. Templates just permit much more complex structures. Doing anything that complex in a pile of recursive macros would make a many times harder to understand error messages, in my experience. It was really painful. (source of pain: bug in reflection system based off complex chained macros.) Maybe things have gotten better, dunno.
I'd say, for simple stuff, both macro and template errors are quite readable nowadays. For anything not so simple, they are both hard.
decltype(f(i)) tmp = f(i); -> auto tmp = f(i);
@PaulSanders Ah, I forgot they had that use of auto way back in c++11. I got so use to "crap, C++11, I gotta remove auto". Sigh.
1

Try:

#define MY_MACRO(X, Y, Z)                   \
    for(std::size_t i=0;i< X#_.size(); i++) \
    {                                       \
      std::string str;                      \
      Y X = data->X(i);                     \
      convert<Z>(X.data(), str);            \
      X#_[i] = Z(str);                      \
    }

But I suggest using template instead.

4 Comments

Thanks a lot. How could this be implemented using templates?
Reminder: macros have no type checking; a template is safer.
For example, only lc_ is modified and data seems to be the only input parameter, so a method accepting data as parameter is more than enough (if both lc_ and mmop_ inherit the same template-class, we could implement lc_.convert(data) maybe).
If you suggest using a template, give OP a template code.
1

You can use a backslash (\) to extend the macro definition by another line. For example if I had:

#define MACRO1(x) std::cout << "macro1: " << x << std::endl;

I could change it to:

#define MACRO1(x) std::cout << "macro1: " \
<< x << std::endl;

You also need to make sure you dont use a backslash on the last line of you definition and always leave a space before you put the backslash.

EDIT:

You can also use templates normally like:

#define my_template<int T> std::cout << T << std::endl;

But I recommend you do this instead:

#define my_template(T) std::cout << T << std::endl;

because:

  1. The errors you get will be text substitution based.
  2. Debugging will usually not be possible.
  3. There are lots of small bugs that can crop up in macro based generation that are very difficult to spot.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.