17

Is there any view on whether using the #define to define full lines of code for simplifying coding is good or bad programming practice? For example, if I needed to print a bunch of words together, I'd get annoyed typing

<< " " <<

To insert a space between words in a cout statement. I could just do

#define pSpace << " " <<

and type

cout << word1 pSpace word2 << endl;

To me this neither adds or subtracts from the clarity of the code and makes typing slightly easier. There are other cases that I can think of where typing will be much easier, usually for debugging.

Any thoughts on this?

EDIT: Thanks for all the great answers! This question just came to me after doing a lot of repetitive typing, but I never thought there would be other, less confusing macros to use. For those who don't want to read all the answers, the best alternative is to use the macros of your IDE to reduce repetitive typing.

7
  • 74
    Its clear to you because you invented it. To everybody else it just obfuscated. At first it looks like a syntax error. When it compiles I would think what the heck and then find that you have a macro that is not in all caps. In my opinion this just makes the code horrible to maintain I would definitely reject this if it came for code review and I don't expect you will find many that would accept it. And you are saving 3 characters!!!!!!!!! Oct 26, 2011 at 6:03
  • 11
    Where you can't reasonably factor out the repetitiveness using functions or whatever, the better approach is to learn what your editor or IDE can do to help you. Text-editor macros or "snippet" hotkeys can save you from typing so much without damaging readability.
    – user8709
    Oct 26, 2011 at 10:38
  • 2
    I've done this before (with larger chunks of boilerplate), but my practice was to write the code, then run the preprocessor, and replace the original file with the preprocessor output. Saved me the typing, spared me (and others) the maintenance hassle.
    – TMN
    Oct 26, 2011 at 11:08
  • 9
    You saved 3 characters and traded them for confusing statements. Very good example of bad macro, imho:o)
    – MaR
    Oct 26, 2011 at 11:13
  • 7
    Many editors have an advanced feature for exactly this scenario, it is called "Copy and Paste" Oct 26, 2011 at 16:04

13 Answers 13

111

Writing code is easy. Reading code is hard.

You write code once. It lives for years, people read it a hundred times.

Optimize code for reading, not for writing.

6
  • 11
    I agree a 100%. (In fact, I was going to write this answer myself.) Code is written once, but might be read dozens, hundreds, or even thousands of times, maybe by dozens, by hundreds, or even by thousands of developers. The time it takes to write code is totally irrelevant, the only thing that counts is the time to read and understand it.
    – sbi
    Oct 26, 2011 at 7:37
  • 2
    Preprocessor can and should be used for optimising code for reading and maintaining.
    – SK-logic
    Oct 26, 2011 at 8:36
  • 2
    And even if it is just you reading the code in one or two years: You will forget these things yourself while doing other things in between.
    – johannes
    Oct 26, 2011 at 11:26
  • 2
    "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live." - (Martin Golding)
    – Dylan Yaga
    Oct 26, 2011 at 13:57
  • @Dylan - otherwise, after a few months maintaining that code, he will find you - (Me)
    – user8709
    Oct 27, 2011 at 9:50
28

Personally, I loathe it. There's a number of reasons why I discourage people from this technique:

  1. At compile-time your actual code changes may be significant. The next guy comes along and even includes a closing bracket in his #define or a function call. What's written at a certain point of code is far from what's going to be there after pre-processing.

  2. It is unreadable. It may be clear to you.. for now.. if it's just this one definition. If it becomes a habit, you will soon end up with dozens of #defines and will start to lose track yourself. But worst of all, no one else will be able to understand what word1 pSpace word2 exactly means (without looking up the #define).

  3. It may become a problem for external tools. Say you somehow end up with a #define that includes a closing bracket, but no opening bracket. Everything may work well, but editors and other tools may see something like function(withSomeCoolDefine; as rather peculiar (i.e., they will report errors and whatnot). (Similar example: a function call inside a define - will your analysis tools be able to find this call?)

  4. Maintenance becomes much harder. You have all those define in addition to the usual problems maintenance brings with it. In addition with the above point, tool support for refactoring may be affected negatively as well.

1
  • 4
    I finally banned myself from almost all macros because of the hassles of trying to handle them right in Doxygen. It's been quite a few years now, and overall I think readability improved quite a bit - irrespective of whether I'm using Doxygen or not.
    – user8709
    Oct 26, 2011 at 10:40
16

My main thought on this, is that I never use "make typing easier" as a rule when writing code.

My main rule when writing code is to make it easily readable. The rationale behind this is simply that code is read an order of magnitude more times that it is written. As such, the time you lose writing it carefully, orderly, correctly laid out is in fact invested in making further reading, and understanding much faster.

As such, the #define you use simply breaks the usual way of alternated << and other-stuff. It breaks the rule of least surprise, and is not IMHO a good thing.

1
  • 1
    +1: "Code is read an order of magnitude more times that it is written" !!!!
    – Giorgio
    Oct 26, 2011 at 12:04
