struct init and assign / scope problem

Discussion in 'C Programming' started by Noob, Apr 19, 2011.

  1. Noob

    Noob Guest

    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.
     
    Noob, Apr 19, 2011
    #1
    1. Advertising

  2. Noob

    James Kuyper Guest

    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).
    --
    James Kuyper
     
    James Kuyper, Apr 19, 2011
    #2
    1. Advertising

  3. Noob

    Noob Guest

    James Kuyper wrote:

    > 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).


    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.
     
    Noob, Apr 19, 2011
    #3
  4. On Apr 19, 1:00 pm, Noob <r...@127.0.0.1> wrote:
    >
    > 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.
     
    Malcolm McLean, Apr 19, 2011
    #4
  5. Noob

    Mark Bluemel Guest

    On 04/19/2011 11:00 AM, Noob wrote:
    > 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);
     
    Mark Bluemel, Apr 19, 2011
    #5
  6. Noob

    James Kuyper Guest

    On 04/19/2011 07:18 AM, Noob wrote:
    > James Kuyper wrote:
    >
    >> 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).

    >
    > 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.

    --
    James Kuyper
     
    James Kuyper, Apr 19, 2011
    #6
  7. Noob

    Noob Guest

    Malcolm McLean wrote:

    > On Apr 19, 1:00 pm, Noob wrote:
    >>
    >> 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.


    I don't want to waste the space, and the function may be called
    by different threads.
     
    Noob, Apr 19, 2011
    #7
  8. Noob <root@127.0.0.1> writes:

    > 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.


    <snip>
    > 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.

    --
    Ben.
     
    Ben Bacarisse, Apr 19, 2011
    #8
  9. Noob

    Noob Guest

    Ben Bacarisse wrote:

    > 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 ^_^

    > 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.
     
    Noob, Apr 19, 2011
    #9
  10. Noob

    James Kuyper Guest

    On 04/19/2011 09:11 AM, Noob wrote:
    > Ben Bacarisse wrote:
    >
    >> I'd use a union and set it using a compound literal:
    >>
    >> char *thing;
    >> union { struct s1 s2; struct s2 s2; } foo;

    ....
    >> 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 ??


    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.


    --
    James Kuyper
     
    James Kuyper, Apr 19, 2011
    #10
  11. Noob <root@127.0.0.1> writes:

    > Ben Bacarisse wrote:
    >
    >> 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 *)


    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>
    --
    Ben.
     
    Ben Bacarisse, Apr 19, 2011
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Tony Cheng
    Replies:
    1
    Views:
    8,286
    Juan T. Llibre
    Feb 24, 2006
  2. Replies:
    18
    Views:
    7,168
    Karl Heinz Buchegger
    Jul 22, 2005
  3. Replies:
    1
    Views:
    684
    Jules
    Aug 18, 2005
  4. Jess
    Replies:
    4
    Views:
    463
  5. news.aon.at
    Replies:
    11
    Views:
    666
    Ian Collins
    Jan 29, 2011
Loading...

Share This Page