Same structures, different names

B

Ben Bacarisse

James Kuyper said:
Yes, that's precisely my point.


As a general rule, if a question does not involve OS- or
compiler-specific issues, the code can always be rewritten to make no
use of or reference to anything involving that particular OS or
compiler, while retaining the issue you wish to discuss. Such a
re-write should be performed before posting it here.

I find that overly prescriptive. CS types are usually good at
abstracting general problems from specific instances so I would not
worry about a C issue being expressed in less that standard terms.
Should such a re-write turn out to be impossible, then that very
impossibility indicates that the question should be posted to an OS-
or compiler-specific forum, in order to get a more useful answer.

If you consider the skill-level of many of the questions here I think
you will find that this advice is impractical. If someone is having
trouble with arrays and pointers, re-writing their UI call-backs to
avoid specific UI functions might introduce a whole new set of issues
with the code and you get the classic "well that's not actually the code
I have" reply when you spend time commenting on it.

Don't get me wrong: I appreciate it when someone goes to the trouble of
making a small self-contained standard C example that illustrates their
problem, but I don't think it is possible to insist that everyone do
this or have their question ruled off topic.
 
K

Keith Thompson

DSF said:
I never understood the overwhelming desire that example code be
entirely standard-based: There is a comp.std.c group for that.

comp.std.c doesn't mean "discussion of standard C", it means "discussion
of the C standard", i.e., of the documents that define the language.
comp.lang.c is for discussion of the language defined by those
documents.

The line between the two can be hazy, and I usually don't worry about it
too much -- though if somebody asks a general "What's wrong with my
code?" message in comp.std.c, I often suggest that they'll have better
luck in comp.lang.c.
(True,
that makes it easier for many to compile and test for themselves, but
quite often errors are glaringly obvious.) My feeling has always been
that if a question is based on the features of a particular compiler
or operating system, it is off topic. If the question involves the C
language, example code that includes OS specific calls or compiler
specific code should not make the question off topic.