14

This question gives a clear example of how you can use macros badly. To see other examples (and be entertained) see this question.

Having said that, I'll give real world examples of what I consider to be good incorporation of macros.

The first example appears in CppUnit, which is a unit testing framework. Like any other standard testing framework, you create a test class and then you have to somehow specify which methods should be run as part of the test.

#include <cppunit/extensions/HelperMacros.h>

class ComplexNumberTest : public CppUnit::TestFixture  
{
    CPPUNIT_TEST_SUITE( ComplexNumberTest );
    CPPUNIT_TEST( testEquality );
    CPPUNIT_TEST( testAddition );
    CPPUNIT_TEST_SUITE_END();

 private:
     Complex *m_10_1, *m_1_1, *m_11_2;
 public:
     void setUp();
     void tearDown();
     void testEquality();
     void testAddition();
}

As you can see, the class has a block of macros as it's first element. If I added a new method testSubtraction, it's obvious what you need to do to have it included in the test run.

These macro blocks expand out to something like this:

public: 
  static CppUnit::Test *suite()
  {
    CppUnit::TestSuite *suiteOfTests = new CppUnit::TestSuite( "ComplexNumberTest" );
    suiteOfTests->addTest( new CppUnit::TestCaller<ComplexNumberTest>( 
                                   "testEquality", 
                                   &ComplexNumberTest::testEquality ) );
    suiteOfTests->addTest( new CppUnit::TestCaller<ComplexNumberTest>(
                                   "testAddition",
                                   &ComplexNumberTest::testAddition ) );
    return suiteOfTests;
  }

Which would YOU prefer to read and maintain?

Another example is in the Microsoft MFC framework, where you map functions to messages:

BEGIN_MESSAGE_MAP( CMyWnd, CMyParentWndClass )
    ON_MESSAGE( WM_MYMESSAGE, OnMyMessage )
    ON_COMMAND_RANGE(ID_FILE_MENUITEM1, ID_FILE_MENUITEM3, OnFileMenuItems)
    // ... Possibly more entries to handle additional messages
END_MESSAGE_MAP( )

So, what are the things which distinguish "Good Macros" from the horrible evil kind?

  • They perform a task which cannot be simplified any other way. Writing a macro to determine a maximum between two elements is wrong, because you can achieve the same using a template method. But there are some complex tasks (for example, mapping message codes to member functions) which the C++ language just doesn't handle elegantly.

  • They have an extremely strict, formal usage. In both of these examples the macro blocks are announced by starting and ending macros, and the inbetween macros will only ever appear inside these blocks. You have normal C++, you briefly excuse yourself with a block of macros, and then you go back to normal again. In the "evil macros" examples, the macros are scattered throughout the code and the hapless reader has no way of knowing when the C++ rules apply and when they do not.

5

It will be, by all means, better if you tune you favorite IDE/text editor for inserting snippets of code you find tiresome to retype again and again. And better is "polite" term for comparison. Actually, I can not think about any similar case when preprocessing beats editor's macros. Well, may be one - when by some mysterious and unhappy reasons you are constantly using different set of tools for coding. But it is not a justification :)

It also can be a better solution for more complex scenarios, when text preprocessing can do thing much more unreadable and complicated (think about parametrized input).

3
  • 3
    +1. Indeed: let the editor do the work for you. You'll get the best of both worlds if for example you make an abbreviation of the << " " <<.
    – mike3996
    Oct 26, 2011 at 9:57
  • -1 for "It also can be a better solution for more complex scenarios, when text preprocessing can do thing much more unreadable and complicated (think about parametrized input)" - If it's that complex, make a method for it, even then, make a method for it. e.g. This evil I recently found in code..... #define printError(x) {puts(x);return x}
    – mattnz
    Oct 26, 2011 at 23:23
  • @mattnz, I've meant loop constructs, if/else constructs, templates for comparator creation and so on - that kind off stuff. In IDEs such kind of parametrized input helps you not only to type quickly few lines of code, but also to iterate quickly through params. No one tries to compete with methods. method is method )))
    – shabunc
    Oct 27, 2011 at 4:46
4

The others have already explained why you should not do it. Your example obviously does not deserve being implemented with a macro. But, there is a wide range of cases where you have to use macros for a sake of readability.

A notorious example of a wise application of such a technique is Clang project: see how .def files are used there. With macros and #include you can provide a single, sometimes entirely declarative definition for a collection of similar things which will be unrolled into types declarations, case statements wherever appropriate, default initialisers, etc. It increases maintainability significantly: you won't ever forget to add new case statements everywhere when you've added a new enum, for example.

So, as with any other powerful tool, you have to use C preprocessor carefully. There are no generic rules in the art of programming, like "you should never use this" or "you always have to use that". All the rules are nothing but guidelines.

3

It's never appropriate to use #defines like that. In your case, you could do this:

