1

Exactly, macro is not necessary for programming languages. For example, Java works quite fine without macro. Usually macro makes code more clear and shorter, at the same time, more dangerous.

So, what's the best way to use macro? Let's talk in code.

qiuxiafei
  • 5,827
  • 5
  • 30
  • 43

5 Answers5

5

With macro, you can write a beautiful solution for problem such as this:

  • Define an enum such that its value can be converted into its string representation and vice-versa.

Suppose, you want to define an enum called period whose members are one, five, ten, fifteen and thirty. Then here is how you do it:

  • First create a header file called period_items.h as:

    //period_items.h
    
    //Here goes the items of the enum
    //it is not a definition in itself!
    E(one)
    E(five)
    E(ten)
    E(fifteen)
    E(thirty)
    
  • then create another header file called period.h as:

    //period.h
    #include <string>
    
    //HERE goes the enum definition!
    enum period 
    {
       #define E(item)  item,
         #include "period_items.h" //it dumps the enum items in here!
       #undef E
       period_end
    };
    
    period to_period(std::string const & name)
    {
       #define E(item)  if(name == #item) return item;
         #include "period_items.h"
       #undef E
       return period_end;
    }
    

Now you can simply include period.h and use to_period function. :-)

You could also add this function to period.h as:

std::string to_string(period value)
{
    #define E(item)  if(value == item) return #item;
        #include "period_items.h"
    #undef E
    return "<error>";
}

Now, you could write this:

#include "period.h"

period v = to_period("fifteen"); //string to period
std::string s = to_string(v);  //period to string

Why this solution is beautiful?

Because now if you want to add few more members to the enum, all you have to do is to add them to period_items.h as:

    //period_items.h

    //Here goes the items of the enum
    //it is not a definition in itself!
    E(one)
    E(five)
    E(ten)
    E(fifteen)
    E(thirty)
    E(fifty)       //added item!
    E(hundred)     //added item!
    E(thousand)    //added item!

And you're done. to_string and to_period will work just fine, without any modification!

--

I took this solution from my solution to another problem, posted here:

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 2
    talking about code obfuscation... I don't find it beautiful – BЈовић Jul 27 '12 at 10:59
  • 1
    This is a kludge. Kludges are usually clever but ugly, and this cannot be counted as an exception. – akappa Jul 27 '12 at 11:32
  • I would not include a different header, but rather use a macro that takes a different macro as argument: `#define APPLY( MACRO ) MACRO(one) MACRO(two)` and then call it `enum X { #define ENUM_ITEM(x) x,` APPLY( ENUM_ITEM ) #undef ENUM_ITEM };`. This is more useful when there are concrete discrete values for the enum, or other mapped information, as you can have the `APPLY` macro pass multiple arguments. Say that you have a message protocol that sends `field_id`, `field_content`, and a given list of fields. You can have `APPLY` define the mapping: `#define APPLY( M ) M(my_field,value,type)`... – David Rodríguez - dribeas Jul 27 '12 at 12:19
3

I think the best way is to use inline

You get all the benefits of macro + all compile time checks

The primary thing macro is useful in c++ is for controlling the compilation. Something like:

#ifdef DEBUG:
    //print some debug information
#endif

or

#ifdef OS_WINDOWS
    //windows specific code
#
Andrew
  • 24,218
  • 13
  • 61
  • 90
  • macro is very useful in C also. same code can be controlled to support different versions of kernels(say example). – Jeyaram Jul 27 '12 at 10:19
1

In my very personal opinion, a good macro is a very rare thing. I try to aviod them as much as possible, because most of them are more like a time bomb.

To be good, a macro must:

  • Be simple.
  • Never ever be ambiguous
  • Never brake the code structure (I've seen macros like #define MACRO } someCode {, which sucks)
  • Be unable to throw an exception, or something like that, basically, it must never make debugging harder
  • Have a name that explains the meaning clearly
  • There must be a really good reason to use a macro instead of, say, an inline function, like compilation control, or header guarding.
SingerOfTheFall
  • 29,228
  • 8
  • 68
  • 105
1

I use macros only in places where nothing else works.

One example is having an easy mapping from error values to strings, for example instead of

switch(code) {
    case ERR_OK: return "ERR_OK";
    case ERR_FOO: return "ERR_FOO";
    :

I use a simple macro like

#define CASE_STR(x) case x: return #x

so I can simplify this to

switch(code) {
    CASE_STR(ERR_OK);
    CASE_STR(ERR_FOO);
    :

However, those cases are usually more for debugging.

Also, I've written a GLSL (OpenGL Shading Language) loop unrolling once using the boost preprocessor suite, which then could be used something like

const char *shader = "#version 120\n"
"..."
GLSL_UNROLL_FOR("int i",0,10,\
    "foo += i\n" \
)
"...";
zerm
  • 2,812
  • 25
  • 17
1

Here is an example (maintainable easily).

enum id {
#define ITEM(id, s) id,
# include "foo.itm"
#undef ITEM
    nbItem
};

static char const *const s[] = {
#define ITEM(id, s) s,
# include "foo.itm
#undef ITEM
}
md5
  • 23,373
  • 3
  • 44
  • 93