I think I agree. But if you (that's a generic "you") *can* show us a
portable C program that exhibits the problem, that's going to be
helpful. On the other hand, restructuring the code to make it portable
can sometimes mask the original problem.

Judicious application of common sense is often helpful.
 
J

J. J. Farrell

DSF said:
...

static inline struct bar foo2bar(struct foo val)
{
struct bar r;
r.a = val.a;
r.b = val.b;
return r;
}
...

The above prototype and function compile fine as C++, but produce
syntax errors unless "inline" is removed.

It will only produce syntax errors if you're using an old definition of
C, it's valid in C as of 1999.
 
B

Ben Bacarisse

DSF said:
Well, if the compiler can't even be forced to accept foo is the same
as bar, I don't see how it could optimize away the copying of
individual elements and just replace foo with bar. If it could do
that, a simple casting would work.

Optimising usually takes place at a low level after most notions about
types have disappeared. I would have thought the optimiser will simply
see lots of apparently pointless data moves. Of course, if the types
are in fact different in some crucial way, then the moves are needed and
putting the two structs into a union would fail.

<snip>
 
D

DSF

or you could use a C99 compound literal making the function body simply

return (struct bar){val.a, val.b};


Not quite. Function calls are not "lvalues" which means (in part) that
you can't take the address of the result. So if you need a bar in some
context where it's address gets taken, you can use the function call.

I think you meant can't? I understand that.

Such as
int need_bar(struct bar br);

could use
need_bar(foo2bar(foo));

Whilst
int need_bar(struct bar *br);

Would require
struct bar tempbar;
....
tempbar = foo2bar(foo);
need_bar(&tempbar);
I'd have though that depends on the context and the quality of the
optimiser but there may be some reason I can't see why it is hard to
remove r altogether.

Well, if the compiler can't even be forced to accept foo is the same
as bar, I don't see how it could optimize away the copying of
individual elements and just replace foo with bar. If it could do
that, a simple casting would work.
Have you been addressing a C++ problem all along or are you trying to
write this code in a language that is the intersection of valid C and
valid C++? If so, I am curious about why you need to have this extra
headache.

No. This is C. But my compiler, like most these days, does C++ as
well. (Sadly, "most these days" consider themselves C++ compilers
that can also do C almost as an afterthought because it's easy enough
to implement both, and you might just need to compile some library
that's written in that "moldy old C" to use with your fantastic C++
code <sarcasm directed at some C++ compiler writers, etc., not C>).

When I got the syntax error, I recompiled it in C++ to confirm
something I dimly remembered from the help file: inline is supported
in C++ but not C in the compiler I use.

From the help file:
Syntax
inline <datatype> <class>_<function> (<parameters>) { <statements>; }

Description
Use the inline keyword to declare or define C++ inline functions.
Inline functions are best reserved for small, frequently used
functions.

I had hoped that the omission of C in the first statement under
Description was an error. This confirmed it was not.

DSF
 
J

Jorgen Grahn

....


Not quite. Function calls are not "lvalues" which means (in part) that
you can't take the address of the result. So if you need a bar in some
context where it's address gets taken, you can use the function call.

I have to confess that I (when I suggested the inline function) was
influenced by C++. Const references and stuff like that may make the
function call solution more attractive there than in C.
I'd have though that depends on the context and the quality of the
optimiser but there may be some reason I can't see why it is hard to
remove r altogether.

And I should also have added: look at the generated code to see if
the function call really gets optimized away (if that matters more
than type safety).

And yes, I was assuming C99.

/Jorgen
 
K

Keith Thompson

Ben Bacarisse said:
Optimising usually takes place at a low level after most notions about
types have disappeared. I would have thought the optimiser will simply
see lots of apparently pointless data moves. Of course, if the types
are in fact different in some crucial way, then the moves are needed and
putting the two structs into a union would fail.

At least some optimizers take advantage of language-level information,
including types, to determine what optimizations can be performed.
For example, given:

int i;
long *lp;
/* ... */
i = 42;
*lp = 43;
/* HERE */

a compiler may safely assume that i still has the value 42 at the
point marked "HERE". Without type information, it couldn't make
that assumption.

Some other optimizations do work on lower-level information, for
example replacing machine instruction sequences with sequences that
are equivalent but faster.
 
B

Ben Bacarisse

Keith Thompson said:
At least some optimizers take advantage of language-level information,
including types, to determine what optimizations can be performed.

Yes, quite right. I should have said "Some optimisations take
place...".

<snip>
 
D

DSF

No, there is a comp.std.c group for discussing the standard *itself*,
not for discussing code conforming with the standard.
Understood.


I mostly agree. The problem is that in some cases, you have to know a fair
bit about a platform to know whether something is an error or not.

Consider the UNIX faq:

if (fd = open("foo", O_RDWR) != -1) {
write(fd, "hello!\n", 7);
}

Why does this print hello instead of writing it to a file?

It turns out that you can't easily answer this if you don't know a
fair bit about Unix, but you don't need to know anything about Unix
to guess at the problem.

Basically... I figure people are justified in not trying to answer such
questions, or suggesting that you'll get a better answer in a
platform-specific group. But I also try to answer them when I notice
a generic-C answer.

I understand. I would consider that example off topic because
understanding it depends entirely on knowing how "open" and "write"
operate. I'll give an example of a problem I had that illustrates
what I mean. It has non-standard calls in it, but I believe the
problem can be ascertained in spite of them.

Please note that I'm posting this code only as an illustration of my
view of on topic code with OS specific calls. It has been corrected
since and I did perform a Bull Shannon when I discovered the obvious
fault.

The purpose of the code is to take a MS wide "multistring" (a single
character array with zeros delimiting the individual strings and a
single zero to terminate the multistring), separate it into its
individual strings and return them as an array of non-wide strings. It
returns an error status and the number of strings returned (via
stringcount). It is up to the caller to free the strings and the
array itself. A function is provided for that.

The definitions for the error messages (and DWORD) are not included
here, but I believe their meaning is clear.

DWORD MultistringWToStringArray(const wchar_t *multistring,
char **stringarray, int *stringcount)
{
size_t msi, wsl, asl;
size_t sl;
char **temp;
char *astring;
wchar_t *wstring;
DWORD ret = NO_ERROR;

/* returns the length in characters of the longest string in
multistring */
sl = FindLongestStringLengthInMultistringW(multistring);
if(sl == 0)
return ST_EMPTY_MULTISTRING;

/* allocate temp space for each individual wide string */
wstring = malloc(sl * sizeof(wchar_t));
if(wstring == NULL)
{
return ST_OUT_OF_MEMORY;
}

stringarray = NULL;
*stringcount = 0;
msi = 0;
while(multistring[msi] != 0)
{
/* allocate space in array for next string pointer */
temp = realloc(stringarray, (*stringcount + 1) *
sizeof(stringarray));
if(temp == NULL)
{
FreeStringArray(stringarray, *stringcount);
ret = ST_OUT_OF_MEMORY;
break;
}
else
stringarray = temp;

/* dstrcpylW is a library function of mine that is a combination of
the wide versions of strcpy and strlen. It copies a wide string from,
in this case, multistring+msi to wstring. It returns the length of
the destination string in characters (wstring) eliminating the need to
traverse wstring twice */
wsl = dstrcpylW(wstring, multistring + msi) + 1;
msi += (wsl * sizeof(wchar_t));

/* Windows API call that converts the wide string to an ASCII string.
if the ASCII string pointer (astring) is NULL, it returns the size
needed to hold the ASCII string */
astring = NULL;
asl = WideCharToMultiByte(CP_ACP, 0, wstring, -1, astring, 0,
NULL, NULL);
if(asl == FALSE)
{
/* WideCharToMultiByte failed, get the error code and clean up */
ret = GetLastError();
FreeStringArray(stringarray, *stringcount);
break;
}
/* else, WideCharToMultiByte returns the length of astring, including
ending zero. */
astring = malloc(asl);
if(astring == NULL)
{
FreeStringArray(stringarray, *stringcount);
ret = ST_OUT_OF_MEMORY;
break;
}
/* Windows API call that converts the wide string wstring to an ASCII
string astring */
WideCharToMultiByte(CP_ACP, 0, wstring, -1, astring, asl,
NULL, NULL);
/* allocate space for the string and copy it to the array */
stringarray[*stringcount] = malloc(asl);
strcpy(stringarray[*stringcount], astring);
free(astring);
(*stringcount)++;
msi++;
}
free(wstring);
return ret;
}

void FreeStringArray(char **stringarray, int stringcount)
{
int i;

for(i = 0; i < stringcount; i++)
free(stringarray);
free(stringarray);
}

I won't state the error here just to see if I am correct and it can
be easily determined, even with non-standard code.

DSF
 
S

Seebs

I understand. I would consider that example off topic because
understanding it depends entirely on knowing how "open" and "write"
operate.

I don't think it does. It really depends on knowing that the code sets
fd to either 0 or 1, not to the return value from open(). You can see
that without any knowledge of the system calls in question...
DWORD MultistringWToStringArray(const wchar_t *multistring,
char **stringarray, int *stringcount)
{
size_t msi, wsl, asl;
size_t sl;
char **temp;
char *astring;
wchar_t *wstring;
DWORD ret = NO_ERROR;

/* returns the length in characters of the longest string in
multistring */
sl = FindLongestStringLengthInMultistringW(multistring);
if(sl == 0)
return ST_EMPTY_MULTISTRING;

/* allocate temp space for each individual wide string */
wstring = malloc(sl * sizeof(wchar_t));
if(wstring == NULL)
{
return ST_OUT_OF_MEMORY;
}

stringarray = NULL;
*stringcount = 0;
msi = 0;
while(multistring[msi] != 0)
{
/* allocate space in array for next string pointer */
temp = realloc(stringarray, (*stringcount + 1) *
sizeof(stringarray));

This is probably wrong but also probably works by happy coincidence, but
really this should be sizeof(*stringarray).
/* dstrcpylW is a library function of mine that is a combination of
the wide versions of strcpy and strlen. It copies a wide string from,
in this case, multistring+msi to wstring. It returns the length of
the destination string in characters (wstring) eliminating the need to
traverse wstring twice */
wsl = dstrcpylW(wstring, multistring + msi) + 1;
msi += (wsl * sizeof(wchar_t));

I am distrustful of the "sizeof(wchar_t)", because we're indexing multistring
by this, and multstring is already wchar_t sized.
I won't state the error here just to see if I am correct and it can
be easily determined, even with non-standard code.

Those two were the only bits that stood out.

-s
 
D

DSF

I don't think it does. It really depends on knowing that the code sets
fd to either 0 or 1, not to the return value from open(). You can see
that without any knowledge of the system calls in question...

Yeah. I see the lack of parenthesis in the first line. So I see
how that would be C related. However fd being 0 or 1 would not leap
out at me as "prints hello instead of to a file." I would assume 0 or
1 in the first parameter would produce some sort of error from write.
That part threw me off a bit.

DWORD MultistringWToStringArray(const wchar_t *multistring,
char **stringarray, int *stringcount)
{
size_t msi, wsl, asl;
size_t sl;
char **temp;
char *astring;
wchar_t *wstring;
DWORD ret = NO_ERROR;

/* returns the length in characters of the longest string in
multistring */
sl = FindLongestStringLengthInMultistringW(multistring);
if(sl == 0)
return ST_EMPTY_MULTISTRING;

/* allocate temp space for each individual wide string */
wstring = malloc(sl * sizeof(wchar_t));
if(wstring == NULL)
{
return ST_OUT_OF_MEMORY;
}

stringarray = NULL;
*stringcount = 0;
msi = 0;
while(multistring[msi] != 0)
{
/* allocate space in array for next string pointer */
temp = realloc(stringarray, (*stringcount + 1) *
sizeof(stringarray));

This is probably wrong but also probably works by happy coincidence, but
really this should be sizeof(*stringarray). Close...
/* dstrcpylW is a library function of mine that is a combination of
the wide versions of strcpy and strlen. It copies a wide string from,
in this case, multistring+msi to wstring. It returns the length of
the destination string in characters (wstring) eliminating the need to
traverse wstring twice */
wsl = dstrcpylW(wstring, multistring + msi) + 1;
msi += (wsl * sizeof(wchar_t));

I am distrustful of the "sizeof(wchar_t)", because we're indexing multistring
by this, and multstring is already wchar_t sized.

That's what I get for posting code that I have recently overhauled
but not tested. It should have been msi += wsl; Thanks.

Another mistake in this section was that I added +1 to compensate
for the ending zero in the wsl=dstrcpylW line above and also had msi++
at then end of the loop. Only one is necessary.

(Remember I had to put the error I found *back* into the code for
this posting I don't believe that introduced any new errors, but it
might have.)
 
D

DSF

I have to confess that I (when I suggested the inline function) was
influenced by C++. Const references and stuff like that may make the
function call solution more attractive there than in C.


And I should also have added: look at the generated code to see if
the function call really gets optimized away (if that matters more
than type safety).

I did, in fact, do this. To venture slightly off topic here, some
X86 code generated passing foo2bar(foo) to function dobar(struct bar).
As in:
; struct foo f;
; struct bar b;
;
; union foobar {
; struct foo f;
; struct bar b;
; };
;
; union foobar fb;

Pass inlined foo2bar to dobar:
; g = dobar(foo2bar(f));
;
mov ecx,dword ptr [ebp-92]
mov dword ptr [ebp-116],ecx
mov ecx,dword ptr [ebp-88]
mov dword ptr [ebp-112],ecx
mov eax,dword ptr [ebp-116]
mov dword ptr [ebp-124],eax
lea eax,dword ptr [ebp-132]
mov edx,dword ptr [ebp-112]
mov dword ptr [ebp-120],edx
mov ecx,dword ptr [ebp-124]
mov dword ptr [ebp-132],ecx
mov ecx,dword ptr [ebp-120]
mov dword ptr [ebp-128],ecx
push dword ptr [eax+4]
push dword ptr [eax]
call @dobar$q3bar
add esp,8
mov dword ptr [$abhiflaa],eax

Pass the bar part of union foobar to dobar:
; g = dobar(fb.b);
;
push dword ptr [ebp-104]
push dword ptr [ebp-108]
call @dobar$q3bar
add esp,8
mov dword ptr [$abhiflaa],eax

As you can see, the inlined call produces a *LOT* more code: It
basically just inlines the member copy.
 

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
474,085
Messages
2,570,597
Members
47,220
Latest member
AugustinaJ

Latest Threads

Top