New output model

P

Peter Ammon

I invented a new (AFAIK) way of doing the O part of C's I/O that is
safer than printf() without sacrificing much convenience. It looks a
bit like C++'s formatted output, without the stupid gotchas (but with
all of the ugly syntax.)

It looks like this:

start()->i(4)->s(" score and ")->f(7)->s(" years ago...")->c('\n');

This would output

4 score and 7.000000 years ago...

Here's some sample code for it.

#include <stdio.h>

static const struct funcs *start(void), *print_string(const char*),
*print_int(int), *print_double(double), *print_char(char);

static const struct funcs {
const struct funcs * (*s)(const char*);
const struct funcs * (*i)(int);
const struct funcs * (*f)(double);
const struct funcs * (*c)(char);
} funcs_val = { print_string, print_int, print_double, print_char };


static const struct funcs* start(void) {
return &funcs_val;
}

static const struct funcs* print_string(const char* s) {
fputs(s, stdout);
return &funcs_val;
}

static const struct funcs* print_int(int s) {
printf("%d", s);
return &funcs_val;
}

static const struct funcs* print_double(double s) {
printf("%f", s);
return &funcs_val;
}

static const struct funcs* print_char(char s) {
printf("%c", s);
return &funcs_val;
}

int main(void) {
start()->i(4)->s(" score and ")->f(7)->s(" years ago...")->c('\n');
return 0;
}

Let me know if you find this useful, can improve on it, point out bugs, etc.

-Peter
 
A

Arthur J. O'Dwyer

I invented a new (AFAIK) way of doing the O part of C's I/O that is
safer than printf() without sacrificing much convenience. It looks a
bit like C++'s formatted output, without the stupid gotchas (but with
all of the ugly syntax.)

Hoo yeah. I don't think anyone will ever use this syntax, ever,
but it *is* a good example of how to do stupid things with operator
overloading even when the language doesn't support operator
overloading. :)
Let me know if you find this useful, can improve on it, point
out bugs, etc.

It would be prettier if the output functions weren't tied to
stdout -- that is, if you could write something like

FILE *fp = ...;
cxxstr_stream out = cxxstr_open(fp);
out->i(4)->s(" score and ");

(The above can't be done in general in C, but can it be done by
adding some more function calls in an unobtrusive manner?)
Then obviously you'd add a global object 'cxxstr_cout' that would
send things to 'stdout', and 'cxxstr_cerr' for 'stderr', and so on.
I don't want to think about it any more.

Interesting, though. :)
-Arthur
 
N

Nick Austin

I invented a new (AFAIK) way of doing the O part of C's I/O that is
safer than printf() without sacrificing much convenience. It looks a
bit like C++'s formatted output, without the stupid gotchas (but with
all of the ugly syntax.)

It looks like this:

start()->i(4)->s(" score and ")->f(7)->s(" years ago...")->c('\n');

Personally, I'd rather stick with printf.

However if such a system were forced upon me I'd suggest the
following:
static const struct funcs {
const struct funcs * (*s)(const char*);
const struct funcs * (*i)(int);

would be better as long.
const struct funcs * (*f)(double);
const struct funcs * (*c)(char);

I would also include the following:
unsigned long
pointers (void *)

Also I would add a second version of each function that allowed
the format string as an argument. This allows flags, field
widths, precision and conversion to be specified.

Finally, for convenience I would include a newline() function.

The complete function table would therefore be:

static const struct funcs
{
const struct funcs *( *s )( const char * );
const struct funcs *( *l )( long );
const struct funcs *( *u )( unsigned long );
const struct funcs *( *f )( double );
const struct funcs *( *c )( char );
const struct funcs *( *p )( void * );
const struct funcs *( *fs )( const char *, const char * );
const struct funcs *( *fl )( const char *, long );
const struct funcs *( *fu )( const char *, unsigned long );
const struct funcs *( *ff )( const char *, double );
const struct funcs *( *fp )( const char *, void * );
const struct funcs *( *newline )( void );
}

Nick.
 
D

Daniel Haude

On Thu, 23 Oct 2003 17:17:52 -0700,
in Msg. said:
I invented a new (AFAIK) way of doing the O part of C's I/O that is
safer than printf() without sacrificing much convenience.

I LOVE it although I think it's both ugly and useless. A nice example of
how to make legal ANSI C look like something completely different. I'll
never use it, and I doubt anybody else (except you, perhaps) will ever use
it, but it's just so damn clever.

Thanks!
--Daniel
 
C

CBFalconer

Peter said:
I invented a new (AFAIK) way of doing the O part of C's I/O that
is safer than printf() without sacrificing much convenience. It
looks a bit like C++'s formatted output, without the stupid
gotchas (but with all of the ugly syntax.)

It looks like this:

start()->i(4)->s(" score and ")->f(7)->s(" years ago...")->c('\n');

This would output

4 score and 7.000000 years ago...

Take a look at how Pascal handles such output. The source code
would be:

write(fp, 4, ' score and ', 7.0, ' years ago...');

or, to impose the final \n:

writeln(fp, 4, ' score and ', 7.0, ' years ago...');

with omission of fp causing the output to go to stdout, or the
equivalent. Each field can also have ": fld" added, to specify
the size of the field in which to write, and floating point values
can have ": fld : places" to specify both field and decimal
places. This is all done within the compiler, and the run time
has routines for writing each of integers, characters, strings,
floats, booleans, and line endings.

The awkward and lengthy C++ syntax is an effort to implement this
convenience without having the compiler aware of the output
methods. C is unable to handle it directly without alterations to
the language, but it could be easily done with an extension.

In a way you have found an ingenious way to build this around the
printf family, by replacing the return value from printf itself
and leaving the type selection to the programmer. You might
improve it by storing fp in the funcs record, and then adding fp
to the start routines parameters. This would allow use on any
text file. Similarly you could create the funcs record with a
replacement for fopen.
 
N

nobody

Peter Ammon said:
I invented a new (AFAIK) way of doing the O part of C's I/O that is

Invention is too strong word, perhaps. But I bet that Patent Office
would fall for it :)
safer than printf() without sacrificing much convenience. It looks a

