We try to compensate for lack on named arguments in so many ways. Check one the lastest takes on a subject: https://www.youtube.com/watch?v=Grveezn0zhUCppCon 2018: Richard Powell “Named Arguments from Scratch”. The recent C++20 "designated initialization" doesn't help much, as you must remember the order of fields as declared in a corresponding struct. Other people try things around it, as well, such as calling functions using curly syntax: https://www.youtube.com/watch?v=L8eeDzTWEtU, or allowing default parameters in the middle of args list https://github.com/joboccara/Defaulted. So, there's a lot, and more: https://www.codeproject.com/Articles/1171605/Named-Cpp-Function-Parameters , https://www.fluentcpp.com/2018/12/14/named-arguments-cpp/ etc.
But let's pull back. What if:
Let's go further and beautify it with a macro:
And, lastly — beautify it even more, getting rid of a strange semicolon.
The other drawback is not so easy refactoring. You can't simply say «rename foo» clicking on foo inside the macro usage.
But let's pull back. What if:
struct foo_args_t { foo_args_t& a(int v) { args.a = v; return *this; } foo_args_t& b(int v) { args.b = v; return *this; } foo_args_t& c(int v) { args.c = v; return *this; } int a() const { return args.a; } int b() const { return args.b; } int c() const { return args.c; } private: struct { int a = 1; int b = 2; int c = 3; } args; }; int foo(foo_args_t args) { return args.a() + args.b() + args.c(); } int main() { foo_args_t args; args.c(44); args.b(43); args.a(42); return foo(args); // return foo(foo_args_t{}.c(44).b(43).a(42)); }This returns 129 as expected. As an additional benefit, you could have a default value for an argument set with some non-constexpr function. Drawback is a ceremony around making up properties getter and setter. In fact, the only reason we need properties there is to make the last commented line possible. Let's stratify this to a regular struct then.
struct foo_args_t { int a = 1; int b = 2; int c = 3; }; int foo(foo_args_t _) { return _.a + _.b + _.c; } int main() { foo_args_t args; args.c = 44; args.b = 43; args.a = 42; return foo(args); }Quite leaner. To add, this is possible in C++20:
struct foo_args_t { int a = 11; int b = 43; int c = 33; }; int foo(foo_args_t _) { return _.a + _.b + _.c; } int main() { return foo({.a = 44, .c = 42}); }Forget for a moment you must remember the order of parameters. At least, you can 1) skip arguments leaving them default even in the middle of the list, and 2) get a compiler error if you mess up the argument order. So, now that would be the compiler preventing you from messing up x and y in bar(y,x) call for void bar(float x, float y). Not bad.
Let's go further and beautify it with a macro:
#define FUNC(R,N,Args) \ struct N##_args_t {Args;}; \ R N(N##_args_t _ = {}) FUNC(int, foo, int a = 11; int b = 43; int c = 33) { return _.a + _.b + _.c; } int main() { int u = foo(); return u + foo({.a = 44, .c = 42}); }No syntactic overhead at all.
And, lastly — beautify it even more, getting rid of a strange semicolon.
#define EXPAND(x) x #define FOR_EACH_1(x) x #define FOR_EACH_2(x, y) x; y #define FOR_EACH_3(x, y, z) x; y; z #define FOR_EACH_4(x, y, z, u) x; y; z; u #define FOR_EACH_5(x, y, z, u, v) x; y; z; u; v #define FOR_EACH_6(x, y, z, u, v, w) x; y; z; u; v; w #define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N()) #define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__)) #define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N #define FOR_EACH_RSEQ_N() 6, 5, 4, 3, 2, 1, 0 #define CONCATENATE(x,y) x##y #define FOR_EACH_(N, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(__VA_ARGS__)) #define FOR_EACH(...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), __VA_ARGS__) #define FUNC(R,N,...) \ struct N##_args_t {FOR_EACH(__VA_ARGS__);}; \ R N(N##_args_t && _ = {}) FUNC(int, foo, int a = 11, int b = 43, int c = 33) { return _.a + _.b + _.c; } int main() { int u = foo(); return u + foo({.a = 44, .c = 42}); }If this macro is worth it or not – up for you to decide.
The other drawback is not so easy refactoring. You can't simply say «rename foo» clicking on foo inside the macro usage.
No comments:
Post a Comment