A C showstopper

C

Chris M. Thomasson

[...]
A nice hack IMHO to implement snprintf() on systems where it is not
available,
is tho fprintf() to a file, and read back the answer. This will only need
one pass, and the arguments are only evaluated once.

On a unix-like systems, the file can even be unlinked after creation.
Since all of the printing takes place in OS-buffers, this will be almost
optimal.

That's basically what I did here except I used `vfprintf()':

http://clc.pastebin.com/f31279cba
 
B

bartc

Chris M. Thomasson said:
you would need to copy the string if `getmonthname()' returned a pointer
to a buffer that can be changed by some other locus of control/mysterious
force.

Yes. And that is not impossible as the rest of your reply suggests.
 
C

Chris McDonald

This alone demonstrates that C is not a viable programming language
anymore, and may never have been, for applications other than writing
operating systems and compilers.


So (after 26 follow-up articles, so far) this wasn't really
a "C showstopper" after all, as far as the C languages goes.

We've seen that pre-C99 solutions are possible, if cumbersome, and
that C99 solutions are quite reasonable. C remains a viable language,
and those of us who don't shout "Fire" as soon as our comprehension is
challenged can get back to C programming.

I'm suspending work on the "adventure" project and shall return when
I'm not so disgusted as I am now.

.... but, hopefully, it will be a "Spinoza showstopper".
 
C

Chris M. Thomasson

bartc said:
Yes. And that is not impossible as the rest of your reply suggests.

It's definitely not impossible. Well, perhaps improbable, but not
impossible.
 
F

Flash Gordon

Richard said:
Yes and no. He who barks at the moon has pointed to a real
problem; Rui asked if snprintf solves the problem. A fair answer
is yes and no, depending on how reads the tea leaves and the
original assertion.

I would read what he wrote as suggesting using snprintf to solve the
problem. Mind you, thinking about it, vsnprintf might be more useful...
The problem is that applying a format specification to a set of
arguments produces a string of unknown length. Snprintf "solves"
the problem by failing to complete the production of the output
string.

Which allows you to solve the problem in one of the two ways the OP
suggested.
As you say, one can use snprintf to find out the size of the
buffer one does need; one way is to keep increasing the size of
the buffer until there is enough space. However this is not a
general solution - if the arguments are expressions with side
effects so that the output string changes each time we resize the
buffer. In short, the technique is a hack. None-the-less it
works most of the time. For that matter allocating a silly
ssized buffer works most of the time.

Check the return value and save yourself all of that work. Actually, I
would use vsnprintf and wrap it in a function which solves the
multiple-evaluation problem.
It would be useful if there were a sprintf variant that does not
require the user to supply a buffer but which takes care of
allocation and returns a pointer to the resulting formatted
string.

There is, it's called snprintf.
And, of course, snprintf isn't part of C90.

True. However, the OP alluded to it not solving the problem, where as it
does actually provide a solution.
 
B

bartc

Chris said:
It's definitely not impossible. Well, perhaps improbable, but not
impossible.

I can think of half-a-dozen ways the result of getmonthname() (or whatever
function) can change between two successive calls. For a start, the string
can come from a file. On the next call, the file contents might be
different.

If the string points to the contents of a memory-mapped device, those
contents again can change between calls.

The string might simply point into some shared memory, especially on a
simple microprocessor system, where some interrupt or other can change the
contents.

If you added an extra argument to your snprintf call, one with side-effects,
yet again there's another way for the string to be changed! Although
side-effects can more directly cause problems anyway.

The string can be the result of character-recognising part of the screen, or
speech-to-text conversion, or just text streaming from stdin!

Is that six ways yet? At least.
 
C

Chris M. Thomasson

bartc said:
Chris M. Thomasson wrote: [...]
It's definitely not impossible. Well, perhaps improbable, but not
impossible.

I can think of half-a-dozen ways the result of getmonthname() (or whatever
function) can change between two successive calls. For a start, the string
can come from a file. On the next call, the file contents might be
different.

If the string points to the contents of a memory-mapped device, those
contents again can change between calls.

The string might simply point into some shared memory, especially on a
simple microprocessor system, where some interrupt or other can change the
contents.

If you added an extra argument to your snprintf call, one with
side-effects, yet again there's another way for the string to be changed!
Although side-effects can more directly cause problems anyway.

The string can be the result of character-recognising part of the screen,
or speech-to-text conversion, or just text streaming from stdin!

Is that six ways yet? At least.

Don't you think that this aspect of said function would have the behavior
explicitly documented? Otherwise, it's return value is completely useless
because it's going to be subjected to race-conditions. Think about it for a
moment.


char* month = getmonthname();

puts(month);


How can you be sure that the output will be coherent? What good what a
crappy function that like be? Unless, you could do something like:


char* month = getmonthname();
getmonthname_lock();
puts(month);
getmonthname_unlock();



This is why I say that a function that behaves like that is improbable, or a
very crappy design.
 
C

Chris M. Thomasson

[...]
char* month = getmonthname();
getmonthname_lock();
puts(month);
getmonthname_unlock();
[...]
______________________________________________________________________
void foo()
{
int size;

getmonthname_lock();

size = snprintf(NULL, 0, "%s", getmonthname());

if (size > -1)
{
char* buf;

++size;
buf = malloc(size);
if (buf)
{
int xsize = snprintf(buf, size, "%s", getmonthname());

getmonthname_unlock();

if (size != xsize)
{
puts("OH SH%T! the buffer was changed by something!!!");

if (xsize > size)
{
puts("OH FUC%! we are DOOMED!!!!!!");
}
}

free(buf);
}
}

else
{
getmonthname_unlock();
}
}
______________________________________________________________________




Don't you think a function that returned a buffer which can be mutated on
the fly would provide the user a method that would allow her/him to perform
atomic operations on said buffer? Otherwise, what good would it be?
 
B

bartc

Chris M. Thomasson said:
bartc said:
Chris M. Thomasson wrote: [...]
It's definitely not impossible. Well, perhaps improbable, but not
impossible.

I can think of half-a-dozen ways the result of getmonthname() (or
whatever function) can change between two successive calls. For a start,
the string can come from a file. On the next call, the file contents
might be different.

If the string points to the contents of a memory-mapped device, those
contents again can change between calls.

The string might simply point into some shared memory, especially on a
simple microprocessor system, where some interrupt or other can change
the contents.
....
Don't you think that this aspect of said function would have the behavior
explicitly documented? Otherwise, it's return value is completely useless
because it's going to be subjected to race-conditions. Think about it for
a moment.


char* month = getmonthname();

puts(month);


How can you be sure that the output will be coherent? What good what a
crappy function that like be? Unless, you could do something like:


char* month = getmonthname();
getmonthname_lock();
puts(month);
getmonthname_unlock();



This is why I say that a function that behaves like that is improbable, or
a very crappy design.

Perhaps I used an over-elaborate example for the snprintf() issue.
itoa(rand()) might have been simpler...

Getting back to getmonthname() and deriving the value from a memory-mapped
device ram for example. Even assuming all your safeguards are in place, it's
possible that such a function will read the string from the device memory
while the device is locked, copy it to a static area, and return a pointer
to that.

It will still be the case that a subsequent getmonthname() call could
produce a different string, unless the locks are done at the higher-level.
But the user of snprintf() and getmonthname() should not need to bother with
that. He only needs to know that a call to getmonthname() in August will
produce a different value to a call made in September.
 
C

Chris M. Thomasson

bartc said:
Chris M. Thomasson said:
bartc said:
Chris M. Thomasson wrote: [...]
It's definitely not impossible. Well, perhaps improbable, but not
impossible.

I can think of half-a-dozen ways the result of getmonthname() (or
whatever function) can change between two successive calls. For a start,
the string can come from a file. On the next call, the file contents
might be different.

If the string points to the contents of a memory-mapped device, those
contents again can change between calls.

The string might simply point into some shared memory, especially on a
simple microprocessor system, where some interrupt or other can change
the contents.
...
Don't you think that this aspect of said function would have the behavior
explicitly documented? Otherwise, it's return value is completely useless
because it's going to be subjected to race-conditions. Think about it for
a moment.


char* month = getmonthname();

puts(month);


How can you be sure that the output will be coherent? What good what a
crappy function that like be? Unless, you could do something like:


char* month = getmonthname();
getmonthname_lock();
puts(month);
getmonthname_unlock();



This is why I say that a function that behaves like that is improbable,
or
a very crappy design.

Perhaps I used an over-elaborate example for the snprintf() issue.
itoa(rand()) might have been simpler...
Granted.




Getting back to getmonthname() and deriving the value from a memory-mapped
device ram for example. Even assuming all your safeguards are in place,
it's
possible that such a function will read the string from the device memory
while the device is locked, copy it to a static area, and return a pointer
to that.

It will still be the case that a subsequent getmonthname() call could
produce a different string, unless the locks are done at the higher-level.

Yes, yes:


getmonthname_lock();
char* month1 = getmonthname();
char* month2 = getmonthname();
assert(! strcmp(month1, month2));
getmonthname_unlock();


I would expect that the assertion would always pass.



But the user of snprintf() and getmonthname() should not need to bother
with that. He only needs to know that a call to getmonthname() in August
will produce a different value to a call made in September.

That's still a race condition waiting to happen if it returns a pointer to
some internal static buffer. If the locking is not used, one might get an
output of:

Augtember


or some incoherent output.
 
C

Chris M. Thomasson

Flash Gordon said:
Richard Harter wrote: [...]
It would be useful if there were a sprintf variant that does not
require the user to supply a buffer but which takes care of
allocation and returns a pointer to the resulting formatted
string.

There is, it's called snprintf.


I believe it's called `asprintf'
 
