how to assign multiple values into a struct at once?

C++CStruct

C++ Problem Overview


I can do this on initialization for a struct Foo:

Foo foo =  {bunch, of, things, initialized};

but, I can't do this:

Foo foo;
foo = {bunch, of, things, initialized};

So, two questions:

  1. Why can't I do the latter, is the former a special constructor for initialization only?

  2. How can I do something similar to the second example, i.e. declare a bunch of variables for a struct in a single line of code after it's already been initialized? I'm trying to avoid having to do this for large structs with many variables:

     Foo foo;
    
     foo.a = 1;
     foo.b = 2;
     foo.c = 3;
     //... ad infinitum
    

C++ Solutions


Solution 1 - C++

Try this:

Foo foo;
foo = (Foo){bunch, of, things, initialized};

This will work if you have a good compiler (e.g. GCC). You might need to turn on C99 mode with --std=gnu99, I'm not sure.

Update: In modern versions of C (but not C++), you can also use a compound literal with designated initializers, which looks like this:

foo = (Foo){ .bunch = 4, .of = 2, .things = 77, .initialized = 8 };

The name right after the "." should be the name of the structure member you wish to initialize. These initializers can appear in any order, and any member that is not specified explicitly will get initialized to zero.

Solution 2 - C++

The first is an aggregate initializer - you can read up on those and tagged initializers at this solution:

https://stackoverflow.com/questions/3016107/what-is-tagged-structure-initialization-syntax

It is a special initialization syntax, and you can't do something similar after initialization of your struct. What you can do is provide a member (or non-member) function to take your series of values as parameters which you then assign within the member function - that would allow you to accomplish this after the structure is initialized in a way that is equally concise (after you've written the function the first time of course!)

Solution 3 - C++

In C++11 you can perform multiple assignment with "tie" (declared in the tuple header)

struct foo {
    int a, b, c;
} f;

std::tie(f.a, f.b, f.c) = std::make_tuple(1, 2, 3);

If your right hand expression is of fixed size and you only need to get some of the elements, you can use the ignore placeholder with tie

std::tie(std::ignore, f.b, std::ignore) = some_tuple; // only f.b modified

If you find the syntax std::tie(f.a, f.b, f.c) too code cluttering you could have a member function returning that tuple of references

struct foo {
    int a, b, c;
    auto members() -> decltype(std::tie(a, b, c)) {
        return std::tie(a, b, c);
    }
} f;

f.members() = std::make_tuple(1, 2, 3);

All this ofcourse assuming that overloading the assignment operator is not an option because your struct is not constructible by such sequence of values, in which case you could say

f = foo(1, 2, 3);

Solution 4 - C++

Memory Footprint - Here is an interesting i386 addition.

After much hassle, using optimization and memcpy seems to generate the smallest footprint using i386 with GCC and C99. I am using -O3 here. stdlib seems to have all sorts of fun compiler optimizations at hand, and this example makes use of that (memcpy is actually compiled out here).

Do this by:

Foo foo; //some global variable

void setStructVal (void)   {

    const Foo FOO_ASSIGN_VAL = {    //this goes into .rodata
            .bunch       = 1,
            .of          = 2,
            .things      = 3,
            .initialized = 4
    };

    memcpy((void*) &FOO_ASSIGN_VAL, (void*) foo, sizeof(Foo));

    return;
}

Result:

  • (.rodata) FOO_ASSIGN_VAL is stored in .rodata
  • (.text) a sequence of movl FOO_ASSIGN_VAL, %registers occur
  • (.text) a sequence of movl %registers, foo occur

Example:

  • Say Foo was a 48 field struct of uint8_t values. It is aligned in memory.

  • (IDEAL) On a 32-bit machine, this COULD be as quick as 12 MOVL instructions of immediates out to foo's address space. For me this is 12*10 == 120bytes of .text in size.

  • (ACTUAL) However, using the answer by AUTO will likely generate 48 MOVB instructions in .text. For me this is 48*7 == 336bytes of .text!!

  • (SMALLEST*) Use the memcpy version above. IF alignment is taken care of,

    • FOO_ASSIGN_VAL is placed in .rodata (48 bytes),
    • 12 MOVL into %register
    • 12 MOVL outof %registers are used in .text (24*10) == 240bytes.
    • For me then this is a total of 288 bytes.

So, for me at least with my i386 code,

- Ideal:    120 bytes
- Direct:   336 bytes
- Smallest: 288 bytes

*Smallest here means 'smallest footprint I know of'. It also executes faster than the above methods (24 instructions vs 48). Of course, the IDEAL version is fastest & smallest, but I still can't figure that out.

-Justin

*Does anyone know how to get implementation of 'IDEAL' above? It is annoying the hell out of me!!

Solution 5 - C++

If you don't care too much about efficiency, you could double assign: i.e. create a new instance of the structure using aggregate initialization, and then copy it over:

struct Foo foo;

{ struct Foo tmp = {bunch, of, things, initialized}; foo = tmp; }

Make sure you keep the portion wrapped in {}s so as to discard the unnecessary temporary variable as soon as it's no longer necessary.

Note this isn't as efficient as making, e.g., a 'set' function in the struct (if c++) or out of the struct, accepting a struct pointer (if C). But if you need a quick, preferably temporary, alternative to writing element-by-element assignment, this might do.

Solution 6 - C++

If you care about efficiency, you can define a union of the same length as your structure, with a type you can assign at once.

To assign values by elements use the struct of your union, to assign the whole data, use the other type of your union.

typedef union
{
    struct
    {
      char a;
      char b;
    } Foo;
    unsigned int whole;
} MyUnion;

MyUnion _Union;
_Union.Foo.a = 0x23;    // assign by element
_Union.Foo.b = 0x45;    // assign by element
_Union.whole = 0x6789;  // assign at once

Be carefull about your memory organization (is "a" the MSB or the LSB of "whole"?).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjimView Question on Stackoverflow
Solution 1 - C++David GraysonView Answer on Stackoverflow
Solution 2 - C++John HumphreysView Answer on Stackoverflow
Solution 3 - C++Nikos AthanasiouView Answer on Stackoverflow
Solution 4 - C++J-DizzleView Answer on Stackoverflow
Solution 5 - C++CodesmithView Answer on Stackoverflow
Solution 6 - C++SimonView Answer on Stackoverflow