Skip to content

Instantly share code, notes, and snippets.

@kentonv
Created November 19, 2013 22:35
Show Gist options
  • Save kentonv/7553792 to your computer and use it in GitHub Desktop.
Save kentonv/7553792 to your computer and use it in GitHub Desktop.
Keyword args in C++?
// 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.)
}
@galo2099
Copy link

@kentonv
Copy link
Author

kentonv commented Oct 16, 2014

http://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html

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