class MyCout 
{
public:
  MyCout (ostream &out) : m_out (out), m_space_pending (false)
  {
  }

  template <class T>
  MyCout &operator << (T &value)
  { 
    if (m_space_pending)
      m_out << " ";

    m_out << value;
    m_space_pending = false;
    return *this;
  }

  MyCout &operator << (const char *value)
  {
    if (m_space_pending)
      m_out << " ";

    m_out << value;
    m_space_pending = true;
    return *this;
  }

  MyCout &operator << (char *value) { return operator << (static_cast <const char *> (value)); }
  MyCout &operator << (ostream& (*fn)(ostream&)) { m_out << fn; return *this; }

private:
  ostream
    &m_out;

  bool
    m_space_pending;
};

int main (int argc, char *argv [])
{
  MyCout
    space_separated (cout);

  space_separated << "Hello" << "World" << endl;
}
2

No.

For macros intended to be used in code, a good guideline for testing appropriateness is to surround its expansion with parentheses (for expressions) or braces (for code) and see if it will still compile:

// These don't compile:

#define pSpace (<< " " <<)
cout << word1 pSpace word2 << endl;

#define space(x) (" " << (x))
cout << word1 << space(word2) << endl;

// These do:

#define FOO_FACTOR (38)
x = y * FOO_FACTOR;

#define foo() (cout << "Foo" << endl)
foo();

#define die(c) { if ((c)) { exit(1); } }
die(foo > 8);

#define space(x) (" " + string((x)))
cout << "foo" << space("bar") << endl;

Macros used in declarations (like the example in Andrew Shepherd's answer) can get away with a looser set of rules as long as they don't upset the surrounding context (e.g. switching between public and private).

1

Its a reasonably valid thing to do in a pure "C" program.

It is unnecessary and confusing in a C++ program.

There are many ways to avoid repetitive typing of code in C++. From using the facilities provided by your IDE (even with vi a simple "%s/ pspace /<< " " <</g" would save as much typing and still produce standard readable code ). You could define a private methods to implement this or for more complex cases C++ template would be a cleaner and simpler.

2
  • 2
    No, it is not a reasonably valid thing to do in pure C. Single values or complete independent expressions that rely only on the macro parameters, yes, and for the latter a function may even be a better choice. Such a half-baked construct as in the example, no way.
    – Secure
    Oct 26, 2011 at 7:22
  • @secure -- I agree that its not a good idea in the case of the example given. But given the lack templates etc. there are valid uses for "#DEFINE" macros in C. Oct 26, 2011 at 8:16
1

In C++ this can be solved with operator overloading. Or even something as simple as a variadic function:

lineWithSpaces(word1, word2, word3, ..., wordn) is both simple and saves you typing pSpaces again and again.

So while in your case it might not seem like a big deal, there's a solution that is simpler and more robust.

In general, there's few cases where using a macro is significantly shorter without introducing obfuscation, and mostly there's a sufficiently short solution using actual language features (macros being more of a mere string substitution).

0

Is there any view on whether using the #define to define full lines of code for simplifying coding is good or bad programming practice?

Yes, it is very bad. I have even saw people doing this :

#define R return

to save typing (what you are trying to achieve).

Such code belongs only in places like this.

-1

Macros are evil and should be used only when you really have to. There are a few cases where macros are applicable (mainly debugging). But in C++ in most cases you can use inline functions instead.

3
  • 2
    There is nothing intrinsically evil in any programming technique. All the possible tools and methods can be used, as long as you know what you're doing. It applies to the infamous goto, to all the possible macro systems, etc.
    – SK-logic
    Oct 26, 2011 at 14:08
  • 1
    That's exactly the definition of evil in this case: "something you should avoid most of the time, but not something you should avoid all the time". It's explained on the link that evil is pointing.
    – sakisk
    Oct 26, 2011 at 15:00
  • 2
    I believe that it is counterproductive to brand anything as "evil", "potentially harmful" or even "suspicious". I do not like the notion of "bad practice" and "code smell". Each and every developer have to decide in every specific case, which practice is harmful. Labeling is a harmful practice - people tend not to think any further if something had already been labeled by the others.
    – SK-logic
    Oct 26, 2011 at 15:57
-2

No, you're not allowed to use macros to save typing.

However you are allowed, even required to use them to separate non-changing part of code from changing ones, and reduce redundancy. For the latter you must think alternatives and pick macro only if better tools don't work. (For practice macro is quite at the end of the line, so having it implies last resort...)

To reduce typing most editors have macros, even intelligent code snippets.

1
  • "No, you're not allowed to use macros to save typing." - Good job I'm not going to listen to your order here! If I have a pile of objects that I need to declare/define/map/switch/etc, you can bet I'm going to use macros to avoid going insane - and increase readability. Using macros to save typing on keywords, control flow, and suchlike is stupid - but to say no one can use them to save keystrokes in valid contexts is equally asinine. Jan 12, 2016 at 0:50

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.