I'm trying to find a convenient way to initialise 'pod' C++ structs. Now, consider the following struct:
struct FooBar { int foo; float bar; }; // just to make all examples work in C and C++: typedef struct FooBar FooBar;
If I want to conveniently initialise this in C (!), I could simply write:
/* A */ FooBar fb = { .foo = 12, .bar = 3.4 }; // illegal C++, legal C
Note that I want to explicitly avoid the following notation, because it strikes me as being made to break my neck if I change anything in the struct in the future:
/* B */ FooBar fb = { 12, 3.4 }; // legal C++, legal C, bad style?
To achieve the same (or at least similar) in C++ as in the /* A */
example, I would have to implement an idiotic constructor:
FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar) {} // -> /* C */ FooBar fb(12, 3.4);
Which is good for boiling water, but not suitable for lazy people (laziness is a good thing, right?). Also, it is pretty much as bad as the /* B */
example, as it does not explicitly state which value goes to which member.
So, my question is basically how I can achieve something similar to /* A */
or better in C++? Alternatively, I would be okay with an explanation why I should not want to do this (i.e. why my mental paradigm is bad).
EDIT
By convenient, I mean also maintainable and non-redundant.
-------------Problems Reply------------
Since style A
is not allowed in C++ and you don't want style B
then how about using style BX
:
FooBar fb = { /*.foo=*/ 12, /*.bar=*/ 3.4 }; // :)
At least help at some extent.
Option D:
FooBar FooBarMake(int foo, float bar)
Legal C, legal C++. Easily optimizable for PODs. Of course there are no named arguments, but this is like all C++. If you want named arguments, Objective C should be better choice.
Option E:
FooBar fb;
memset(&fb, 0, sizeof(FooBar));
fb.foo = 4;
fb.bar = 15.5f;
Legal C, legal C++. Named arguments.
Your question is somewhat difficult because even the function:
static FooBar MakeFooBar(int foo, float bar);
may be called as:
FooBar fb = MakeFooBar(3.4, 5);
because of the promotion and conversions rules for built-in numeric types. (C has never been really strongly typed)
In C++, what you want is achievable, though with the help of templates and static assertions:
template <typename Integer, typename Real>
FooBar MakeFooBar(Integer foo, Real bar) {
static_assert(std::is_same<Integer, int>::value, "foo should be of type int");
static_assert(std::is_same<Real, float>::value, "bar should be of type float");
return { foo, bar };
}
In C, you may name the parameters, but you'll never get further.
On the other hand, if all you want is named parameters, then you write a lot of cumbersome code:
struct FooBarMaker {
FooBarMaker(int f): _f(f) {}
FooBar Bar(float b) const { return FooBar(_f, b); }
int _f;
};
static FooBarMaker Foo(int f) { return FooBarMaker(f); }
// Usage
FooBar fb = Foo(5).Bar(3.4);
And you can pepper in type promotion protection if you like.
Yet another way in C++ is
struct Point
{
private:
int x;
int y;
public:
Point& setX(int xIn) { x = Xin; return *this;}
Point& setY(int yIn) { y = Yin; return *this;}
}
Point pt;
pt.setX(20).setY(20);
If you can, use GCC. Its C++ front.end understands C initializer syntax.
The way /* B */
is fine in C++ also the C++0x is going to extend the syntax so it is useful for C++ containers too. I do not understand why you call it bad style?
If you want to indicate parameters with names then you can use boost parameter library, but it may confuse someone unfamiliar with it.
Reordering struct members is like reordering function parameters, such refactoring may cause problems if you don't do it very carefully.
Extract the contants into functions that describe them (basic refactoring):
FooBar fb = { foo(), bar() };
I know that style is very close to the one you didn't want to use, but it enables easier replacement of the constant values and also explain them (thus not needing to edit comments), if they ever change that is.
Another thing you could do (since you are lazy) is to make the constructor inline, so you don't have to type as much (removing "Foobar::" and time spent switching between h and cpp file):
struct FooBar {
FooBar(int f, float b) : foo(f), bar(b) {}
int foo;
float bar;
};