S

spinoza1111

spinoza1111wrote:

What about snprintf() ?

I covered that as C99's answer. C99's answer is no good. You replace
the possibility of memory overwrites with the possibility of something
that can be EVEN WORSE.

This is the error no-one sees, where an snprintf truncates silently.
This type of error normally affects the end-user, including clients of
systems whose information is destroyed (such as military veterans
trying to get medical care, whose forms fail to include the required
information). C programmers might think it's cute to truncate. I
don't.
 
S

spinoza1111

"The problem is that any sprintf whatsoever, insofar as it formats
strings, has no control over string length. Most code I've seen
decides to create some silly "buffer" of some silly size. "

you still have to create some silly buffer of some silly size.

Correctomundo. As soon as a programmer starts yapping about creating a
"buffer" you know he's a dork.
 
S

spinoza1111

He refers to it, but does not understand it well enough to see that it
provides exactly the tool he needs to implement what he wants whilst
also providing the flexibility to do other things.

Hey, CREEP, reply to the poster whose points you're addressing or
don't post. You're wrong, and at this point outvoted. The wikipedia
points out the problem you don't see as does another poster. Clearly,
your software is fulla "buffers" which silently overflow and no-one's
the wiser.
 
S

spinoza1111

Now read what Rui wrote. snprintf does not have that problem and can
even be used to find out the size of the buffer you do need.

