Similar to how the posix API works with struct arguments for extensibility reasons, in C++ you can pass structs by reference with named arguments and init them in place, like:
void dothing(const dothingArgs& args);
...
dothing({ .arg1 = 1, .arg2 = someOtherThing });
Register colouring should make it run the same as regular args for the most part in optimized builds.
This has two benefits:
1) Changing the order of the struct args won't introduce ordering bugs like with function signatures.
2) Default arguments are more explicit and there's less chance of breakage as the argument list grows over time and gets more default values, as tends to happen.
And 2 drawbacks:
1) Passing complex types as arguments without copying can be tricky.
2) The compiler errors don't tell you specifically which arguments are broken, at least in g++. This one sucks the most imo.
FYI - designated initializers in C++20 require that the designators appear in the same order as the field declarations in the struct or class[1]. If the designators aren't in the same order as they appear in the struct declaration, the behavior of the designated initializer statement is undefined. Note that this is different from C - C99 supports a broader variety of designated initializers than C++20 - there's a table in the document I linked.
Because of this, the statement "Changing the order of the struct args won't introduce ordering bugs like with function signatures" is actually not true - I know from experience that clang will sometimes just silently leave the struct uninitialized, which could lead to nasty bugs. The behavior can also vary between optimized and debug builds, so it might not surface when you run your unit tests, etc.
I believe that once C++20 is standardized both compilers will probably introduce warnings or errors to let you know when you've unintentionally initialized fields out of order, but for right now the feature is a landmine and I wouldn't recommend using it.
g++ gives an error if you do things out of order. But what I meant is that this is still better than a regular function signature.
ie: int f(int a, int b) changed to f(int b, int a) works but the arguments will be wrong if the call site doesn't change.
f({ .a = 1, .b = 2}) will fail at the call site when the struct is changed to expect f({ .b = 2, .a = 1})
But yes, while ideally the order of declaration wouldn't matter, C++ is going the other way, being extremely strict about initialization order to avoid complex constructor errors. I wish POD types would be treated differently for those warnings/errors.
Makes sense - I misunderstood what you meant by order. It’s good to know that g++ gives you an error if the designator are out of order - thanks for pointing that out.
Forgive what may be a dumb question (I'm not a C++ user), but why should dothing() accept a reference to struct? Why is this better than just making it accept a struct like
void dothing(const dothingArgs args);
and then just passing it an anonymous struct that only briefly sits on the stack when you call
dothing({ .arg1 = 1, .arg2 = someOtherThing });
A reference would make sense if you already have a struct sitting somewhere else in your program, but if you're putting together the struct at the time you call the function, wouldn't it make more sense to just pass by value?
Because otherwise the struct will be copied when the function is called.
Though your question is kind of getting at r-values and move semantics, which is a solution to prevent copying the temp value both when calling the function and in its execution. You can read about it because it will take too long to explain it here.
The C99 designated initialization (where you give member names in the initialization) AFAIK only works in clang when compiled in C++ mode, gcc and MSVC doesn't appear to support it.
C++20 will get a fairly limited subset of the C99's designated init though.
Note that what's been added is a subset of the C99 behavior - there are a bunch of things that work in C99, but are not allowed (and might introduce undefined behavior) in C++20. See the "Design Decisions" section of this document for a summary of differences: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p032...
void dothing(const dothingArgs& args);
...
dothing({ .arg1 = 1, .arg2 = someOtherThing });
Register colouring should make it run the same as regular args for the most part in optimized builds.
This has two benefits:
1) Changing the order of the struct args won't introduce ordering bugs like with function signatures.
2) Default arguments are more explicit and there's less chance of breakage as the argument list grows over time and gets more default values, as tends to happen.
And 2 drawbacks:
1) Passing complex types as arguments without copying can be tricky.
2) The compiler errors don't tell you specifically which arguments are broken, at least in g++. This one sucks the most imo.