4
$\begingroup$

In the C++ standard library, the format and printf functions specifically accept extraneous arguments:

It is not an error to provide more arguments than the format string requires: std::format("{} {}!", "Hello", "world", "something"); // OK, produces "Hello world!"

Why did the C++ language designers make this decision? It has the downside that the compiler cannot detect obvious mistakes at compile-time.

$\endgroup$
3
  • 5
    $\begingroup$ std::format and printf are very different functions; which do you mean to ask about? For printf it's just because of how C's varargs work. $\endgroup$
    – Bbrk24
    Commented Dec 9, 2023 at 13:32
  • 1
    $\begingroup$ GCC supports compile-time checking of printf-style format strings by declaring the function with the format attribute. $\endgroup$
    – dan04
    Commented Dec 11, 2023 at 16:39
  • $\begingroup$ If the designers of printf had wished their compiler to detect obvious mistakes at compile time, they would have designed a very different language than C! $\endgroup$ Commented Jan 10 at 4:57

4 Answers 4

10
$\begingroup$

printf

With respect to printf, the C++ committee didn't really have a lot of choice. printf was "inherited" from C, and its use accepted as-is, for the sake of backward compatibility.

std::format

I haven't found any notes about discussion of this specific rule with respect to std::format (but it's possible it was there and I missed it--the std::format spec went through 10 revisions before being incorporated into the standard, and there were a number of related papers as well (see links below).

That leaves us able to make only an educated guess1 about why they chose what they did. My guess would be that the reasoning was pretty simple: printf already displays this behavior. Maintaining similar behavior in this respect allows code that currently used printf to be converted to use std::format more easily, by editing only conversion specifiers in the format string, without worrying about architectural-level decisions that might result in an argument that might sometimes be unused.

Error Detection

This rule does not prevent detection of discrepancies between format strings and argument count. It merely means such discrepancies would be reported as warnings, rather than errors. Technically, they could actually be reported as errors if you really wanted to, but I'm reasonably certain most C++ programmers would find that somewhat surprising. The standard requires that any program that contains an diagnosable error produce at least one "diagnostic", but doesn't go into detail about what constitutes a "diagnostic". Many compilers need a fairly specific set of compiler flags to get their closest approximation of conforming behavior in any case.


1. But it is at least somewhat educated--I've attended meetings of the C++ standards committee, read many of the proposals since the mid-1990's or so, and contributed some changes to both the C and C++ standards.

References

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html

https://open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2572r1.html

https://open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2713r1.html

https://open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2675r1.pdf

$\endgroup$
5
$\begingroup$

The mechanism for processing variadic argument lists in C++ was inherited from C. This mechanism does not provide a way to determine the number of arguments that were supplied, it simply supports sequential access to each argument.

So variadic formatting functions work by using the format string to guide iteration over the variadic arguments. Since there's nothing wrong with not fetching all the provided arguments, nothing bad can happen if the format string has fewer placeholders than arguments -- the extra arguments are simply ignored.