Safer in what ways? And it's apparent that our opinions differ
in what "convenience" means.
bit like C++'s formatted output, without the stupid gotchas (but with
all of the ugly syntax.)

I'm failing to see how it's like any's language *formatted* output,
bit or not-bit. And IMHO syntax is uglier than C++'s (less "natural").
It looks like this:

start()->i(4)->s(" score and ")->f(7)->s(" years ago...")->c('\n');

This would output

4 score and 7.000000 years ago...
and it would output
0.000000
for
start()->f(0.0000001)->c('\n')
[snip]
Let me know if you find this useful, can improve on it, point out bugs, etc.
It was maybe interesting as an exercise, but impractical due
- limitations (offers only fraction of functionality of printf(), no
possibility
of error detection)
- at least one bug (see above)
- performance implications (function calls overhead on top of
internally used printf() - 11 calls in your example instead of 1)
- syntax (more typing than for printf())

For time being, I will stick with printf() family.

I'm afraid that my answer falls into "etc." category. As saying goes, be
careful what you wish for ...;-)
 
K

Kevin Goodsell

nobody said:
Safer in what ways?

If this isn't obvious to you, then I question whether you should be
using printf() (and similar functions) at all.

-Kevin
 
N

nobody

Kevin Goodsell said:
If this isn't obvious to you, then I question whether you should be
using printf() (and similar functions) at all.
No, it's not obvious to me, how calling printf() via "wrapper" is safer than
calling it directly with same argument values (e.g calling print_int(4)
instead
of printf("%d", 4)). Also, if one is aware of limitations, implications and
pittfalls of something, does it imply that (s)he will use it in unsafe way?
I didn't have yet encounter problems related to usage of printf() family
functions in programs I wrote so far. I will welcome any substantiation
of your questioning, or better yet, if you can show me why OP's code
is safer. I personally don't mind to learn from this NG (or any other
source,
for that matter).
 
A

Arthur J. O'Dwyer

No, it's not obvious to me, how calling printf() via "wrapper" is safer than
calling it directly with same argument values (e.g calling print_int(4)
instead of printf("%d", 4)).

extern int printf(const char *, ...);
extern int print_int(int);

printf("%d", 4); /* legal and safe */
print_int(4); /* legal and safe */
printf("%d", "x"); /* legal and unsafe */
print_int("x"); /* illegal */

Variadic functions allow the unwary user to mess up big-time.
The print_int() style automatically handles implicit conversion
to the right type, and forces the compiler to warn about incorrect
usage.
Also, if one is aware of limitations, implications and
pittfalls of something, does it imply that (s)he will use it in unsafe way?

Ever done any of the following?

int *p;
long l;
int i;
printf("%p\n", p);
printf("%d\n", l);
printf("%x\n", i);

Then your code is (technically speaking) incorrect. Variadic
functions don't perform as many useful conversions as you might
think. The correct lines would be

printf("%p\n", (void *) p);
printf("%ld\n", l);
printf("%x\n", (unsigned int) i);

HTH,
-Arthur
 
K

Kevin Goodsell

nobody said:
No, it's not obvious to me, how calling printf() via "wrapper" is safer than
calling it directly with same argument values (e.g calling print_int(4)
instead
of printf("%d", 4)). Also, if one is aware of limitations, implications and
pittfalls of something, does it imply that (s)he will use it in unsafe way?
I didn't have yet encounter problems related to usage of printf() family
functions in programs I wrote so far. I will welcome any substantiation
of your questioning, or better yet, if you can show me why OP's code
is safer. I personally don't mind to learn from this NG (or any other
source,
for that matter).

