Created
November 19, 2013 22:35
-
-
Save kentonv/7553792 to your computer and use it in GitHub Desktop.
Keyword args in C++?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Keyword args. In C++. | |
// | |
// Compile with: | |
// g++ -std=c++11 kwargs.c++ | |
#include <utility> | |
#include <assert.h> | |
#include <string.h> | |
#include <vector> | |
#include <initializer_list> | |
#include <iostream> | |
// ============================================================================ | |
// Framework code -- skip down to see usage example | |
template <typename Name, typename T> | |
class KeywordAssign { | |
public: | |
KeywordAssign(T&& value): value(std::forward<T>(value)) {} | |
T&& get() { return std::forward<T>(value); } | |
private: | |
T&& value; | |
}; | |
template <typename Name, typename T> | |
class KeywordAssignArray { | |
public: | |
KeywordAssignArray(std::initializer_list<T>&& value) | |
: begin_(value.begin()), end_(value.end()) {} | |
const T* begin() const { return begin_; } | |
const T* end() const { return end_; } | |
private: | |
const T* begin_; | |
const T* end_; | |
}; | |
template <typename Name> | |
class KeywordArg { | |
public: | |
template <typename T> | |
inline constexpr KeywordAssign<Name, T> operator=(T&& value) { | |
return { std::forward<T>(value) }; | |
} | |
template <typename T> | |
inline constexpr KeywordAssignArray<Name, T> operator=( | |
std::initializer_list<T> value) { | |
return { std::move(value) }; | |
} | |
}; | |
template <typename... Args> | |
inline void noop(Args... args) {} | |
#define KWARG(NAME) \ | |
constexpr KeywordArg<struct $##NAME> $##NAME = KeywordArg<struct $##NAME>(); | |
#define KWSTRUCT_CTOR(NAME) \ | |
template <typename... KwArgs> \ | |
NAME(KwArgs... kwArgs) { \ | |
noop(applyKwArg(std::move(kwArgs))...); \ | |
} | |
#define KWSTRUCT_FIELD(NAME) \ | |
template <typename T> \ | |
int applyKwArg(KeywordAssign<struct $##NAME, T>&& val) { \ | |
NAME = val.get(); \ | |
return 0; \ | |
} | |
#define KWSTRUCT_ITERABLE_FIELD(NAME) \ | |
template <typename T> \ | |
int applyKwArg(KeywordAssignArray<struct $##NAME, T>&& val) { \ | |
NAME.insert(NAME.end(), val.begin(), val.end()); \ | |
return 0; \ | |
} | |
// ============================================================================ | |
// Application code | |
// Define the available arg names. These can be shared between different usage | |
// sites, though you'll need some scheme to avoid double-declaring (perhaps | |
// #ifdefs, or put all your arg names in a common header). | |
KWARG(foo); | |
KWARG(bar); | |
KWARG(baz); | |
// We'll want to declare our arg set as a struct. | |
struct MyArgs { | |
KWSTRUCT_CTOR(MyArgs); | |
int foo = 0; | |
const char* bar = ""; | |
std::vector<int> baz; | |
private: | |
KWSTRUCT_FIELD(foo); | |
KWSTRUCT_FIELD(bar); | |
KWSTRUCT_ITERABLE_FIELD(baz); | |
}; | |
// Now we can declare a function. | |
void myFunc(MyArgs args) { | |
std::cout << "{" << std::endl; | |
std::cout << " foo = " << args.foo << std::endl; | |
std::cout << " bar = " << args.bar << std::endl; | |
std::cout << " baz = { "; | |
for (int i: args.baz) { std::cout << i << ", "; } | |
std::cout << "}" << std::endl; | |
std::cout << "}" << std::endl; | |
} | |
int main() { | |
// Look, I'm calling a function. With keyword args. And type safety. | |
myFunc({$bar = "hello", $foo = 123, $baz = {12, 34, 56}}); | |
myFunc({$bar = "abcd"}); | |
// (The names are prefixed with $ to avoid name conflicts with your other | |
// stuff. Most compilers accept $ in symbol names. Please do not argue | |
// with me about whether this is acceptable; it's beside the point here.) | |
} |
Yes, C99 designated initializers are great... but not supported in C++.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
http://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html