struct init and assign / scope problem

N

Noob

Hello,

I have code that looks like this at the moment:

extern int do_it(char *name, char *thing, void *param);
struct s1;
struct s2;
int err;

if (some_condition)
{
struct s1 foo1 = { a, b, c };
err = do_it(name, "XYZ", &foo1);
}
else
{
struct s2 foo2 = { x, y, z, t, q, r, s };
err = do_it(name, "QSDF", &foo2);
}
if (err) do_something(err);

I don't have the source code for do_it (it's a library function).
I know that it looks at "thing" to decide how to use "param".
(Probably some form of strcmp).

I'd like to move the call to do_it after the if/else blocks,
to avoid duplicating code.

I know I can't write

char *thing;
void *param;
if (some_condition)
{
struct s1 foo1 = { a, b, c };
thing = "XYZ";
param = &foo1;
}
else
{
struct s2 foo2 = { x, y, z, t, q, r, s };
thing = "QSDF";
param = &foo2;
}
err = do_it(name, thing, param);
if (err) do_something(err);

because foo1 and foo2 don't "exist" outside their respective blocks.
(i.e. referring to them after they come out of scope has UB.)

I could init foo1 and foo2 BEFORE the test.

char *thing;
void *param;
struct s1 foo1 = { a, b, c };
struct s2 foo2 = { x, y, z, t, q, r, s };
if (some_condition)
{
thing = "XYZ";
param = &foo1;
}
else
{
thing = "QSDF";
param = &foo2;
}
err = do_it(name, thing, param);
if (err) do_something(err);

but one of the init would be just wasting cycles.

I'd want to be able to write something like

char *thing;
void *param;
struct s1 foo1;
struct s1 foo2;
if (some_condition)
{
foo1 = { a, b, c };
thing = "XYZ";
param = &foo1;
}
else
{
foo2 = { x, y, z, t, q, r, s };
thing = "QSDF";
param = &foo2;
}
err = do_it(name, thing, param);
if (err) do_something(err);

but it's not possible, is it?

Is there a nice solution?

Regards.
 
J

James Kuyper

On 04/19/2011 06:00 AM, Noob wrote:
....
extern int do_it(char *name, char *thing, void *param); ....
I'd want to be able to write something like

char *thing;
void *param;
struct s1 foo1;
struct s1 foo2;
if (some_condition)
{
foo1 = { a, b, c };
thing = "XYZ";
param = &foo1;
}
else
{
foo2 = { x, y, z, t, q, r, s };
thing = "QSDF";
param = &foo2;
}
err = do_it(name, thing, param);
if (err) do_something(err);

but it's not possible, is it?

Why not? If combined with suitable definitions for every identifier used
above that is not defined, and placed inside a function, I don't see a
problem (though I might have missed something).
 
N

Noob

James said:
On 04/19/2011 06:00 AM, Noob wrote:
...

Why not? If combined with suitable definitions for every identifier used
above that is not defined, and placed inside a function, I don't see a
problem (though I might have missed something).

The assignment to foo1 and foo2 is problematic.

$ cat mini.c
int do_it(char *thing, void *param);
void do_something(int err);
struct s1 { int a, b, c, d, e; };
struct s2 { double x, y, z; };

void gogo(int some_condition)
{
int err;
char *thing;
void *param;
struct s1 foo1;
struct s2 foo2;
if (some_condition)
{
foo1 = { 1, 2, 3 }; /*** PROBLEM HERE ***/
thing = "XYZ";
param = &foo1;
}
else
{
foo2 = { 4.5, 8.25 }; /*** AND HERE ***/
thing = "QSDF";
param = &foo2;
}
err = do_it(thing, param);
if (err) do_something(err);
}

$ gcc -Wall -Wextra -c mini.c
mini.c: In function 'gogo':
mini.c:15: error: expected expression before '{' token
mini.c:21: error: expected expression before '{' token

You were perhaps suggesting that I write the following?

if (some_condition)
{
struct s1 temp = { 1, 2, 3 };
foo1 = temp;
thing = "XYZ";
param = &foo1;
}
else
{
struct s2 temp = { 4.5, 8.25 };
foo2 = temp;
thing = "QSDF";
param = &foo2;
}

I suppose it boils down to QoI to optimize temp away,
and write the contents directly to the correct struct.

Regards.
 
M

Malcolm McLean

Is there a nice solution?
There's a nasty solution, which is to make the structs static. This
will work for most programs, but you need to be careful if running in
a threaded environment or if the function is recursive.
 
M

Mark Bluemel

Hello,

I have code that looks like this at the moment:

extern int do_it(char *name, char *thing, void *param);
struct s1;
struct s2;
int err;

if (some_condition)
{
struct s1 foo1 = { a, b, c };
err = do_it(name, "XYZ",&foo1);
}
else
{
struct s2 foo2 = { x, y, z, t, q, r, s };
err = do_it(name, "QSDF",&foo2);
}
if (err) do_something(err);

I don't have the source code for do_it (it's a library function).
I know that it looks at "thing" to decide how to use "param".
(Probably some form of strcmp).

I'd like to move the call to do_it after the if/else blocks,
to avoid duplicating code.

Why? It's not really duplication is it?

If you want it to be more expressive of the differences, you could try
something like this (untested...):-

extern int do_it(char *name, char *thing, void *param);
#define DO_XYZ(name, param) do_it(name,"XYZ",param)
#define DO_OSDF(name,param) do_it(name,"OSDF",param)
struct s1;
struct s2;
int err;

if (some_condition)
{
struct s1 foo1 = { a, b, c };
err = DO_XYZ(name, &foo1);
}
else
{
struct s2 foo2 = { x, y, z, t, q, r, s };
err = DO_OSDF(name, &foo2);
}
if (err) do_something(err);
 
J

James Kuyper

The assignment to foo1 and foo2 is problematic.

Sorry - you're right, I wasn't awake enough. In your original code those
weren't assignments, they were initializations. In this modified
version, they are neither - they're just syntax errors. However, they
can be replaced with member-wise assignments:

foo1.member1 = a;
foo2.member2 = b;
You were perhaps suggesting that I write the following?

if (some_condition)
{
struct s1 temp = { 1, 2, 3 };
foo1 = temp;
thing = "XYZ";
param = &foo1;
}

If I'd been adequately awake, that would have been one of my three
suggestions. The third one would have made use of a new C99 feature
called a compound literal. The following code has essentially the same
meaning as the above code, but is slightly simpler:

if(some_condition)
{
foo1 = (struct s1){a, b, c};
thing = "XYZ";
param = &foo1;
}

Compilers which support this C99 feature are becoming more and more
common, but it's far from being universally supported.

....
I suppose it boils down to QoI to optimize temp away,
and write the contents directly to the correct struct.

I think it's reasonable to expect that a decently optimizing compiler
will generate the exact same code, whether you do member-wise
assignment, a temporary struct, or a compound literal.
 
N

Noob

Malcolm said:
There's a nasty solution, which is to make the structs static. This
will work for most programs, but you need to be careful if running in
a threaded environment or if the function is recursive.

I don't want to waste the space, and the function may be called
by different threads.
 
B

Ben Bacarisse

Noob said:
I have code that looks like this at the moment:

extern int do_it(char *name, char *thing, void *param);
struct s1;
struct s2;
int err;

if (some_condition)
{
struct s1 foo1 = { a, b, c };
err = do_it(name, "XYZ", &foo1);
}
else
{
struct s2 foo2 = { x, y, z, t, q, r, s };
err = do_it(name, "QSDF", &foo2);
}
if (err) do_something(err);

I don't have the source code for do_it (it's a library function).
I know that it looks at "thing" to decide how to use "param".
(Probably some form of strcmp).

I'd like to move the call to do_it after the if/else blocks,
to avoid duplicating code.

I'd want to be able to write something like

char *thing;
void *param;
struct s1 foo1;
struct s1 foo2;
if (some_condition)
{
foo1 = { a, b, c };
thing = "XYZ";
param = &foo1;
}
else
{
foo2 = { x, y, z, t, q, r, s };
thing = "QSDF";
param = &foo2;
}
err = do_it(name, thing, param);
if (err) do_something(err);

but it's not possible, is it?

Is there a nice solution?

I'd use a union and set it using a compound literal:

char *thing;
union { struct s1 s2; struct s2 s2; } foo;
if (some_condition)
{
foo.s1 = (struct s1){ a, b, c };
thing = "XYZ";
}
else
{
foo.s2 = (struct s2){ x, y, z, t, q, r, s };
thing = "QSDF";
}
err = do_it(name, thing, &foo);
if (err) do_something(err);

Trivially it saves space and uses fewer variables but the main advantage
is that I think it's clearer: the union announces, up front, that foo
is either one struct or the other.
 
N

Noob

Ben said:
I'd use a union and set it using a compound literal:

char *thing;
union { struct s1 s2; struct s2 s2; } foo;
if (some_condition)
{
foo.s1 = (struct s1){ a, b, c };
thing = "XYZ";
}
else
{
foo.s2 = (struct s2){ x, y, z, t, q, r, s };
thing = "QSDF";
}
err = do_it(name, thing, &foo);

Is it OK to provide a (union X *) as the 3rd parameter, even if
do_it( ) expects either a (struct s1 *) or a (struct s2 *) and
will thus do something along the lines of

struct s1 *pp = param3;

where param 3 really points to a union X ??

BTW, I like the compound literal solution ^_^
 
J

James Kuyper

Is it OK to provide a (union X *) as the 3rd parameter, even if
do_it( ) expects either a (struct s1 *) or a (struct s2 *) and
will thus do something along the lines of

struct s1 *pp = param3;

where param 3 really points to a union X ??

Yes. &foo, &foo.s1 and &foo.s2 all point at the same location in memory,
the first byte of foo. The conversion should work fine.
 
B

Ben Bacarisse

Noob said:
Is it OK to provide a (union X *) as the 3rd parameter, even if
do_it( ) expects either a (struct s1 *) or a (struct s2 *)

Yes. I could add (sarcastically) that that is why I suggested it, but I
make too many mistakes to get away with that style!
and
will thus do something along the lines of

struct s1 *pp = param3;

where param 3 really points to a union X ??

Yes. If that's what happens inside do_it, then it will work.

However... you can't access the struct s1 though pp safely unless
it was a struct s1 that was put there. The access itself is not a
problem but the data you find might be garbage or even some sort of trap
representation. This means that the other parameters of do_it must be
such that do_it can tell what it should be looking for via its third
parameter. Of course, this applies to all the solutions including the
current code, but your example is so generalised that it's hard to see
how it can do this. You say the do_it is some sort of compare. If is
the third parameter that determines some property of this compare, then
you need to know one other thing: when a union contains structures that
share a common prefix, that prefix can be safely access via any member.
I mention this simply because I have a gut feeling that it may apply to
your real code.

<snip>
 

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

Similar Threads


Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top