One meaningful metric for "safety" in this case is "the number of
opportunities for one to make a mistake." printf() gives you many, many
chances to make a mistake, and the compiler generally cannot help you
locate such mistakes - they quietly invoke undefined behavior.

How many ways can you screw up a call to print_xxx() (where xxx is some
type)? Basically, you can call it with the wrong type. In this case, one
of two things will happen. Either there is no implicit conversion and
the compiler will issue a diagnostic, or there is an implicit conversion
and the result will be well-defined. Nothing bad can happen other than
the printed result appearing incorrect.

How many ways can you screw up a printf() call? I doubt I can list them all.

* Wrong format specifier for the type
* Wrong size specifier for the type
* Transposed arguments
* Too few arguments
* Too many arguments (technically not an error, but probably a bug)
* Incorrect argument type after promotion

Most of these lead to undefined behavior, and the compiler cannot, in
general, detect the error.

You've never made one of these mistakes?


....are you sure?


-Kevin
 
N

nobody

Arthur J. O'Dwyer said:
extern int printf(const char *, ...);
extern int print_int(int);

printf("%d", 4); /* legal and safe */
print_int(4); /* legal and safe */
printf("%d", "x"); /* legal and unsafe */
print_int("x"); /* illegal */

Variadic functions allow the unwary user to mess up big-time.
The print_int() style automatically handles implicit conversion
to the right type, and forces the compiler to warn about incorrect
usage.
way?

Ever done any of the following?

int *p;
long l;
int i;
printf("%p\n", p);
printf("%d\n", l);
printf("%x\n", i);
Hard to say if ever, but I think not in last N years.
Then your code is (technically speaking) incorrect. Variadic UB

functions don't perform as many useful conversions as you might
I know. They (or rather compiler) can't.
think. The correct lines would be

printf("%p\n", (void *) p);
printf("%ld\n", l);
printf("%x\n", (unsigned int) i);
Thanks for an answer. I've got the point. Though I still stick with
printf:)-).
(It was semantic issue /or my lack of understanding English/. This code
*is* safer, but that doesn't imply everyone not using it will write
less-safe
code.)
 
N

nobody

Kevin Goodsell said:
One meaningful metric for "safety" in this case is "the number of
opportunities for one to make a mistake." printf() gives you many, many
chances to make a mistake, and the compiler generally cannot help you
locate such mistakes - they quietly invoke undefined behavior.

How many ways can you screw up a call to print_xxx() (where xxx is some
type)? Basically, you can call it with the wrong type. In this case, one
of two things will happen. Either there is no implicit conversion and
the compiler will issue a diagnostic, or there is an implicit conversion
and the result will be well-defined. Nothing bad can happen other than
the printed result appearing incorrect.

How many ways can you screw up a printf() call? I doubt I can list them all.

* Wrong format specifier for the type
* Wrong size specifier for the type
* Transposed arguments
* Too few arguments
* Too many arguments (technically not an error, but probably a bug)
* Incorrect argument type after promotion

Most of these lead to undefined behavior, and the compiler cannot, in
general, detect the error.

You've never made one of these mistakes?


...are you sure?
Thanks for an answer. Will nor repeat what I've replied to Arthur, but yes,
I've made at least one of those mistakes in the past, but also I was talking
about "problems" (hidden bugs discovered by QA or customers) - none of
these (I'm talking about printf() only). All mistakes were found and
correected quickly when I tested my code, and hopefully I've better
knowledge now than some years back.
 
J

James Antill

One meaningful metric for "safety" in this case is "the number of
opportunities for one to make a mistake." printf() gives you many, many
[snip]

How many ways can you screw up a printf() call? I doubt I can list them all.

* Wrong format specifier for the type
* Wrong size specifier for the type
* Transposed arguments
* Too few arguments
* Too many arguments (technically not an error, but probably a bug)
* Incorrect argument type after promotion

Most of these lead to undefined behavior, and the compiler cannot, in
general, detect the error.

You've never made one of these mistakes?

...are you sure?

I've used a static format checker that tells me if I've made any of the
above, for as long as I can remember. I'm sure I had problems before I did
that, and I'm sure I haven't since I got told about them at compile time.
If I had to go back to not having a static format checker, then I guess
I might look for a way to format data without printf() ... thankfully that
isn't a problem I have to solve.
 
D

Doug Eleveld

Very interesting I think. If I was you I might try returning a struct
instead of a pointer so you could actually add data to the struct as
you go along. i.e. the start function could have a FILE* or char* of
where to redirect.

It would also then use the struct reference '.' which I think looks
better than '->', although it may be confusing in this context. I
dont know if it would reduces efficieny or not.

You do realize that you could use this for formatted input too?

I think this is very interesting, could you keep me informed as to any
improvements you make?

Doug Eleveld
(e-mail address removed)
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top