On Tue, Mar 16, 2021 at 5:36 AM Andrii Nakryiko <andrii.nakryiko@xxxxxxxxx> wrote: > On Wed, Mar 10, 2021 at 2:02 PM Florent Revest <revest@xxxxxxxxxxxx> wrote: > > +#define ___bpf_build_param0(narg, x) > > +#define ___bpf_build_param1(narg, x) ___param[narg - 1] = x > > +#define ___bpf_build_param2(narg, x, args...) ___param[narg - 2] = x; \ > > + ___bpf_build_param1(narg, args) > > +#define ___bpf_build_param3(narg, x, args...) ___param[narg - 3] = x; \ > > + ___bpf_build_param2(narg, args) > > +#define ___bpf_build_param4(narg, x, args...) ___param[narg - 4] = x; \ > > + ___bpf_build_param3(narg, args) > > +#define ___bpf_build_param5(narg, x, args...) ___param[narg - 5] = x; \ > > + ___bpf_build_param4(narg, args) > > +#define ___bpf_build_param6(narg, x, args...) ___param[narg - 6] = x; \ > > + ___bpf_build_param5(narg, args) > > +#define ___bpf_build_param7(narg, x, args...) ___param[narg - 7] = x; \ > > + ___bpf_build_param6(narg, args) > > +#define ___bpf_build_param8(narg, x, args...) ___param[narg - 8] = x; \ > > + ___bpf_build_param7(narg, args) > > +#define ___bpf_build_param9(narg, x, args...) ___param[narg - 9] = x; \ > > + ___bpf_build_param8(narg, args) > > +#define ___bpf_build_param10(narg, x, args...) ___param[narg - 10] = x; \ > > + ___bpf_build_param9(narg, args) > > +#define ___bpf_build_param11(narg, x, args...) ___param[narg - 11] = x; \ > > + ___bpf_build_param10(narg, args) > > +#define ___bpf_build_param12(narg, x, args...) ___param[narg - 12] = x; \ > > + ___bpf_build_param11(narg, args) > > took me some time to get why the [narg - 12] :) it makes sense, but > then I started wondering why not > > #define ___bpf_build_param12(narg, x, args...) > ___bpf_build_param11(narg, args); ___param[11] = x > > ? seems more straightforward, no? Unless I'm misunderstanding something, I don't think this would work. The awkward "narg - 12" comes from the fact that these variadic macros work by taking the first argument out of the variadic arguments (x followed by args) and calling another macro with what's left (args). So if you do __bpf_build_param(arg1, arg2) you will have __bpf_build_param2() called with arg1 and __bpf_build_param1() called with arg2. And if you do __bpf_build_param(arg1, arg2, arg3) you will have __bpf_build_param3() called with arg1, __bpf_build_param2() called with arg2, and __bpf_build_param1() called with arg3. Basically, things are inverted, the position at which you need to insert in ___param evolves in the opposite direction of the X after ___bpf_build_param which is the number of arguments left. No matter in which order __bpf_build_paramX calls __bpf_build_param(X-1) (before or after setting ___param[n]) you will be unable to know just from the macro name at which cell in __param you need to write the argument. (except for __bpf_build_param12 which is an exception, because the max number of arg is 12, if this macro gets called, then we know that narg=12 and we will always write at __param[0]) That being said, I share your concern that this code is hard to read. So instead of giving narg to each macro, I tried to give a pos argument which indicates in which cell the macro should write. pos is basically a counter that goes from 0 to narg as macros go from narg to 0. #define ___bpf_fill0(array, pos, x) #define ___bpf_fill1(array, pos, x) array[pos] = x #define ___bpf_fill2(array, pos, x, args...) array[pos] = x; ___bpf_fill1(array, pos + 1, args) #define ___bpf_fill3(array, pos, x, args...) array[pos] = x; ___bpf_fill2(array, pos + 1, args) #define ___bpf_fill4(array, pos, x, args...) array[pos] = x; ___bpf_fill3(array, pos + 1, args) #define ___bpf_fill5(array, pos, x, args...) array[pos] = x; ___bpf_fill4(array, pos + 1, args) #define ___bpf_fill6(array, pos, x, args...) array[pos] = x; ___bpf_fill5(array, pos + 1, args) #define ___bpf_fill7(array, pos, x, args...) array[pos] = x; ___bpf_fill6(array, pos + 1, args) #define ___bpf_fill8(array, pos, x, args...) array[pos] = x; ___bpf_fill7(array, pos + 1, args) #define ___bpf_fill9(array, pos, x, args...) array[pos] = x; ___bpf_fill8(array, pos + 1, args) #define ___bpf_fill10(array, pos, x, args...) array[pos] = x; ___bpf_fill9(array, pos + 1, args) #define ___bpf_fill11(array, pos, x, args...) array[pos] = x; ___bpf_fill10(array, pos + 1, args) #define ___bpf_fill12(array, pos, x, args...) array[pos] = x; ___bpf_fill11(array, pos + 1, args) #define ___bpf_fill(array, args...) \ ___bpf_apply(___bpf_fill, ___bpf_narg(args))(array, 0, args) I hope this makes things a bit clearer ? (I often joke that BPF is written in preprocessor... :p)