Since you write out foo(F0 a, F1 b, F2 c, F3 d), you probably aren't computing the number passed to TEMPLATE_COMMON_FLOAT, and you have a maximum number of arguments.
So you can easily implement this as a series of macros instead:
#define TEMPLATE_COMMON_FLOAT_1 \
template<std::floating_point F0> \
std::common_type_t<F0>
#define TEMPLATE_COMMON_FLOAT_2 \
template<std::floating_point F0, std::floating_point F1> \
std::common_type_t<F0, F1>
#define TEMPLATE_COMMON_FLOAT_3 \
template<std::floating_point F0, std::floating_point F1, std::floating_point F2> \
std::common_type_t<F0, F1, F2>
#define TEMPLATE_COMMON_FLOAT_4 \
template<std::floating_point F0, std::floating_point F1, std::floating_point F2, std::floating_point F3> \
std::common_type_t<F0, F1, F2, F3>
// Up to how many times you need for your code
// And if you *really* want TEMPLATE_COMMON_FLOAT(N)
#define TEMPLATE_COMMON_FLOAT(N) TEMPLATE_COMMON_FLOAT_ ## N
// Or if you have a large number of these, you can write them in terms of the previous version
// XTEMPLATE_COMMON_ARGS_ ## N(B, L) expands to B L0, B L1, B L2, ..., B L(N-1)
#define XTEMPLATE_COMMON_ARGS_1(BEFORE, LETTER) BEFORE LETTER ## 0
#define XTEMPLATE_COMMON_ARGS_2(BEFORE, LETTER) XTEMPLATE_COMMON_ARGS_1(BEFORE, LETTER), BEFORE LETTER ## 1
#define XTEMPLATE_COMMON_ARGS_3(BEFORE, LETTER) XTEMPLATE_COMMON_ARGS_2(BEFORE, LETTER), BEFORE LETTER ## 2
#define XTEMPLATE_COMMON_ARGS_4(BEFORE, LETTER) XTEMPLATE_COMMON_ARGS_3(BEFORE, LETTER), BEFORE LETTER ## 3
#define XTEMPLATE_COMMON_ARGS_5(BEFORE, LETTER) XTEMPLATE_COMMON_ARGS_4(BEFORE, LETTER), BEFORE LETTER ## 4
// ...
#define TEMPLATE_COMMON_FLOAT(N) \
template<XTEMPLATE_COMMON_ARGS_ ## N(std::floating_point, F)> \
std::common_type_t<XTEMPLATE_COMMON_ARGS_ ## N(, F)>
However, a simpler (and no preprocessor) solution is a variadic template:
template<std::floating_point... Fs> requires(sizeof...(Fs) == 4)
std::common_type_t<Fs...> foo(Fs... fs) {
auto [ a, b, c, d ] = std::tie(fs...); // Can access by name now
/* ... */
}
And it's not actually that difficult to write with an abbreviated function template for floating point types:
auto foo(std::floating_point auto a, std::floating_point auto b, std::floating_point auto c, std::floating_point auto d) -> decltype(a+b+c+d) {
// ...
}
// Or if you can't accept float being promoted to double
auto foo(std::floating_point auto a, std::floating_point auto b, std::floating_point auto c, std::floating_point auto d) -> decltype(0?a:0?b:0?c:d) {
// ...
}
requires sizeof...(Ts) == 4TEMPLATE_COMMON_FLOAT(foo,F0,a,F1,b,F2,c,F3,d) { ... }