tags:

views:

130

answers:

5

I want to write preprocessor functions/arrays that are evaluated at compile time. For example, if I define

#define MYARR[] {5,4,3,2,1,0}

then, the code

int x = R[0];

should be presented as

int x = 5;

to the compiler. (of course only literals can be used in the index). This is important if code size/memory is critical and we dont want to store MYARR, but we need it for coding convenience.

Compile time functions would also be good. For example, something like

#define MYMAP(n)
#if n==1
5
#else
2

So, the statement

int x = MYMAP(4);

should be presented to the compiler as

int x = 2;

Obviously, we have to use a literal as the argument. Is this possible?

Thanks!

A: 

myarr for chars I can do

#define MYARR(x) ("1234567"[x])

still pondering ints. Mymap is

#define MYMAP(n) (n == 1 ? 5 : 2)
pm100
@pm100: your `MYARR` would better do `(((char const*)"1234567")[x])`
Jens Gustedt
@Jens: the `const` makes no difference. Modifying string literals is undefined behavior so the compiler is free to put them in shared constant storage anyway, and in practice, it will do so.
R..
@R.: It makes a difference for the caller. You can't accidentally take the result as an lvalue and modify it. `MYMAP(2) = 27;` would be permitted (though UB) in the original version, but produce a compile time error with the `const`. I think for macros like that, you have to be the most careful, since a potential user doesn't forcibly master all the consequences.
Jens Gustedt
+2  A: 

The standard C preprocessor will not do what you want. To get this sort of behavior reliably you are going to need a more powerful, nonstandard preprocessing tool. However, I'm not familiar enough with what's available to direct you to which one you might want.

Although, in the second case, you may still be able to get the effect you want on most modern compilers. For example:

#define MYMAP(n) (((n) == 1) ? 5 : 2)

int x = MYMAP(4);

will still be presented to the compiler as:

int x = ((4 == 1) ? 5 : 2);

but the compiler itself, if it is a reasonable modern compiler and you allow it to optimize, will likely notice that this expression can be evaluated at compile-time and will emit code that is identical to

int x = 2;

However, there's nothing that guarantees that a compiler will perform this optimization.

Tyler McHenry
You can do preprocessor meta-programming as Boost.PP proves. Its only cumbersome and of course limited regarding the data types you can work on.
Georg Fritzsche
A: 

There is no standard preprocessor commands that handle arrays the way you are wanting. I suggest that you use a const.

static const int R[] = {5,4,3,2,1,0};
...
int a = R[0];

Then for the MYMAP question...

#define MYMAP(n) ((n)==1?5:2)
...
int a = MYMAP(1); // equals 5.
jojaba
+3  A: 

Sure its possible. While you could do that manually, Boost.Preprocessor already gives you the tools you need:

#include <boost/preprocessor.hpp>
#define SEQ (5)(4)(3)(2)(1)(0)
int x = BOOST_PP_SEQ_ELEM(0, SEQ);

... gets transformed to:

int x = 5;

It also includes arithmetic, comparisons and control structures like IF, FOR, FOR_EACH, enumerations, ... You just have to keep in mind that the data types you can work with are rather limited.

Utilizing Boost.PP again, your second example can be done like so:

#define MYMAP(x) BOOST_PP_IF(BOOST_PP_EQUAL(x, 1), 5, 2)

You can of course implement manually what Boost.PP does, but considering the time and effort needed for this i personally wouldn't bother.

As a C user, you won't be interested in the rest of Boost, so you might want to use bcp to extract the preprocessor components.

Georg Fritzsche
@Georg: In *modern* C aka C99, you may program such things much simpler (and easier to read) than Boost does it. Boost is restricted by the lack of variadic macros in C++. But with compound literals you even get it straight forward, no need for acrobatics ;-)
Jens Gustedt
@Jens: Good point - as a mainly C++ user i haven't looked into the use patterns with variadic macros yet.
Georg Fritzsche
-1 for Boost answer to C question.
R..
@R..: Euh, why? Boost.PP works the same there - would you rather reimplement all control structures yourself?
Georg Fritzsche
Boost.PP is not C. I would equally give a -1 for a recommendation to write a Perl script to generate C code, as long as the question was about C and the C preprocessor.
R..
@R..: Boost.PP works with the common preprocessors i know of and doesn't depend on any C++ behaviour. The above works with C and its preprocessor, so i don't see how a comparison with a Perl script fits.
Georg Fritzsche
@Georg, wow. And I though template metaprogramming was scary and that the C preprocessor was not capable of scaring me that badly. Was I ever wrong. Now I'm compelled to learn more about Boost.PP...
RBerteig
@RBerteig, @Georg: for those that like C99 :) I have programmed a library (well include files) P99 that is entirely based on C99 features. It is not yet published but you may already have a glance in the documentation that I put online: http://p99.gforge.inria.fr/p99-html/
Jens Gustedt
@Jens: Nice, looks promising. Are you planning to add `ENUM`, `ENUM_PARAMS`, et al? :)
Georg Fritzsche
@Georg: Any sort of "code unrolling" that you want. See e.g http://p99.gforge.inria.fr/p99-html/group__statement__lists.html (If you'd like to discuss this further let's take this discussion to other channels.)
Jens Gustedt
This turned out to be the best solution, and yes, it is compatible with pure C. Thanks! I have a follow up question about trying to select whether to do a preprocessor computation vs. run time computation. I guess I will submit a new question.
highBandWidth
+2  A: 

In C99 (you really need that, C89 wouldn't do) you can do with something like

#define MyMap(N) (((int const[]){ 3, 4, 5, 6, 7})[N])

provided your type is int, but any other type would do. The weird thing (int const[]){ 3, 4, 5, 6, 7} is called a compound literal. The const in there for the base type tells the compiler that it will not be modified and that he may alias all occurrences with the same contents to the same fixed location.

Edit, after caf's remark: Generally, for this approach most compilers will be able to completely optimize any reference to the array away, provided N is an expression that can be evaluated at compile time, such as a fixed value 7 or 'a' or so.

If this is not the case, the compiler has to create the array object somewhere. With the const he could be allowed to generate just one copy of it, regardless how often you call the macro in your code. When he manages to do so, the initialization of the array would be done at compile time, so there would be no compile time overhead.

I checked this for the three compilers that I have on my machine:

  • gcc and clang allocate the array on the stack and this for every single call to MyMap, bad, since the overhead is proportional to the size of the array.
  • opencc allocates the array statically but creates a new copy for every call to MyMap, better but not yet ideal.
Jens Gustedt
If you want to allow the array itself to be optimised out, you'd have to be careful not to pass a variable to the macro.
caf
@caf: yes, the OP spoke of *literals*. My assumption was that he meant compile time constant expressions. I add that to my answer.
Jens Gustedt