问题
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.
回答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" \
)
"...";
回答2:
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:
- converting from enum to int
回答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
#
回答4:
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.
回答5:
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
}
来源:https://stackoverflow.com/questions/11685706/when-could-macro-make-code-more-beautiful-than-function-does