Why do Boost Format and printf behave differently on same format string
Asked Answered
I

2

7

The Boost Format documentation says:

One of its goal is to provide a replacement for printf, that means format can parse a format-string designed for printf, apply it to the given arguments, and produce the same result as printf would have.

When I compare the output of boost:format and printf using the same format string I get different outputs. Online example is here

#include <iostream>
#include <boost/format.hpp>

int main()
{
    boost::format f("BoostFormat:%d:%X:%c:%d");

    unsigned char cr =65; //'A'
    int cr2i = int(cr);

    f % cr % cr % cr % cr2i;

    std::cout << f << std::endl;
    printf("Printf:%d:%X:%c:%d",cr,cr,cr,cr2i);
}

The output is:

BoostFormat: A:A:A:65

printf: 65:41:A:65

The difference is when I want to display a char as integral type.

Why there is a difference? Is this a bug or wanted behavior?

Ingvar answered 20/11, 2015 at 9:20 Comment(0)
N
6

This is expected behaviour.

In the boost manual it is written about the classical type-specification you uses:

But the classical type-specification flag of printf has a weaker meaning in format. It merely sets the appropriate flags on the internal stream, and/or formatting parameters, but does not require the corresponding argument to be of a specific type.

Please note also, that in the stdlib-printf call all char arguments are automatically converted to int due to the vararg-call. So the generated code is identical to:

printf("Printf:%d:%X:%c:%d",cr2i,cr2i,cr2i,cr2i);

This automatic conversion is not done with the % operator.

Neilneila answered 20/11, 2015 at 9:44 Comment(5)
+1 But, wow, that's horrible. I wonder what's their reasoning for this is? It seems they took C style format (which isn't great to begin with) and yet still did not make it backwards compatible. Like picking worse of both worlds.Aggressive
Thanks your answer. Good to know that is expected behavior, however it was not what I expected. Here, the actual type overrules the specifier. So for char it makes no difference between '%d' und '%c', StrangeIngvar
@user694733: Boost.Format cannot change the (compile-time) overloading of operator% based on the (run-time) contents of the format string. Now you can hide the problem, but that does add significant complexity - you'd have to do implement the possible overload scenarios at compile time, and choose the correct one at runtime. This isn't space-efficient either.Struve
@joefel In your code you haven't actually written code that matches the behaviour of what you want to output. If you wanted to match to the printf behaviour what you should have written is f % +cr % +cr % cr % +cr;Kelikeligot
This is using the unary + operator to perform Integral promotion - see msdn.microsoft.com/en-us/library/ewkkxkwb(d=hv.2,v=vs.140).aspx for detailsKelikeligot
C
0

Addition to the accepted answer:

This also happens to arguments of type wchar_t as well as unsigned short and other equivalent types, which may be unexpected, for example, when using members of structs in the Windows API (e.g., SYSTEMTIME), which are short integers of type WORD for historical reasons.

If you are using Boost Format as a replacement for printf and "printf-like" functions in legacy code, you may consider creating a wrapper, which overrides the % operator in such a way that it converts

  • char and short to int
  • unsigned char and unsigned short to unsigned int

to emulate the behavior of C variable argument lists. It will still not be 100% compatible, but most of the remaining incompatibilities are actually helpful for fixing potentially unsafe code.

Newer code should probably not use Boost Format, but the standard std::format, which is not compatible to printf.

Circumpolar answered 14/4, 2021 at 10:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.