1

I need to compute max of N integer constants (MACROS) at compile time.

This is a possible way out -

#define C_MAX_2(a, b)                         (((a) > (b)) ? (a) : (b))
#define C_MAX_3(a, b, c)                      C_MAX_2((C_MAX_2((a),(b))), (c))
#define C_MAX_4(a, b, c, d)                   C_MAX_2((C_MAX_2((a),(b))), \
                                                      (C_MAX_2((c),(d))))
#define C_MAX_5(a, b, c, d, e)                C_MAX_2((C_MAX_3((a),(b), (c))), \
                                                      (C_MAX_2((d),(e))))

Any better generic way in C ?

Kamath
  • 4,461
  • 5
  • 33
  • 60
  • Does this even work compile time? – Bart Friederichs Dec 02 '13 at 12:56
  • 2
    Having a script to calculate constants and write source file would seem more sensible and flexible approach. – user694733 Dec 02 '13 at 13:04
  • 1
    possible duplicate: http://stackoverflow.com/questions/824639/variadic-recursive-preprocessor-macros-is-it-possible – bph Dec 02 '13 at 13:58
  • @ Bart Friederichs, yes it works at compile time is all args to macros are constants. – Kamath Dec 03 '13 at 07:13
  • @user694733, can you please provide me an example to such a script. It should parse header fies for provided constants and its values. Compute max, and the define a new constant with max value. – Kamath Dec 03 '13 at 07:15
  • @ZEN.Kamath I usually have my data in in some generic format (CSV/TXT/JSON/XML/...), something that Python can easily read. Then I calculate data and finally open and write source files. Scripts are usually less than 100 lines, but you can do what ever you want them to. – user694733 Dec 03 '13 at 08:26

3 Answers3

1

The compiler should have these values calculated during compilation. Assuming of course that a, b ,c, d and e are all constants. This is basic constant propagation.

So, if this works as expected than that's the optimal way - during runtime there are not max calculations.

Edit This is true for debug (non optimized) builds as well.

egur
  • 7,830
  • 2
  • 27
  • 47
1

First of all, @egur is right,

during runtime there are not max calculations.

So you should'n worry about performance

A more consistent (generic) way could be

#define MAX_2(n1, n2) (n1 > n2 ? n1 : n2)
#define MAX_3(n1, n2, n3)         MAX_2(n1, MAX_2(n2, n3))
#define MAX_4(n1, n2, n3, n4)     MAX_2(n1, MAX_3(n2, n3, n4))
#define MAX_5(n1, n2, n3, n4, n5) MAX_2(n1, MAX_4(n2, n3, n4, n5))
bolov
  • 72,283
  • 15
  • 145
  • 224
  • No I am not worried about performance, I am avaoiding dynamic memory allocation. I need to define an array with fixed size in compile time. – Kamath Dec 03 '13 at 07:11
  • 3
    At least the MAX_2 macro might need to have an internal parenthesis at `(n1) > (n2)`. Otherwise you can't call it like a function in some cases. For example `MAX_2(byte1 & mask, byte2 & mask)` would with the present code expand to `(byte1 & (mask > byte2) & mask)`. – Lundin Dec 03 '13 at 07:32
0

First we define a macro for counting how many arguments were passed.

#define COUNTARGS(...) GET21ST(__VA_ARGS__,COUNTDOWN())
#define COUNTDOWN() 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define GET21ST(a20,a19,a18,a17,a16,a15,a14,a13,a12,a11,a10,a9,a8,a7,a6,a5,a4,a3,a2,a1,a,...) a

However the expansion of GET21ST will happen before COUNTDOWN, so we need to force the precompiler to evaluate these in a different order. Fortunately this answer provides us with a mechinism to do just that.

#define EVAL(...)  EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__

#define VANISH()
#define OBSTRUCT(...) __VA_ARGS__ VANISH()
#define COUNTDOWN() 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define COUNTARGS(...) EVAL(OBSTRUCT(GET21ST)(__VA_ARGS__,COUNTDOWN()))
#define GET21ST(a20,a19,a18,a17,a16,a15,a14,a13,a12,a11,a10,a9,a8,a7,a6,a5,a4,a3,a2,a1,a,...) a

Now we are able to automatically select the correct macro from a list based on the number of arguments.

Todo this we will need a collection of macros to select from.

#define MAX_1(n) n
#define MAX_2(a,b) ((a)>(b)?(a):(b))
#define MAX_3(a,...) MAX_2(a,MAX_2(__VA_ARGS__))
#define MAX_4(a,...) MAX_2(a,MAX_3(__VA_ARGS__))
...
#define MAX_20(a,...) MAX_2(a,MAX_19(__VA_ARGS__))

We will also need some way of generating new macro calls.

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

From here we can define a generic MAX macro that will call the appropriate specific macro.

#define MAX(...) CAT(MAX_,COUNTARGS(__VA_ARGS__))(__VA_ARGS__)

And testing on https://godbolt.org/ with the -E flag.

This:

MAX(2,3,4,1)

Results in:

(2>(3>(4>1?4:1)?3:(4>1?4:1))?2:(3>(4>1?4:1)?3:(4>1?4:1)))

NOTE

For those using C++, this is a simpler and safer macro.

#include <vector>
#include <algorithm>

#define MAX(...) [](auto v)constexpr{      \
        return *std::max_element(v.begin(), \
                                v.end());    \
    }(std::vector<float>{__VA_ARGS__})

Or even better, just use algorithm yourself.

unDeadHerbs
  • 1,306
  • 1
  • 11
  • 19