There's nothing preventing a compiler from doing sanity checks that aren't required by the language, and many popular compilers issue warnings for this type of mistake. There are also typically compiler options (e.g. GCC's -Werror) that will convert warnings into fatal errors.

Note that this check can only be done at compile time when the format string is constructed at compile time. You can pass a dynamically-constructed format string to printf(). Since it's not possible to create the argument list dynamically, it may be useful to pass extra arguments that might not be used by some of the dynamically-generated format strings.

Actually, C++'s parameter pack mechanism improves on C's stdarg.h feature, and provides a std::sizeof... operator that allows the program to get the number of arguments passed. std::format() uses this, so it would be possible for it to check the number of arguments, and there's less reason for the spec to be so liberal. I can only speculate on why they didn't, perhaps to ease the transition from printf() to std::format().

$\endgroup$
2
  • $\begingroup$ @G.Sliepen Thanks, I just glanced at the linked page. Could it use sizeof... to be able to compare the number of arguments with the number of format string placeholders? $\endgroup$
    – Barmar
    Commented Dec 13, 2023 at 16:43
  • $\begingroup$ Thanks, I've updated the answer to mention this. $\endgroup$
    – Barmar
    Commented Dec 13, 2023 at 19:58
2
$\begingroup$

C++ allows extra arguments to printf because it follows at least a few of the fundamental principles of C, from which printf was inherited.

The fundamental principle of C which is of interest here is that the compiler should be kept as separate as possible from the standard library. In C it was possible to develop systems without using the standard library at all; you could re-invent whatever parts of the standard library you needed by issuing native calls to your target environment, and you could build any kind of system you could imagine. It would not be portable, but that's a different story.

In early versions of C++ the same held true, though it was a bit more difficult. I do not know whether this is possible in modern C++, but the general philosophy is still there.

Because of this fundamental principle, the compiler does not have, or is not expected to have, any knowledge whatsoever of the printf function, or of any other standard library function. The language provides a syntax for variadic functions, so that functions like printf can be written, and that is as far as the language goes.

This is a concept that many folks coming from other languages found hard to grasp: Strictly speaking, the question "how do you print something in C?" does not make sense, and it is only answerable in a roundabout way: technically, you do not print anything in C, because C is a language, and it is not concerned with such mundane concepts like printing. However, the creators of the language were so kind as to provide a standard library, and in order to print something using the C standard library you use the printf function.

So, since the compiler has no knowledge of the printf function, it cannot have a saying as to whether the number of arguments that you are passing to it is correct or not. It just does not know, and does not care.

Compilers that do, nonetheless, issue warnings on improper use of printf are performing a bit of a blasphemy: they are assuming knowledge of the fact that you are most likely using the standard libraries, which is above and beyond their duties, and they do it for the sake of the programmer's convenience.

$\endgroup$
2
  • $\begingroup$ When the compiler finds for example #include <stdio.h> then it knows that printf is the standard library printf (or it could be a macro or maybe static function but then you shouldn’t get the warning). $\endgroup$
    – gnasher729
    Commented Dec 18, 2023 at 21:12
  • $\begingroup$ @gnasher729 I am not sure I understand what point you are trying to make. $\endgroup$
    – Mike Nakis
    Commented Dec 19, 2023 at 1:26
0
$\begingroup$

The best reason to not raise errors when extra arguments are provided, I think, is to allow the format string to be easily changed ─ or perhaps, provided dynamically ─ without having to change the argument list.

Note that the same is true in other languages such as Python, so this isn't a quirk of C++:

>>> '{} {}'.format('Hello', 'world', 'something')
'Hello world'

The upside is that you can specify the format string elsewhere, or e.g. load it at runtime (useful for internationalisation), and it can be edited without having to edit the code which provides the arguments. For example:

WELCOME_STRING = '{0} version {1}\n\n{3}'

# ...

print(WELCOME_STRING.format(PROGRAM_NAME, PROGRAM_VERSION, COMPILED_DATE, LICENSE_TEXT))

This works even better with keyword-arguments in the format string, although C++ doesn't support those (presumably because libc doesn't):

WELCOME_STRING = '{PROGRAM_NAME} version {PROGRAM_VERSION}\n\n{LICENSE_TEXT}'
$\endgroup$
8
  • 2
    $\begingroup$ You can't have a dynamic string in C++ though, it has to be known at compile time $\endgroup$ Commented Dec 9, 2023 at 12:59
  • 2
    $\begingroup$ Thank you for your answer, but tbh, it makes very weak argument. $\endgroup$
    – Deborah C
    Commented Dec 9, 2023 at 13:04
  • 1
    $\begingroup$ @mousetail What do you mean that you can't have a dynamic string in C++? You can get one in all the same ways you can in C, like loading from a file or using a function like scanf. std::string can be created from a const char*. $\endgroup$
    – Bbrk24
    Commented Dec 9, 2023 at 13:28
  • 1
    $\begingroup$ What I mean is, in printf/format specifically, the string has to be static. $\endgroup$ Commented Dec 9, 2023 at 14:17
  • 4
    $\begingroup$ The question asks “Why did the C++ language designers make this decision?” This answer does not answer that question; it’s just a guess (and one I don’t think is particularly likely to be the explanation, for what it’s worth). $\endgroup$
    – Alexis King
    Commented Dec 9, 2023 at 17:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .