Using printf in C++

I

Ian Collins

Le 22/05/12 23:31, Ian Collins a écrit :
Le 22/05/12 21:33, Adam Skutt a écrit :
Le 22/05/12 05:27, Adam Skutt a écrit :



It means I can pass any object to the formatting function, and I don't
have to remember the stupid formatting code (if I don't need special
behavior), nor do I have to remember to treat certain types
specially.

Sure, then the boss says:

"I want rounding to 2 decimals, names flush left in the first
column that must be 16 chars long"

printf("%-16s|%10.2g|\n",name,value);


And how would it look in C++ please?

Just compare then.


sprintf to a buffer, output the buffer.


Yes!.

KISSes!

Simplicity is golden.


How's your list example coming along?
 
J

jacob navia

Le 22/05/12 23:42, Ian Collins a écrit :
How's your list example coming along?

Look, you can download the source code of all the containers library
from:

http://code.google.com/p/ccl/

I have uploaded yesterday the generic listgen. Just see yourself.
I am in the middle of a huge reorganization of the code to accomodate
the generic containers (the first one is the list container but
now I have to do vector and hashtable)
 
J

jacob navia

Le 22/05/12 23:41, Luca Risolia a écrit :
Sure, then the boss says:

"I want rounding to 2 decimals, names flush left in the first
column that must be 16 chars long"

printf("%-16s|%10.2g|\n",name,value);

And how would it look in C++ please?

Please be complete, because I suspect you gave your boss the wrong
solution. Give us a simple program which we can compile and run to
reproduce your results.