(Sigh). How do you know the size of the "buffer" you do need? You're
already in a mess, Flash, because you think the software can
predetermine the size! Go join Twitter.
 
S

spinoza1111

Yes and no. He who barks at the moon has pointed to a real

Hey, CREEP, I'll thank you to use my name when you use my
contributions. It's spinoza1111.
problem; Rui asked if snprintf solves the problem.  A fair answer
is yes and no, depending on how reads the tea leaves and the

A fair answer is NO.
original assertion.

The problem is that applying a format specification to a set of
arguments produces a string of unknown length.  Snprintf "solves"
the problem by failing to complete the production of the output
string.

As you say, one can use snprintf to find out the size of the
buffer one does need; one way is to keep increasing the size of
the buffer until there is enough space.  However this is not a
general solution - if the arguments are expressions with side
effects so that the output string changes each time we resize the
buffer.  In short, the technique is a hack.  None-the-less it
works most of the time.  For that matter allocating a silly
ssized buffer works most of the time.

It would be useful if there were a sprintf variant that does not
require the user to supply a buffer but which takes care of
allocation and returns a pointer to the resulting formatted
string.

There is, and I mentioned it in the OP. It's asprintf, and I
implemented it in 1991. It's available in the GNU compiler. My code
wasn't a "hack". It was a documented solution which keeps on
mallocing, freeing the malloc, and mallocing again until the sprintf
is completed or the remalloc fails.

