Not sure you'll call this cleaner or simpler...
but thought I would throw that out there for the sake of fun
C++ offers great evaluation (even at compile time) capabilities with constexpr and you can put that to work to your benefit by using templated recursive calls..
here is an example:
template <typename T> constexpr T maximum(T a) {return a ;}
template <typename T> constexpr T maximum(T a, T b) {return a > b ? a : b;}
template <typename T, typename... Targs> constexpr T maximum(T first, Targs... rest) {return maximum(first, maximum(rest...));}
#define numA 300
#define numB 600
#define numC 100
#define numD 200
#define numE 400
#define numF 500
#define numG 42
constexpr int numMax = maximum(numA, numB, numC, numD, numE, numF, numG);
void setup() {
Serial.begin(115200); Serial.println();
Serial.print(F("Max = ")); Serial.println(numMax);
}
void loop() {}
the great thing about this is — as demonstrated above — that you are no longer obliged to modify the macro if you have more or less than 4 values as the maximum() function takes a variable number of arguments ➜ you now can pass any number (1 or more) of constexpr arguments.
The other nice thing about it is the code size impact➜ NO code has been generated. It's evaluated at compile time for you by the compiler because everything is constexpr.
(of course I would discourage you to use the #define and go for constexpr as well for your numA, numB, ...) as strong typing always helps the compiler understand your intent.