EZ fwide[R]? It ain't all that. Roll it up and smoke it, you won't get very high....

by Michael S. Kaplan, published on 2009/06/23 10:01 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2009/06/23/9797156.aspx

Conformance to standards seems to be a pretty big deal these days in many different parts of Microsoft.

Not all, mind you -- that is something I know about through both law of averages (there are a lot of groups at Microsoft!) and also some specific knowledge (I've chatted with a few of them now and again).

But by and large if there is a standard related to the work people are doing at the company, then there is some degree of effort to conform. A statistically significaant trend, you might say.

I was thinking about this other day when people were having kind of a conversation about the C runtime fwide function.

The documentation is something like this:

Run-Time Library Reference

int fwide(
   FILE *stream,
   int mode;

Pointer to FILE structure (ignored).

The new width of the stream: positive for wide character, negative for byte, zero to leave unchanged. (This value is ignored.)

Return Value
This function currently just returns mode.
The current version of this function does not comply with the Standard. 

Okay, it is being kind of upfront here on the whole conformance issue.

The documentation hints that this function basically returns whatever it is passed in the mode parameter, so I'll be clear and say that is exactly what it is doing.

Of course this makes the claim that the mode parameter is ignored kind of inaccurate -- it lives and dies by that parameter, but we'll let that slide.

Let's look at the standard itself to see what conformance would mean, what it would look like. It is over in C99, a standard that Microsoft has taken around the dancefloor but not yet gotten fully busy with just yet: The fwide function

int fwide(FILE *stream, int mode);

The fwide function determines the orientation of the stream pointed to by stream. If mode is greater than zero, the function first attempts to make the stream wide oriented. If mode is less than zero, the function first attempts to make the stream byte oriented.Otherwise, mode is zero and the function does not alter the orientation of the stream.

The fwide function returns a value greater than zero if, after the call, the stream has wide orientation, a value less than zero if the stream has byte orientation, or zero if the stream has no orientation.

This is rather vague -- either Microsoft's implementation is mostly right or the standard is saying this:

I could spend a little bit of time discussing how fundamentally useless this function would be even if this is the meaning and it were fully conformant; this seems like an important point. But we'll leave it alone for a moment and get back to this point in a bit.

Now the fundamental issue here is that Microsoft's implementation of the file stream does not store any kind of attribute on it indicating its wideness or lack thereof. This is important as it means all the information about trying to change this attribute on a stream is not going to happen. In the words of Yoda:

"There is do, and not do; there is no Try."

In this case, Microsoft improves on Yoda a bit -- there is no "not do" either. :-)

Now if you passed <0 or >0 then you wanted the function to try to do something, and it is returning that it succeeded. This does meet the letter of the law in regards to conformance but obviously violates the spirit since if you call the function once you might reasonably expect the call to impact what will happen later. And it won't.

And if you passed 0, then you didn't want the function to change anything, you were just asking a question. The Microsoft explanation just answers the question with the same value that means "who knows?".

It is probably just as well that the docs claim the function is unimplemented since by any sort of reasonable man standard it is probably not implemented.

Would it be nice if you could write to/read from the stream using whatever functions you wanted (wide or narrow) and have it automatically do conversions to follow the behavior of fwide? Maybe. I tend to hate behavior that will silently but happily do a ton of conversion work that may not be required. But I can't claim that there aren't people who would find it useful.

Irregardless, at this time it is not out there, so the results shouldn't be expected to conform.

Now clearly conformance is, on the whole, a good thing. But one would be hard put to claim with authority that the lack of rush to support the standard for this one case would be likely to hurt anyone.

Does anyone disagree?

I mean, based on practical reasons, not lofty "it's the standard" type reasons, of course....

John Cowan on 23 Jun 2009 5:51 PM:

I don't think your equation of 0 with failure is correct.  In particular, suppose a model where (a) streams are either narrow or wide, (b) a narrow stream can be widened only if nothing has been read from/written to it, and (c) a wide stream can't be narrowed at all (because of buffering issues, say).  In that case, fwide(wideStream, -1) should return 1, not 0, and fwide(narrowStream, 1) might return either 1 or -1, but in no case 0.

The Microsoft model seems to be one in which streams never have any orientation.  Therefore, an attempt to widen does nothing and returns 0 (not oriented); an attempt to narrow does nothing and returns 0 (not oriented), and a query returns 0 (not oriented).  In short, the correct implementation is "int fwide(FILE *stream, int mode) { return 0; }".  Go forth and fix it.

Mihai on 23 Jun 2009 6:37 PM:

This looks like a work-around for something that I have seen the Linux world (UNIX? POSIX?): once you write something stream-oriented to a file, it gets set to that and does not accept output any other way.

This is kind of disconcerting when you see it first:

   wprintf( L"Hello wide world" );

   printf( "Hello narrow world" );

You will only see the first message (and all the other messages that use "wide APIs"), while the narrow ones get discarded.

As a solution, adding another a "wideness status" to a stream looks very clunky to me. A bit of a repeat of the text/binary file distinction in the MS-DOS/Windows world which makes all the POSIX world yell "bloody murder" :-)

Michael S. Kaplan on 23 Jun 2009 7:43 PM:

John, not sure I understand what you are saying here. Since it never technically does in fact fail, changing to what you are asking for would break callers for no reason!

Mike Dimmick on 24 Jun 2009 6:11 AM:

DevDiv's position on C99 is that C is completely superseded by C++, and that the target is C++0x conformance (hey, they'll get to full C++98 eventually). C99 falls under the heading of 'too late'.

I think, though, that it must have been in an earlier version of the C standard as it's been in the MS runtime at least as far back as VS 6.0! Possibly Amendment 1 of ISO 9899:1990, since I think this is where wide character APIs were first standardised.

If John's point is correct about the wide vs byte-oriented APIs working or not on other systems, then arguably fwide's behaviour is correct: both sets of APIs work on any stream, and it may well be better to say 'yes that set of APIs will now work' than to say 'I couldn't do that'.

Unfortunately it looks like 9899:1990 is no longer available from the ANSI standards store, only the 1999 version.

Pinky on 24 Jun 2009 7:16 AM:

"Conformance to standards seems to be a pretty big deal these days in many different parts of Microsoft."

I wish I'd known this was going to be a comedy post; I've just spat coffee all over my monitor.

John Cowan on 25 Jun 2009 3:57 PM:

Okay, less compactly:

Returning 0 from fwide() does not mean failure.

I repeat, returning 0 from fwide() does not mean failure.

It means that the stream has no orientation.

Microsoft streams have no orientation, no matter what orientation you try to set.

Therefore, fwide() should return 0.

The current implementation of fwide() is grossly inconsistent.  For example, if you call fwide(s, 1) to set a stream to wide-oriented, it returns 1 to indicate that the stream is now wide-oriented.  But if you ask for its orientation with fwide(s, 0), it returns 0, which means the stream has no orientation!  (Same story for -1, of course.)

There are no wide-oriented streams.

There are no byte-oriented streams.

There are only streams with no orientation.

fwide() should always return 0.

Michael S. Kaplan on 25 Jun 2009 10:14 PM:

I prefer the "weak-minded function, susceptible to the Jedi Mind Trick" explanation:

The fwideEx function will also take you to Jabba now....

Please consider a donation to keep this archive running, maintained and free of advertising.
Donate €20 or more to receive an offline copy of the whole archive including all images.

go to newer or older post, or back to index or month or day