The problem is that "C", it is clear from this discussion alone, isn't
a singular programming language at all. It's a set of options and
praxis.

Praxis (day to day practice in groups) is part of any language, but a
formal language needs to minimize it. C fails to do so.
 
S

spinoza1111

Chris M. Thomasson said:
[...]
LOL! Well, it does work if you're platform supports something like the
NUL device.
Humm, I suppose you could also use a dummy file.
[...]
Here is another version of the program which uses a temporary file as a
"fallback" just in case the file open fails with the standard names for
some platforms NUL device:

Well, even though this is a crappy little hack, I should still check if
`malloc()' succeeds or not!

ROFL!!!

I guess I should not return `EXIT_SUCCESS' when `malloc()' fails. Too funny!

;^)

[...]

BTW, how many of you find that the overall idea contained within the hack I
whipped up might be "useful"?- Hide quoted text -

I implemented it in 1991, kid. So it's useful. Now all you have to do
is change variables to Hungarian and write literate comments as I did.
 
S

spinoza1111

So (after 26 follow-up articles, so far) this wasn't really
a "C showstopper" after all, as far as the C languages goes.

We've seen that pre-C99 solutions are possible, if cumbersome, and
that C99 solutions are quite reasonable.  C remains a viable language,
and those of us who don't shout "Fire" as soon as our comprehension is
challenged can get back to C programming.

I'm afraid your comprehension is challenged, cowboy.

I saw the problem in 1991 and wrote an asprintf as part of a library
of code to address each deficiency of C wrt each problem I
encountered. I tested this library in full. I documented it in full.

I'm looking at having to do the same today all over again since in the
intervening years the problem hasn't been solved by the C community,
which prefers to jerk its collective pud off and hound Herb Schildt.

All you're saying, cowboy, is that "any" problem can be solved by C in
Turing's sense. But as we know, this is bullshit.
... but, hopefully, it will be a "Spinoza showstopper".

**** you, asshole.
 
C

Chris M. Thomasson

messagenews:[email protected]... [...]

BTW, how many of you find that the overall idea contained within the
hack I
whipped up might be "useful"?- Hide quoted text -
I implemented it in 1991, kid.

Hey, thanks for the compliment! You know, it still makes my day when
somebody wants to check my ID when I am purchasing alcohol.

;^)



So it's useful.
Agreed.




Now all you have to do is change variables to Hungarian

There can be a "downside" to Hungarian notation. This is very contrived
example of course but it could possibly happen. Think of prefixing a bunch
of signed int variables with 'int' (e.g., `intMyVar'). Then for whatever
reason you decide to change them to unsigned variables. Well, you're going
to have to change every variable name's prefix to `uint' or something that
represents the variables unsigndness, so to speak.



and write literate comments as I did.

:^)
 
S

spinoza1111

Richard Harter wrote: [...]
It would be useful if there were a sprintf variant that does not
require the user to supply a buffer but which takes care of
allocation and returns a pointer to the resulting formatted
string.
There is, it's called snprintf.

I believe it's called `asprintf'

asprintf isn't available on all compilers. C isn't portable. To write
portable C, you have to reinvent the wheel. To reinvent the wheel
properly, you need to be able to write English in addition to
effective C, and very few people here have the ability to construct a
complex sentence.

To construct an asprintf, you must construct it on top of an snprintf
if you're using a C99 compliant compiler, or else either recreate the
logic of printf. You have to scan for percents and sum the length of
strings and determine the string length of numbers. I suppose you
could use Malcolm's value of 30.

You then have to stress test.

To use the "hacks" presented here, you have to read the code line by
line to make sure the hacker hasn't fucked up. As a personal matter, I
don't like to read most code written by others, since their aesthetic
is usually not mine (I found John Nash's code most pleasant on the
other hand). I'd rather code my own, with longer identifiers and
overall a more elegant way of expression...not that it's perfect and
bug free.

C is great in worlds without strings. That would be an operating
system kernel. If you want to write C, learn how to develop operating
systems. Don't mess up military veterans' benefits, small businesses,
or schools with this language.
 

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
474,266
Messages
2,571,075
Members
48,772
Latest member
Backspace Studios

Latest Threads

Top