8

I'm currently writing code for a microcontroller; since the ATmega128 does not have a hardware multiplier or divider, these operations must be done in software and they take up a decent amount of cycles.

However, for code portability and ease of use, I'd prefer not to hard-code precomputed values into my code. So for instance, I have a number of tasks which are dependent on the system clock frequency. Currently I’m running at 16 MHz, but should I choose to lower that, say to reduce power consumption for battery applications?

I'd like to change one line of code rather than many.

So with that said, can the C preprocessor compute arithmetic expressions and then "paste" the result into my code rather than "pasting" the original expression into the code? If so, how would I go about doing this? Are their compiler options and whatnot that I need to consider?

NOTE: The values I want to compute are constant values, so I see no reason why this would not be a feature.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
audiFanatic
  • 2,296
  • 8
  • 40
  • 56
  • 3
    If the expressions are constant, aren't they optimized away by the compiler anyway? – 1'' Apr 26 '14 at 04:16
  • 1
    duplicate of http://stackoverflow.com/a/1560385/995714 – phuclv Apr 26 '14 at 04:27
  • well, there are preprocessor libraries, maybe there is one for you, it mostly depends on what version of the C language are you targeting, for example for C99 and above there is P99 http://p99.gforge.inria.fr/ – user2485710 Apr 26 '14 at 04:31
  • currently I'm using C89, but I can switch to C99. Not sure how I'd use these though as I'm using IAR Embedded Workbench – audiFanatic Apr 26 '14 at 04:34
  • never heard of this compiler/IDE before, I noticed that it's not the usual `gcc` port and it's a totally different beast, I don't think I have ever seen a library supporting this compiler in general. Good luck with that. Maybe since this compiler is not that popular you should try to get help from some IRC, maling list or forum that is IAR specific . – user2485710 Apr 26 '14 at 04:53
  • If a limited extent, use boost/preprocessor/arithmetic – BLUEPIXY Apr 26 '14 at 08:24
  • 1
    possible duplicate of [Can the C preprocessor perform integer arithmetic?](http://stackoverflow.com/questions/1560357/can-the-c-preprocessor-perform-integer-arithmetic) – M.M Apr 26 '14 at 10:18

4 Answers4

8

This is one question:

  • Q1. Can the C preprocessor perform arithmetic?

And this is another:

  • Q2. Can the C preprocessor compute arithmetic expressions and then "paste" the result into my code rather than "pasting" the original expression into the code?

The answer to Q1 is yes. The answer to Q2 is No. Both facts can be illustrated with the following file:

foo.c

#define EXPR ((1 + 2) * 3)
#if EXPR == 9
int nine = EXPR;
#else
int not_nine = EXPR;
#endif

If we pass this to the C preprocessor, either by cpp foo.c or equivalently gcc -E foo.c, we see output like:

# 1 "foo.c"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 30 "/usr/include/stdc-predef.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/predefs.h" 1 3 4
# 31 "/usr/include/stdc-predef.h" 2 3 4
# 1 "<command-line>" 2
# 1 "foo.c"


int nine = ((1 + 2) * 3);

The fact that the preprocessor retains the line defining int nine and has dropped the line defining not_nine shows us that it has correctly performed the arithmetic required to evaluate #if EXPR == 9.

The fact that the preprocessed text of the definition is int nine = ((1 + 2) * 3); shows us that the #define directive causes the preprocessor to replace EXPR with its definition ((1 + 2) * 3), and not with the arithmetic value of its definition, 9.

Does the C preprocessor have any directive besides #define which has the second effect? No.

But this does not of course imply that the definition of int nine must entail a run-time calculation, because the compiler will almost certainly evaluate the arithmetic expression ((1 + 2) * 3) at compile time and replace it with the constant 9.

We can see how the compiler has translated the source file by examining the compiled object file. Most toolchains will provide something like GNU binutils' objdump to assist with this. If I compile foo.c with GCC:

gcc -c -o foo.o foo.c

and then invoke:

objdump -s foo.o

to see the full contents of foo.o, I get:

foo.o:     file format elf64-x86-64

Contents of section .data:
 0000 09000000                             ....
Contents of section .comment:
 0000 00474343 3a202855 62756e74 752f4c69  .GCC: (Ubuntu/Li
 0010 6e61726f 20342e38 2e312d31 30756275  naro 4.8.1-10ubu
 0020 6e747539 2920342e 382e3100           ntu9) 4.8.1.

And there is the hoped-for 9 hard-coded in the .data section.

Note that the preprocessor's arithmetic capabilities are restricted to integer arithmetic

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
5

Yes, you can do arithmetic using the preprocessor, but it takes a lot of work. Reading this page here, shows how to create an increment counter, and a while loop. So with that, you could create addition:

#define ADD_PRED(x, y) y
#define ADD_OP(x, y) INC(x), DEC(y)
#define ADD(x, y) WHILE(ADD_PRED, ADD_OP, x, y)

EVAL(ADD(1, 2)) // Expands to 3

So reusing the ADD macro, you can then create a MUL macro. Something like this:

#define MUL_PRED(r, x, y) y
#define MUL_OP(r, x, y) ADD(r, x), x, DEC(y)
#define MUL_FINAL(r, x, y) r
#define MUL(x, y) MUL_FINAL(WHILE(MUL_PRED, MUL_OP, 0, x, y))

EVAL(MUL(2, 3)) // Expands to 6

Division and subtraction can be built in a similar way.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59
  • Boost defines macros for preprocessor add/sub/mul/div. But with some limits on value-range (saturating to `BOOST_PP_LIMIT_MAG`, which might be defined as 240). https://www.boost.org/doc/libs/1_72_0/libs/preprocessor/doc/ref/add.html and [c preprocessor: arithmetic in concatenation](https://stackoverflow.com/a/59931527) shows an example. – Peter Cordes Aug 15 '23 at 16:07
3

It can, but is unnecessary: you don't actually need to involve the preprocessor unless you actually want to generate new identifiers that involve numbers in some way (e.g. stuff like func1, func2).

Expressions like 1 + 2 * 3, where all elements are compile-time constant integer values, will be replaced with the single result at compile-time (this is more or less demanded by the C standard, so it's not "really" an optimisation). So just #define constants where you need to name a value that can be changed from one place, make sure the expression doesn't involve any runtime variables, and unless your compiler is intentionally getting in your way you should have no runtime operations to worry about.

Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
  • Ok, so either way my expression will be replaced by a single constant at compile-time (provided I have no variables in my `#define`)? – audiFanatic Apr 26 '14 at 04:24
  • I would double-check the assembly just in case. Not all embedded C compilers adhere to the C spec; some deviate quite significantly. – nneonneo Apr 26 '14 at 04:40
  • 2
    @Leushenko One can even use enums instead of defines in the second case, that way programs are easier to debug and one doesn't need the preprocessor at all. – Étienne Apr 26 '14 at 08:36
  • @Étienne: And with `enum` that constant expression value is forced to be computed at compile-time by standard, IIRC – Jack Sep 17 '15 at 20:32
0

I compiled a file containing the following lines using gcc -E.

#define MUL(A, B) ((A)*(B))

#define CONST_A 10
#define CONST_B 20

int foo()
{
   return MUL(CONST_A, CONST_B);
}

The output was:

# 1 "test-96.c"
# 1 "<command-line>"
# 1 "test-96.c"


int foo()
{
   return ((10)*(20));
}

That's just one data point for you.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 2
    As it should be - that usage of the preprocessor is just text replacement. The compiler itself will usually replace the expression with a constant at compile time though. – Kevin Lam Apr 26 '14 at 04:55