-2

Variadic macros in CPP (the C/C++ preprocessor; for simplicity's sake I'll treat it as a single, separate language in this question) are extremely limited compared to, e.g., C++'s variadic templates. Essentially, variadic macros are just macros with a parameter whose argument is allowed to contain commas. This provides no straightforward way to count arguments, operate on arguments one by one, etc. Those things are possible, but require elaborate, confusing, and slow-to-compile hacks, such as those described in this question. The only thing that's anywhere near straightforward to do with VA_ARGS is pass them to a variadic function.

My question is, why were they designed this way? The standard approach to lists in any pure-functional language like CPP is cons-style pattern matching: handle the first argument of a list and recurse for the rest, and have a base case for the empty list. The standards committee members would have been quite familiar with this approach.

Why was an approach like this not taken with CPP's variadic macros? Were variadic macros seen as simply a way to wrap variadic functions, such that there was no need to operate on the argument list? Was there some underlying issue that would have made it impractical to allow variadic macros to recurse? Or...?

NOTE: I'm not looking for answers/comments of the form "because people shouldn't want variadic macros". The existence of things like boost.preprocessor indicates that reasonable people wanted to use the preprocessor in non-trivial ways. Also not looking for personal opinions on why some other design would be a good/bad idea. I'm trying to find out the actual reasoning from the time.

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • 3
    C. C++ was designed to be pretty for easy for C programmers to switch to (so lots of backwards compatibility). – NathanOliver Mar 05 '19 at 13:50
  • 4
    Is there any reason why you have to process it inside a macro instead of delegating the hard work to an actual function? – user202729 Mar 05 '19 at 13:52
  • 1
    C. C has `...` arguments (which are similarly awful) for functions. Passing arguments to something `printf`-like was identified as desirable very early in C's lifetime – Caleth Mar 05 '19 at 13:56
  • macros are unpleasant, variadic macros are variadic unpleasant :P – 463035818_is_not_an_ai Mar 05 '19 at 13:58
  • 4
    Well the needed standard elaborations for variadic macros are probably less than 1/10th of those for variadic templates. The preprocessor was not designed to be a language in itself, so the fact that it's very limited should not come as a surprise? – Max Langhof Mar 05 '19 at 13:59
  • Does C++ even support `__VA_ARGS__`? – Lundin Mar 05 '19 at 14:13
  • 3
    Anyway, the answer is probably that you aren't supposed to be writing icky macros in the first place. If they were made more powerful, the result would be even more icky macros. – Lundin Mar 05 '19 at 14:15
  • @Lundin [Yes](https://timsong-cpp.github.io/cppwp/cpp.replace#5) – NathanOliver Mar 05 '19 at 14:24
  • 2
    I don't think your question is answerable with easy to cite resources. The WG14 original paper on the subject (*"N580, Feather, Varargs for Function-like Macros"*) is not readily available (despite being listed in the wg14 [old document registry](http://www.open-std.org/jtc1/sc22/wg14/www/olddocuments.txt)). Unless someone from a very select group of people shows up, it's anybody's guess (and therefore, too opinionated for SO). – StoryTeller - Unslander Monica Mar 05 '19 at 14:25
  • @NathanOliver: I think that was more of a corollary, given that early C++ compilers compiled to C code. – Bathsheba Mar 05 '19 at 14:32
  • 1
    @StoryTeller [Um, what, how is this not readily available?](https://google.com/search?q=N580+Varargs) – Yakk - Adam Nevraumont Mar 05 '19 at 14:33
  • @Yakk - Well, ain't that a lucky break. Can you pull that off with any old random proposal on that page? – StoryTeller - Unslander Monica Mar 05 '19 at 14:34
  • *I'll treat [the preprocessor] as a single, separate language* If the designers of C++ didn't make a similar assumption, the basis for your question is invalid. – Caleb Mar 05 '19 at 14:34
  • @StoryTeller Variadic macros weren't introduced until C99, for which we have a published rationale. – Lundin Mar 05 '19 at 14:36
  • @SergeBallesta Variadic macros were introduced 1999, not in the 1970's. – Lundin Mar 05 '19 at 14:37
  • @Lundin - For which Yakk found the original proposal, containing the unabridged design considerations. – StoryTeller - Unslander Monica Mar 05 '19 at 14:37
  • @Caleb I'm not sure what your point is. Both languages treat preprocessing as a subset of the compilation passes, with different syntax and semantics from the rest of the respective languages. The C and C++ preprocessors are virtually identical. – Sneftel Mar 05 '19 at 14:37
  • 1
    Macros do not recurse, variadic or not. Macros do not form a Turing complete programming language. Are you asking why? – n. m. could be an AI Mar 05 '19 at 14:41
  • @Sneftel If you're talking about formal languages, fine. However, you seem to be comparing the C/C++ preprocessor to functional programming language e.g. Haskell, ML, Lisp, etc., when it's pretty clear it was never designed to be anything like that. Perhaps the "single, separate" part in my comment confused you — I wasn't objecting to that, but rather the comparison to languages (in the sense of programming language). – Caleb Mar 05 '19 at 14:48
  • @n.m. The designers of the language didn't seem to care one way or the other about recursion or turing completeness in the preprocessor. Recursive macros without a stopping case would be useless, of course. My question was, given that they were adding a facility to the language, why did they add it in such an unusual way? – Sneftel Mar 05 '19 at 14:55
  • @Caleb Yes, I'm using "language" in the formal sense. Nobody's going to write an email client in the preprocessor. :-) – Sneftel Mar 05 '19 at 14:56
  • They apparently didn't seek to turn the macro facility into a programming language, and carefully and deliberately restricted it such that it won't become one. That's a legitimate design decision. – n. m. could be an AI Mar 05 '19 at 15:51

3 Answers3

5

Variardic macros where inherited into C++ from C.

Clive Feather wrote the paper adding variardic macros to C.

The paper, N580: Varargs for Function-like Macros states:

This proposal allows the author of a macro to state that it takes a variable number of arguments, and to substitute the trailing arguments en bloc.

[...]

It would be possible to provide further facilities, but this has been left for further proposals.

One issue is whether it should be possible to allow zero actual arguments to match the trailing parameter. It has been decided not to allow this, as this fits better with another proposal being made separately.

so the plan was to add in the feature most asked for (a macro that can be used to populate a printf call), and leave further expansion for later papers.

In C++, macros are second class citizens. C++ rarely innovates with new macro syntax or features, and instead works out ways to solve the problem without a macro.

C hasn't further innovated.

The actual vararg macros added to C come from papers derived from that one, but that one had the clearest motivational text behind this decision.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thanks, from that it does sound like the goal of the proposal was a use case-specific one. Is there any indication of what other proposal "fit better" with not allowing zero-argument VA_ARGS? – Sneftel Mar 05 '19 at 14:34
  • @Sneftel I did not see it anywhere obvious on Feather's list of papers. – Yakk - Adam Nevraumont Mar 05 '19 at 16:21
  • Any idea to what "other proposal" is being alluded to in the last highlighted paragraph? – supercat Mar 07 '19 at 17:59
  • @supercat Yes, in fact, in the two days since I answered that qurstion I have come up with a wonderful and clear answer to it, different than my answer above to the exact same question. Sadly the margins, I mean comment field, is not large enough to contain its magnificant magesterious magesty. So now you are bereft, beriddled and beside yourself with sweet sorrow, like Sung Jin-hwan. Alas, poor Sung Jin-hwan! I knew him. A fellow who has a wikipedia page, of most excellent music. He hath never crossed my mind before, but is prominant in this comment. Pass the viel, for the end is nigh – Yakk - Adam Nevraumont Mar 07 '19 at 18:12
3

In certainly seems that the intention was simply to be a wrapper for variadic functions. Variadic macros were introduced in C99 and then far later in C++11. The C99 rationale V5.10 6.10.3 offers this explanation:

A new feature of C99: C89 introduced a standard mechanism for defining functions with variable numbers of arguments, but did not allow any way of writing macros with the same property. For example, there is no way to write a macro that looks like a call to printf.

This facility is now available. The macro definition uses an ellipsis in the same way to indicate a variable argument list. However, since macro substitution is textual rather than run-time, a different mechanism is used to indicate where to substitute the arguments: the identifier __VA_ARGS__. This is replaced by all the arguments that match the ellipsis, including the commas between them.

There are a few other uses of __VA_ARGS__ though, but that might be more of a coincidence. For example you can use variadic macros to count the number of items present in an initializer list.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • I don't see how that answers the question. How does the quoted text *explain* or even *hint* at the design considerations? – StoryTeller - Unslander Monica Mar 05 '19 at 14:38
  • 1
    @StoryTeller Like it says: here's a new feature which you can use to wrap in your variadic functions. So this was one of the main purposes, or possibly the only purpose. – Lundin Mar 05 '19 at 14:42
  • Dear lord, for a rationale document it sure does convey rationale poorly. One vague sentence? *"For example, there is no way to write a macro that looks like a call to printf"* - And even then your interpretation is not immediately obvious. – StoryTeller - Unslander Monica Mar 05 '19 at 14:44
  • @StoryTeller Yeah well, I didn't write it, don't shoot the messenger :) – Lundin Mar 05 '19 at 14:46
  • Wasn't my intent. I'm just flabbergasted. – StoryTeller - Unslander Monica Mar 05 '19 at 14:46
  • It seems weird that, if delegating to `printf` was their one-and-only use case, they didn't even get that one quite right (thanks to the zero-argument comma thing). – Sneftel Mar 05 '19 at 15:01
  • 1
    @Sneftel Rationale actually mentions that case: Intent is to include the format string in `__VA_ARGS__`, so there is always at least one parameter. `#define dprintf(...) dfprintf(stderr, __VA_ARGS__)` – user694733 Mar 05 '19 at 15:26
  • @user694733 Hah, good point. The limitations of variadic macros dovetail with the design of vararg functions in the standard library (opaque block of arguments described separately, rather than there being a terminator). – Sneftel Mar 05 '19 at 15:53
  • @user694733: What's ironic is that most of the need to use hacky macros in printf wrappers targeting pre-Standard implementations was eliminated in C89 with the formalization of `vfprintf` and `vsprintf`. The situations were variadic macros would have been most useful are those which would involve being able to say, e.g. `countedFoo(10,20,30,40)` and have a compiler produce a static const object whose address could be passed by code expecting a pointer to a struct with layout `{int count; short dat[];}`, with `count` being automatically computed as 4. That shouldn't be hard, but... – supercat Mar 08 '19 at 17:18
  • ...even with variadic macros it's very difficult, and requires that source code include explicit provision for any particular number of arguments that might be passed. – supercat Mar 08 '19 at 17:18
1

A couple of good answers already, but I wanted to follow up with a synthesis of them, as well as mention an important point by user694733. Given the proposal paper and the description in the language standard, it does seem that the one-and-only envisioned use case for that proposal was delegating to a printf-style variadic function.

The proposal mentioned that allowing zero actual arguments "fits better with another proposal". I'd initially thought that that meant "Not allowing zero arguments allows this proposal to be compatible with another one", but in fact the meaning seems to be closer to "Allowing zero arguments should be part of a different proposal". While I can't find a proposal that would refer to, I did find a public comment by him, suggesting the __VA_COUNT__ keyword. Assuming that that comment represents at least part of the thrust of his other proposal, __VA_COUNT__ is the sort of thing one would want for a more elaborate use of variadic macros.

It seems likely that N580 (and its successors) represented the popular and uncontroversial proposal to allow varargs to be passed through a macro, while __VA_COUNT__ was a component of more richer preprocessing of variadics (either through MACRO_ARGS_1,MACRO_ARGS_2,etc., or through some other, unknown mechanism), and only the first proposal achieved consensus.

Sneftel
  • 40,271
  • 12
  • 71
  • 104