JS_Free*: public vararg macros#1535
Conversation
useful for dense code with visible towers of frees
why didn't I do that the first time
this also tends to pile up
|
Thoughts @bnoordhuis ? No strong opinion,I don't see myself using these. |
Just now I was editing |
|
You have a very fair point there! Have you measured any performance difference? |
|
I have not measured the performance but with the vararg stack pushing requirement it will indeed always be slower than a normal function, however the body of those functions is the simplest it can be. A vararg macro that applies another subtitution to each of its args is technically possible in C but that'd require me to pollute the qjs header with something like 16 or such linear macros or a few exponential macros... 🤷 |
|
You mean like this, right? #define JS_COUNT_ARGS_(_0, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
#define JS_COUNT_ARGS(...) JS_COUNT_ARGS_(0, __VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define JS_FreeValueV1(ctx, a) do { JS_FreeValue(ctx, a); } while (0)
#define JS_FreeValueV2(ctx, a, b) do { JS_FreeValue(ctx, a); JS_FreeValue(ctx, b); } while (0)
#define JS_FreeValueV3(ctx, a, b, c) do { JS_FreeValue(ctx, a); JS_FreeValueV2(ctx, b, c); } while (0)
#define JS_FreeValueV4(ctx, a, b, c, d) do { JS_FreeValue(ctx, a); JS_FreeValueV3(ctx, b, c, d); } while (0)
#define JS_FreeValueV5(ctx, a, b, c, d, e) do { JS_FreeValue(ctx, a); JS_FreeValueV4(ctx, b, c, d, e); } while (0)
#define JS_FreeValueV6(ctx, a, b, c, d, e, f) do { JS_FreeValue(ctx, a); JS_FreeValueV5(ctx, b, c, d, e, f); } while (0)
#define JS_FreeValueV7(ctx, a, b, c, d, e, f, g) do { JS_FreeValue(ctx, a); JS_FreeValueV6(ctx, b, c, d, e, f, g); } while (0)
#define JS_FreeValueV8(ctx, a, b, c, d, e, f, g, h) do { JS_FreeValue(ctx, a); } JS_FreeValueV7(ctx, b, c, d, e, f, g, h); } while (0)
#define JS_FreeValueV__(ctx, N, ...) JS_FreeValueV##N(ctx, __VA_ARGS__)
#define JS_FreeValueV_(ctx, N, ...) JS_FreeValueV__(ctx, N, __VA_ARGS__)
#define JS_FreeValueV(ctx, ...) JS_FreeValueV_(ctx, JS_COUNT_ARGS(__VA_ARGS__), __VA_ARGS__)Seems fine to me. The generated code is an ungainly mess of nested do/while statements but no one looks at that, and it's definitely more efficient than a variadic function. What would be interesting is measuring if it's better to expand to direct JS_FreeValue calls, or have JS_FreeValueV1, JS_FreeValueV2, etc. as public API functions that call JS_FreeValue in turn - i.e., more direct calls, or fewer indirect calls. I could see it going either way but I expect that, as JSValue is two words on 64 bits machines, the compiler spills to the stack more and more after JS_FreeValueV3 or JS_FreeValueV4, negating any performance benefits. Maybe you can mix and match: split up in pairs, call JS_FreeValueV2 for each pair, then mop up the remaining JSValue if there is one with JS_FreeValue. Lots of opportunity to measure, tweak and micro-optimize. :-) |
May I suggest an alternative that is more generic and produces more readable C code: I don't particularly like this kind of preprocessor abuse, but we cannot do much better with the C preprocessor. I must admit I did author some other ones in QuickJS. An extra advantage in using this macro is the order of evaluation of the arguments is fixed, left to right, as opposed to unspecified behavior for the variadic function. |
|
If we accept such macros it is true that they come with more benefits and I'd gladly replace the vararg funcs with them.
I particularly like this solution because it seems like a set of macros that can then be applied to a lot of other potential bulk operations. |
shamelessly adapted from chqrlie's response

This introduces 4 new public macros and 4 public functions (though the macros are preferred, which is why the functions use the lowercase
js_*prefix instead).The functions to use directly are
JS_FreeValuesJS_FreeValuesRTJS_FreeAtomsJS_FreeAtomsRTnote the "s"
Example usage
JS_FreeValues(ctx, myval1, myval2, someotherval3);Why?
I am editing dense code with tight interop with QJS and i've noticed an annoying pattern of FreeValue/Atom accumulating, especially when dup'ing for calls when the JSValue layout isn't initially linear, or when going down object chains with atoms. I also feel like this could benefit more users than me.