void PrintResults(size_t n,const char **names,const double *values)
{
for (size_t i = 0; i<n;i++
printf("%-16s|%10.2g|\n",name,value);
}
 
J

Juha Nieminen

Scott Lurndal said:
Writing a method to format a user-defined type (e.g. a toString() style method)
is just as viable as overloading the << operator, and much more readable.

Having a toString() method is less efficient than being able to write
a value directly to a stream, because now it has to be written to a string
in memory instead.

This is worsened if you allocate said string every time because memory
allocation is a heavy operation. (You could use a static buffer that
doesn't get destroyed after each call to toString(), but then it wouldn't
be thread-safe.)
 
J

Juha Nieminen

jacob navia said:
"I want rounding to 2 decimals, names flush left in the first
column that must be 16 chars long"

And then you have to remember all the manipulators code and get
it right in C++ what a nightmare. After some tinkering you say:
"Who cares?" and write

printf("%-16s|%10.2g|\n",name,value);

and you are done.


Then you change the type of eg. the 'value' array, and your printf()
breaks. (A compiler can only help you if the format string is right
there in the printf() call. However, if the format string is retrieved
from somewhere else, you are out of luck.)

The major problem with printf() is that it's not type-safe. The second
major problem is that it only supports basic types, not user-defined
types.
 
J

jacob navia

Le 23/05/12 16:33, Juha Nieminen a écrit :
jacob navia said:
"I want rounding to 2 decimals, names flush left in the first
column that must be 16 chars long"

And then you have to remember all the manipulators code and get
it right in C++ what a nightmare. After some tinkering you say:
"Who cares?" and write

printf("%-16s|%10.2g|\n",name,value);

and you are done.


Then you change the type of eg. the 'value' array, and your printf()
breaks.


Only if you change from double to long double. Floats and doubles share
the same format.

(A compiler can only help you if the format string is right
there in the printf() call.

As shown in this example. In most cases (I would even say 99.99%) it is
there anyway.
However, if the format string is retrieved
from somewhere else, you are out of luck.)

In gcc you can declare a function "printf-like" if I remember
correctly.
The major problem with printf() is that it's not type-safe.

Not really a big deal. But if that could be a problem for you
use iostreams.
The second
major problem is that it only supports basic types, not user-defined
types.

In many situations you are going to put out numbers and letters,
strings and integers/floating point data.

It is rare that ypu wouldwangt to output a type in a table
in a single piece. But obviously, if you want that use iostreams.

Contrary to many people, I am not religious.

You need iostreams?

Use iostreams.

You need to oitput a correctly formatted table?

Use printf.
 
L

Luca Risolia

Sure, then the boss says:

"I want rounding to 2 decimals, names flush left in the first
column that must be 16 chars long"

printf("%-16s|%10.2g|\n",name,value);


And how would it look in C++ please?


In C++ it would look like:

cout << format << name << value << '\n';

The C++ way also gives you more flexibility: you can swap the columns by
just swapping the variables and without modifying the formatting
manipulator; or you can add as many columns as you want in the same
line, with any type (string or double). You can NOT do this via printf()
without also modifying the formatting string every time.

Here is a program showing what I have said:

#include <iostream>
#include <iomanip>
#include <cstdio>

template<class T> class OStreamWrapper {
public:
OStreamWrapper(T& stream) : m_stream(stream) { }
const OStreamWrapper& operator<<(const double v) const {
m_stream.precision(2);
m_stream << std::setw(10) << std::right << v << '|';
return *this;
}
const OStreamWrapper& operator<<(const char* s) const {
m_stream << std::setw(16) << std::left << s << '|';
return *this;
}
template<class U> // anything else terminates the formatting
T& operator<<(const U& u) const {
return m_stream << u;
}
private:
T& m_stream;
};

class Format {
};

template<class T>
inline OStreamWrapper<T> operator <<(T& stream, Format(*)()) {
return OStreamWrapper<T > (stream);
}

inline Format format() {
return Format();
}

int main() {
const char* name[2] = {"Hello", "World!"};
const double value[2] = {1.123, 123.123};
std::printf("%-16s|%10.2g|\n", name[0], value[0]); // C way
std::cout << format << name[0] << value[0] << '\n'; // C++ way
// You can add columns preserving the formatting
std::cout << format << name[0] << value[0] << name[1] << value[1]
<< '\n';
// ..or you can swap columns by swapping the variables
std::cout << format << value[0] << name[0] << '\n';
return 0;
}
 
L

Luca Risolia

Luca Risolia said:
Sure, then the boss says:

"I want rounding to 2 decimals, names flush left in the first
column that must be 16 chars long"

printf("%-16s|%10.2g|\n",name,value);


And how would it look in C++ please?


In C++ it would look like:

cout<< format<< name<< value<< '\n';


No, you're not counting the OStreamWrapper you need to define.


I am. This is C++.
So, 33 lines to replace one. I don't see how that is a win.

It's a win because you make the code more readable, flexible and
type-safe. It's a win when you want to share the same code and logic
with others; you define the manipulator once and place it in a library
for re-use in big projects,
 
D

Dombo

Op 22-May-12 21:15, jacob navia schreef:
Le 22/05/12 05:27, Adam Skutt a écrit :
It means I can pass any object to the formatting function, and I don't
have to remember the stupid formatting code (if I don't need special
behavior), nor do I have to remember to treat certain types
specially.

Sure, then the boss says:

"I want rounding to 2 decimals, names flush left in the first
column that must be 16 chars long"

And then you have to remember all the manipulators code and get
it right in C++ what a nightmare. After some tinkering you say:
"Who cares?" and write

printf("%-16s|%10.2g|\n",name,value);

and you are done.

Printf is a run time interpreter that uses a simple command language
to describe the formatting of values. There is no alternative in
sight (yet)


The boost format library seems to be an interesting alternative.
 
J

jacob navia

Le 23/05/12 17:26, Robert Wessel a écrit :
Interesting. Any references?

http://www.unixwiz.net/techtips/gnu-c-attributes.html
__attribute__ format

This __attribute__ allows assigning printf-like or scanf-like
characteristics to the declared function, and this enables the compiler
to check the format string against the parameters provided throughout
the code. This is exceptionally helpful in tracking down hard-to-find bugs.
There are two flavors:
__attribute__((format(printf,m,n)))
__attribute__((format(scanf,m,n)))
 
I

Ian Collins

The obvious way to do is is for the caller to pass a buffer, often a block-local
auto (in the traditional sense, not the C++11 sense), into the toString()
method (along with the buffer size). Just like snprintf. Overload it for
std::string arg, if you must.

So you consider that much more readable than overloading the << operator?
 
I

Ian Collins

FWIW, in our reasonably large codebase (~1.5MLOCs over several
projects, of C or C moderately ported to C++), I'd venture that less
than half, probably closer to a third, of *printf calls do not have
the format string in the library call itself. And I'd say that for
complex format strings, the percentage is even lower, as well as for
the more core components.

My experience is similar. The last C code I was working with had the
majority of its format strings in one file to support translation.
That being said, I think the cout/iostream stuff is particularly ugly.
It's only redeeming qualities are extensibility and type safety.

I don't thank anyone would argue with that. I've yet to find an I/O
mechanism that was beautiful, extensible and type safe!
 
I

Ian Collins

Yes, to a wider audience (C programmers who will be tasked to maintain
the code in the future).

C programmers maintaining C++ code is a recipe for nervous breakdowns
and legions of bugs!
The code is all in one place, unlike the overloaded<< operator.

I not sure I understand that bit. Consider a common case for me,
serialising a structure. Given

struct X
{
int a;
B b;
C c;
D d;
};

I would have:

std::eek:stream& operator<<( std::eek:stream& out, const X& x )
{
return out << x.a << ' ' << x.b << ' ' << x.c << ' ' << x.d;
}

Or if X where part of a streamable hierarchy, a single output operator
and a stream member function.

How would a toString() based solution improve readability?

To make the problem more interesting, how would it improve readability
while guaranteeing the full output was atomic in a threaded environment?
 
A

Adam Skutt

I don't thank anyone would argue with that.  I've yet to find an I/O
mechanism that was beautiful, extensible and type safe!

It's hardly attractive, but neither is printf. Lots of people here
seem to assume something along the lines of "terseness is beauty".
While you certainly can be too verbose, I'm not sure that applies to
iostreams. Even if it does, that doesn't become an argument in
printf's favorable, which is unquestionably too terse.

Perhaps the readability 'issue' could have been avoided if formatting
(when needed) and value were combined, so instead of doing:

cout << modifier1 << modifier2 << value;

We do:

cout << format(10, 2, value);

which I think is more consistent with how formatted values are treated
anyway. It certainly removes the need to undo formatting changes for
a one-time format, which is good. However, actually doing this in C++
without resorting to formatting strings might still be pretty ugly,
since C++ lacks keyword parameters. You could do something like:

cout << width(10, precision(2, value));

which is arguably better, but hardly perfect. The same people who
think 'creat' is superior to 'create' will still complain, though.

Adam
 
A

Adam Skutt

Yes, to a wider audience (C programmers who will be tasked to maintain
the code in the future).

If that's a requirement for your C++ codebase, there's only one
solution. This isn't least the bit reasonable.
The code is all in one place, unlike the overloaded << operator.

How is this even possibly true? How can you possibly claim this with
a straight face?

Adam
 
J

jacob navia

Le 24/05/12 00:33, Adam Skutt a écrit :
If that's a requirement for your C++ codebase, there's only one
solution. This isn't least the bit reasonable.


Well, mocking C programmers and C in general is a common habit of many
C++ programmers that somehow feel superior to C programmers that are
seen as "know nothing" backward idiots only able to do menial
tasks like maintenance.

This attitude is widespread in this group, and insulting C programmers
a widely practiced "sport". In this thread we already saw the
"too many braind dead C programmers" sentence, and now this one.
 
I

Ian Collins

Le 24/05/12 00:33, Adam Skutt a écrit :

Well, mocking C programmers and C in general is a common habit of many
C++ programmers that somehow feel superior to C programmers that are
seen as "know nothing" backward idiots only able to do menial
tasks like maintenance.

I don't see any mocking here, but the original suggestion above quite
bizarre. Anyway, I'd wager I'm not the only C++ programmer posting here
who is also C programmer.

I would call anyone a fool (unless they were a wealthy fool!) who asked
me to maintain their Perl code, just as I would be a fool to ask a C
programmer without C++ experience to maintain my C++ code.

Despite their common ancestry, C and C++ have very different idioms.
 
A

Adam Skutt

Have you ever worked at a company?   This happens all the time.   They don't
hire a perl programmer to maintain a 30 line perl script; even most of
my RTL designers write perl.


They also don't generally write C++ code with the expectation that it
must be maintained by programmers who only know, or predominately
know, C. It's also irrelevant, as it cannot defend your original
claim. You originally said that C++ code that could be understood by
C programmers is inherently superior to other C++ code.
That doesn't preclude someone from learning C++ and its idioms in a few days.

Few people are capable of learning C++ and its idioms in a few days,
even with the right texts and a lot of patience.
OJT happens more often than you think
and it is a foolish architect who assumes otherwise.

Except you weren't advocating for on-the-job training or anything of
the sort. You were claiming that writing code in programming language
X so it would be more comprehensible to people who are familiar with
programming language Y yields superior code.

I don't see how, nor why, assuming that some people will have to learn
a programming language on the job, or even on the fly, ipso facto
justifies lowering the quality of the codebase simply to cope with
those people. You're basically assuming your colleagues are
incompetent and planning your project accordingly.

Adam
 
J

jacob navia

Le 23/05/12 11:04, gwowen a écrit :
That bad, eh?

????

If you want to say something say it. This is a free world.

But that doesn't make any sense:

"That bad" means what?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top