Skip to content

Instantly share code, notes, and snippets.

@jplatte
Last active February 3, 2016 15:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jplatte/695ea4c489c60621e7f4 to your computer and use it in GitHub Desktop.
Save jplatte/695ea4c489c60621e7f4 to your computer and use it in GitHub Desktop.
Named parameters with C++14 (mandatory ones only, to keep the code short)
// Based on https://gist.github.com/ilpropheta/7576dce4c3249df89f85
// Works with:
// * g++ (since 4.9.2, potentially before)
// * clang (since 3.5, potentially before)
// * Visual C++ (since version 14 / 2015)
#ifndef _NAMED_PARAMS_HPP_
#define _NAMED_PARAMS_HPP_
#include <type_traits> // std::is_same
#include <utility> // std::forward
namespace _named_params
{
// Tagged parameter type
template <typename tag, typename type>
struct named_param_t
{
using _tag = tag;
using _type = type;
template <typename T>
named_param_t(T&& value)
: _value(std::forward<T>(value))
{ }
type _value;
};
template <typename... pack>
struct is_named_param_pack : std::false_type { };
template <typename tag, typename type, typename... tail>
struct is_named_param_pack<named_param_t<tag, type>, tail...>
: is_named_param_pack<tail...> { };
template <typename tag, typename type>
struct is_named_param_pack<named_param_t<tag, type>> : std::true_type { };
// Tagged proxy that allows syntax _name = value
// operator=(T&&) returns a named_param_t instance
template <typename tag>
struct named_param_proxy
{
using _tag = tag;
constexpr named_param_proxy() {}
template <typename T>
auto operator=(T&& value) const
{
return named_param_t<tag, decltype(value)>(std::forward<T>(value));
}
};
template <typename T, typename head, typename... tail>
struct named_type_at_p
{
enum { _tmp = (std::is_same<T, typename head::_tag>::value)
? 0 : named_type_at_p<T, tail...>::_pos };
enum { _pos = (_tmp == -1) ? -1 : _tmp + 1 };
};
template <typename T, typename head>
struct named_type_at_p<T, head>
{
enum { _pos = (std::is_same<T, typename head::_tag>::value) ? 1 : -1 };
};
template <typename T, typename head, typename... tail>
struct named_type_at
{
enum { _tmp = named_type_at_p<T, head, tail...>::_pos };
enum { _pos = (_tmp == -1) ? -1 : _tmp - 1 };
};
template <int pos, int curr>
struct named_get_at
{
static_assert(pos >= 0, "Required parameter missing!");
template <typename head, typename... tail>
static auto get(head&&, tail&& ... t)
{
return named_get_at<pos, curr + 1>::get(std::forward<tail>(t)...);
}
};
template <int pos>
struct named_get_at<pos, pos>
{
static_assert(pos >= 0, "Required parameter missing!");
template <typename head, typename... tail>
static auto get(head&& h, tail&& ...)
{
return std::forward<typename head::_type>(h._value);
}
};
}
#define CREATE_TAG(name) \
struct p_##name { }; \
constexpr _named_params::named_param_proxy<p_##name> _##name
#define NAMED_PARAM(name) \
_named_params \
::named_get_at<_named_params::named_type_at<p_##name, pack...>::_pos, 0> \
::get(std::forward<pack>(_pack)...)
template <typename... pack>
using is_named_param_pack = _named_params::is_named_param_pack<pack...>;
#endif // _NAMED_PARAMS_HPP_
#include <array>
#include <iostream>
#include <string>
#include "named_params.hpp"
using namespace std;
CREATE_TAG(a);
CREATE_TAG(b);
CREATE_TAG(c);
CREATE_TAG(d);
struct Example
{
template <typename... pack,
typename = enable_if_t<is_named_param_pack<pack...>::value>>
Example(pack&&... _pack)
: a (NAMED_PARAM(a))
, b (NAMED_PARAM(b))
, c (NAMED_PARAM(c))
, d (NAMED_PARAM(d))
{ }
unsigned int a;
long double b;
string c;
array<string, 4> d;
};
int main()
{
Example ex {
_a = 6,
_b = 1.2,
_c = "asdf",
_d = array<string, 4> {
"a","b","c","d"
}
};
// Copy constructor is selected, param pack constructor
// doesn't match because of is_named_param_pack
Example ex2 = ex;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment