JOIN
Get Time
forums   
Search | Watch Thread  |  My Post History  |  My Watches  |  User Settings
View: Flat (newest first)  | Threaded  | Tree
Previous Thread  |  Next Thread
Using and Understanding C++ Macros | Reply
Problem:

You want to understand programs written by C++ users, but they are using so many macros, and other abbreviations, that their solutions no longer look like normal C++ code, and seem impossible to understand.

Or, you want to be like them, and use macros yourself.

Note: this recipe is about abbreviations which use features available only in C++. See the "Writing Template" recipe for more abbreviations, which are useful for all languages.

Solution

Most of people abbreviate the same things. Here are the most common abbreviations.

Abbreviations for data types which occur most frequently in TopCoder problems:
typedef long long LL;
typedef long double LD;
typedef vector<int> VI;
typedef vector<string> VS;
typedef pair<int,int> PII;

There are two common versions of FOR macros:
#define FOR(k,a,b) for(typeof(a) k=(a); k <= (b); ++k)
#define REP(k,a) for(int k=0; k < (a); ++k)


or:
#define FOR(k,a,b) for(typeof(a) k=(a); k < (b); k++)


Size macro:
#define SZ(x) ((int)((x).size()))


Other popular abbreviations:
#define ALL(c) (c).begin(), (c).end()  
#define PB push_back 
#define MP make_pair 


Discussion:

Of course, this is just one possible set of macro names (although probably the most common one), and you will see solutions where macros are named differently. For example, some people use lowercase names; using uppercase names for your macros agrees with the normal programming style, and improves readability by making obvious that something is an abbreviation, not a variable name. It is not obvious whether common programming style suggests using uppercase names for type name abbreviations (typedefs), but still, it improves readability. There are also people who use even shorter abbreviations (as REP(i,a) is too long for them, they use just fi(a)—the Cookbook does not recommend it, since it only helps with making your solution harder to read and challenge, and making those who want to learn from your submissions angry, there is almost no point in saving these 3 characters).

As shown above, that there are two popular versions of FOR macros: one uses the closed range [a,b] while the other uses the range [a,b). Remember about that when challenging. Both have their advantages and disadvantages: [a,b) agrees with the convention used throughout STL and is usually faster to use, while using [a,b] makes it harder to have an off-by-one bug. Having both FOR and REP, as shown in the first version, gives advantages of both styles.

Why so many coders use abbreviations for FOR loops anyway? The reason is that FOR loops usually appear several times in a TopCoder match, and if you save, for example, 3 seconds by using one, and there are 20 for loops in total in an SRM, you save a minute in total, which could be significant. Another reason to use them is that the normal for statement is bug prone: you could write e.g. for(int i=0; i<n; n++) instead of for(int i=0; i<n; i++), and using a macro saves you from that. Coders usually also have some macros for traversing the loop range in reverse (named for example FORD).

Using typeof in your macros will allow the same macro to work on each type of data possible. For example, a FOR macro using typedefs will be able to iterate both over numbers and over vectors.

Remember to use parentheses when referencing macro parameters, because otherwise precedence of operators might break your program, like in the following example:
#define FOR(k,a,b) for(int k=a; k<b; k++)
FOR(k, 0, x ? 3 : 5) ...


In this example, the condition used to check when the loop finishes is expanded to "k < x ? 3 : 5", which is probably always true (as is evaluates to either 3 or 5, which are non-zero numbers). It's better to have ugly macros with redundant parentheses than to allow them to break unexpectedly.

The SZ(x) macro is only a bit shorter than x.size(), but it also has another advantage: it stops the compiler from issuing a warning about type mismatch when doing something like FOR(k, 0, s.size()-3). (Note that the compiler is actually right when it issues a warning in this case: since s.size() is unsigned, s.size()-3 will be evaluated to a large number when s.size() is 2, which will make the FOR loop go forever and break your solution.)

The size macro could be done not with a macro, but with a template function—use what you like more.

It is possible to use more advanced macros, which allow the coder to do even more interesting things. For example, at the time of writing this Cookbook, GNU C++ has extensions which allow executing for loops and other statements from inside expressions, like:
  printf("%d", (({int x=1; FOR(i,1,10) x*=i; x;})));

prints the factorial of 9. Using these, you could write a set of macros which allow using quantifiers, minimum, and sum operations in your expressions. Thus, finding the smallest i such that, for all j, i % mods[j] == vals[j] could be done just by writing
return FIRST(i, 0, INF, FORALL(j, 0, Size(vals), i % mods[j] == vals[j]))

However, be careful with it, as, since these features are rarely used in normal programming, compiler bugs sometimes cause them to break. They work at the time of writing this recipe, but in future they might break, or even be completely removed from GNU C++.

Use your creativity to invent new abbreviations, or look for them in your competitors' solutions. Of course, all these abbreviations should be a part of your template (see "Writing Template").

Comment

Some parts of the original recipe has been removed, in order to become a part of the "Writing Template" recipe.
Re: Using and Understanding C++ Macros (response to post by Eryx) | Reply
Great - nothing to add :-)
RSS