Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

As someone who understands C macros relatively well and who makes frequent use of them, I don't think I understand what __EXP_COUNTER__ does, and how it is different from __COUNTER__. I would have to experiment each time before using it, and would then quickly forget again the intricate details about expansion order, etc., similar to how every time I do some kind of STRINGIFY macro I have to make sure to use the right number of forwarding macro calls.

Is there a concrete use case for this that really can't be solved by __LINE__? I've used __LINE__ in the past to generate unique identifiers used in macro-generated code chunks. I don't see that non-uniqueness thing you mentioned causing any problems except for global variables (so not really an issue in my book).

As much as I love the C preprocessor as a crude tool that can solve many practical issues that are solved in other languages with a magnitude more complexity (besides solving problems that other languages don't have), I think the value doesn't come from its unintelligible execution model. And if __EXP_COUNTER__ is so difficult to understand, I personally don't like it.



The parent comment explained pretty well the advantages over __LINE__ imo.

Crafting macros is often black magic, but using them shouldn't be (if they're well crafted). Having an implicit rule in your macro that it cannot be used twice in the same line is surprising and potentially dangerous.

Another example is where you have a macro doing lots of work, if it needs to use a submacro multiple times that itself needs a unique identifier, then __LINE__ is no longer sufficient.

__UEXP_COUNTER__ is a little more difficult to imagine a use-case for, I'll admit (I can see it allows passing counters around, but I can't see why a parameter couldn't do the same).

Again, preprocessor macros are black magic, these additions seem a lot simpler to understand than `__VA_OPT__` (and its predecessor `##__VA_ARGS__`) or MSVCs awful stringify problems, as you brought up.


__UEXP_COUNTER__ is required so that you can abstract creating unique symbols:

  #define MAC(...) WHATEVER UNIQ(X) WHATEVER
without __UEXP_COUNTER__, you would have to pass __EXP_COUNTER__ as an extra argument to UNIQ(X).

You cannot make a nickname for __EXP_COUNTER__; e.g.

  #define EC __EXP_COUNTER__
because the expansion of EC itself has its own counter!

Using __UEXP_COUNTER__, UNIQ(X) can just do everything necessary: obtain MAC's counter, and combine it with the prefix X.


__EXP_COUNTER__ has a stable value in a given token replacement sequence (right hand side of a macro).

__COUNTER__ __COUNTER__ __COUNTER__ might give you 42 43 44, whereas __EXP_COUNTER__ __EXP_COUNTER__ __EXP_COUNTER__ will produce 73 73 73.

We can imagine that every macro has a hidden parameter:

  #define MAC(A, B, C, __EXP_COUNTER__)
we don't pass this parameter when calling the macro; the macro expander does that, and it passes an integer token whose value is incremented for each such call.


Thanks, it's a little clearer in my mind now. But wouldn't this work for you?

    #include <stdio.h>

    #define FOO_(c) printf("%d %d %d\n", c, c, c);
    #define FOO() FOO_(__COUNTER__)
    #define FOO2() FOO_(__COUNTER__)

    int main(void)
    {
            printf("%d %d %d\n", __COUNTER__, __COUNTER__, __COUNTER__);
            FOO();
            FOO();
            FOO2();
            FOO2();
            return 0;
    }
Output:

    0 1 2
    3 3 3
    4 4 4
    5 5 5
    6 6 6


Right, but now you can't use that COUNTER value in a macro and have it stay the same value. Think about concatenating a variable name with the counter and trying to use that same new name later in the same macro. Like NAME ## __COUNTER = NAME ## __COUNTER -1; This won't work without some extra state.


This code won't work in any case. You can't do arithmetic like that. And I think it's much better code anyway to create the expansion once, because otherwise you have to construct NAME ## __COUNTER at each use and it quickly becomes unmaintainable and hard to change how you construct that name.

IMO the best solution if you want to avoid an extra indirection to inject some state, would be preprocessor variables that you can assign to in a macro expansion. Procedural preprocessor code basically. But the preprocessor doesn't work like that.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: