views:

205

answers:

6

Is there a better way to initialise C structures?

I can use initialiser lists at the variable declaration point; however this isn't that useful if all arguments are not known at compile time, or if I'm not declaring a local/global instance, eg:

Legacy C Code which declares the struct, and also has API's using it

typedef struct
{
    int x, y, z;
} MyStruct;

C++ code using the C library

void doSomething(std::vector<MyStruct> &items)
{
    items.push_back(MyStruct(5,rand()%100,items.size()));//doesnt work because there is no such constructor
    items.push_back({5,rand()%100,items.size()});//not allowed either

    //works, but much more to write...
    MyStruct v;
    v.x = 5;
    v.y = rand()%100;
    v.z = items.size();
    items.push_back(v);
}

Creating local instances and then setting each member one at a time (myStruct.x = 5 etc) is a real pain, and somewhat hard to read when trying to add say 20 different items to the container...

+4  A: 

You're looking for C99 compound literals. Example code:

struct foo *foo = malloc(sizeof *foo);
*foo = (struct foo){ bar, baz };
Christoph
Sounds like a great solution, if it was supported by the compiler (VC++9) :(
Fire Lancer
this time, it's actually not the compiler's fault as compound literals are a C99 feature unsupported by C++; I misread your question and thought you were looking for a solution in C...
Christoph
see http://stackoverflow.com/questions/1705147/struct-initialization-of-the-c-c-programming-language/1705166#1705166 for some explanations and the C++0x syntax
Christoph
Complain to MS that their C compiler is archaic because it is only supporting a twenty year old standard, not the ten year old replacement standard.
Jonathan Leffler
+2  A: 

How about:

MyStruct v = {5, rand()%100, items.size()};
items.push_back(v);
Autopulated
So to do multiple things ill have to do v1,v2,v3,v4 etc, no way to reuse the one local?
Fire Lancer
If you have v1, v2, v3, and v4, you are stuck with writing something out 4 times - unless you want them to have the same value, in which case you can do simple assignments, of course. If you had v[0], v[1], v[2], v[3] and v[4], then you could use a loop instead.
Jonathan Leffler
I'm not 100% sure about this, but I think:MyStruct vs[2] = {{1, 2, 3}, {2, 3, 4}}; will work
Autopulated
+2  A: 

Not clear what you are asking. In C++, the obvious solution is to give the struct a constructor:

struct MyStruct {
  int x, y, z;
  MyStruct( int ax, int ay, int az ) : x( ax ), y( ay ), z( az ) {}
};
anon
he's looking for a solution in C
Christoph
It is tagged c++ though
Autopulated
Then why's he tagged it C++? This is why I said I wasn't clear.
anon
My guess is that he has headers which have to stay in C (legacy code shared between projects) but he want a convenient way of using the struct in C++.
AProgrammer
The thing is its a struct in some legacy C code, so unless there is some way to retrospectively add the constructor (i.e. without breaking compatibility with the C library the struct is for, and ideally without editing the declaration in the c headers themselves)?
Fire Lancer
+7  A: 

If you can't add a constructor (which is the best solution in C++03 but you probably have compatibility constraint with C), you can write a function with the same effect:

MyStruct makeAMyStruct(int x, int y, int z)
{
    MyStruct result = { x, y, z };
    return result;
}

items.push_back(makeAMyStruct(5,rand()%100,items.size()));

Edit: I'd have checked now that C++0X offers something for this precise problem:

items.push_back(MyStruct{5,rand()%100,items.size()});

which is available in g++ 4.4.

AProgrammer
+1: I think that's the answer he's looking for
Christoph
btw: why not use an initialization list?
Christoph
No good reason, I'll change my function.
AProgrammer
"...C++0X offers something..." does this feature have a name that I could use to search for more info?
Fire Lancer
It's part of the uniform initialization syntax.
AProgrammer
+2  A: 

Create a function to initialize it, similar to what a C++ constructor would do.

nos
(+1) KISS answer from all perspectives.
Hassan Syed
A: 

Another option is to derive from the struct and add a constructor there.

struct MyDerivedStruct : public MyStruct
{
    MyDerivedStruct(int xi, int yi, int zi)
    {
        x = xi;
        y = yi;
        z = zi;
    }
}

Then you can use this derived type in your own code and pass it to the C library when necessary. The language should take care of implicitly converting to MyStruct when appropriate.

As a bonus, you could also add other useful member functions, perhaps even wrapping many of the legacy C functions that use this type.

TheUndeadFish