C++ Metaprogramming - A Paradigm Shift - Louis Dionne - CppCon 2015
C++ Metaprogramming - A Paradigm Shift - Louis Dionne - CppCon 2015
A PARADIGM SHIFT
static_assert(has_xxx(foo), "");
static_assert(!has_xxx("abcdef"), "");
INTROSPECTION: THEN
namespace keys {
struct name;
struct age;
}
BOOST_FUSION_DEFINE_ASSOC_STRUCT(
/* global scope */, Person,
(std::string, name, keys::name)
(int, age, keys::age)
)
int main() {
Person john{"John", 30};
std::string name = at_key<keys::name>(john);
int age = at_key<keys::age>(john);
}
INTROSPECTION: NOW
struct Person {
BOOST_HANA_DEFINE_STRUCT(Person,
(std::string, name),
(int, age)
);
};
int main() {
Person john{"John", 30};
std::string name = at_key(john, BOOST_HANA_STRING("name"));
int age = at_key(john, BOOST_HANA_STRING("age"));
}
INTROSPECTION: TOMORROW
struct Person {
BOOST_HANA_DEFINE_STRUCT(Person,
(std::string, name),
(int, age)
);
};
int main() {
Person john{"John", 30};
std::string name = at_key(john, "name"_s);
int age = at_key(john, "age"_s);
}
GENERATING JSON: THEN
// sorry, not going to implement this
GENERATING JSON: NOW
struct Person {
BOOST_HANA_DEFINE_STRUCT(Person,
(std::string, name),
(int, age)
);
};
Output:
[1, "c", {"name" : "Joe", "age" : 30}]
HANDLE BASE TYPES
std::string quote(std::string s) { return "\"" + s + "\""; }
boost/mpl/clear.hpp:29:7: error:
implicit instantiation of undefined template
'boost::mpl::clear_impl<mpl_::integral_c_tag>::apply<mpl_::int_<1> >'
: clear_impl< typename sequence_tag<Sequence>::type >
^
ERROR MESSAGES: NOW
auto xs = hana::reverse(1);
boost/hana/reverse.hpp:35:9:
static_assert failed
"hana::reverse(xs) requires 'xs' to be a Sequence"
static_assert(Sequence<S>::value,
^ ~~~~~~~~~~~~~~~~~~
error_messages.cpp:19:24:
in instantiation of function template specialization
'boost::hana::reverse_t::operator()<int>' requested here
auto xs = hana::reverse(1);
^
COMPILE-TIMES: THEN AND NOW
Including various metaprogramming libraries
(smaller is better)
2
1.59041s 1.60888s
1.5
Time (s)
0.5
0.35452s
0
Boost.Hana Boost.MPL Boost.Fusion
Highcharts.com
1.5
hana::tuple
Time (s)
mpl::vector
fusion::vector
1
0.5
0
0 50 100 150 200 250 300 350 400
Number of elements
Highcharts.com
static_assert(std::is_same<
plus<integral_constant<int, 1>, integral_constant<int, 4>>::type,
integral_constant<int, 5>
>::value, "");
THAT'S OK, BUT...
WHAT IF?
template <typename V, V v, typename U, U u>
constexpr auto
operator+(integral_constant<V, v>, integral_constant<U, u>)
{ return integral_constant<decltype(v + u), v + u>{}; }
// ...
TADAM!
static_assert(decltype(
integral_constant<int, 1>{} + integral_constant<int, 4>{}
==
integral_constant<int, 5>{}
)::value, "");
(OR SIMPLY)
static_assert(integral_constant<int, 1>{} + integral_constant<int, 4>{}
==
integral_constant<int, 5>{}
, "");
PASS ME THE SUGAR, PLEASE
template <int i>
constexpr integral_constant<int, i> int_c{};
static_assert(equal_to<
distance<point<int_<3>, int_<5>>, point<int_<7>, int_<2>>>::type,
int_<5>
>::value, "");
COMPILE-TIME ARITHMETIC: NOW
template <typename P1, typename P2>
constexpr auto distance(P1 p1, P2 p2) {
auto xs = p1.x - p2.x;
auto ys = p1.y - p2.y;
return sqrt(xs*xs + ys*ys);
}
int main() {
int_c<5>.times(f);
}
char a = std::get<1>(values);
char b = values[1_c];
WHY STOP HERE?
std::ratio
std::integer_sequence
HEARD OF <type_traits>?
template <typename T>
struct add_pointer {
using type = T*;
};
auto t = type_c<int>;
auto p = add_pointer(t);
static_assert(is_pointer(p), "");
BUT WHAT DOES THAT BUY US?
TYPES ARE NOW FIRST CLASS CITIZENS!
auto xs = make_tuple(type_c<int>, type_c<char>, type_c<void>);
auto c = xs[1_c];
// sugar:
auto ys = tuple_t<int, char, void>;
FULL LANGUAGE CAN BE USED
Before
using ts = vector<int, char&, void*>;
using us = copy_if<ts, or_<std::is_pointer<_1>,
std::is_reference<_1>>>::type;
After
auto ts = make_tuple(type_c<int>, type_c<char&>, type_c<void*>);
auto us = filter(ts, [](auto t) {
return is_pointer(t) || is_reference(t);
});
ONLY ONE LIBRARY IS REQUIRED
Before
// types (MPL)
using ts = mpl::vector<int, char&, void*>;
using us = mpl::copy_if<ts, mpl::or_<std::is_pointer<_1>,
std::is_reference<_1>>>::type;
// values (Fusion)
auto vs = fusion::make_vector(1, 'c', nullptr, 3.5);
auto ws = fusion::filter_if<std::is_integral<mpl::_1>>(vs);
After
// types
auto ts = tuple_t<int, char&, void*>;
auto us = filter(ts, [](auto t) {
return is_pointer(t) || is_reference(t);
});
// values
auto vs = make_tuple(1, 'c', nullptr, 3.5);
auto ws = filter(vs, [](auto t) {
return is_integral(t);
});
UNIFIED SYNTAX MEANS MORE REUSE
(AMPHIBIOUS EDSL USING BOOST.PROTO)
auto expr = (_1 - _2) / _2;
// compile-time computations
static_assert(decltype(evaluate(expr, 6_c, 2_c))::value == 2, "");
// runtime computations
int i = 6, j = 2;
assert(evaluate(expr, i, j) == 2);
UNIFIED SYNTAX MEANS MORE CONSISTENCY
Before
auto map = make_map<char, int, long, float, double, void>(
"char", "int", "long", "float", "double", "void"
);
std::string i = at_key<int>(map);
assert(i == "int");
After
auto map = make_map(
make_pair(type_c<char>, "char"),
make_pair(type_c<int>, "int"),
make_pair(type_c<long>, "long"),
make_pair(type_c<float>, "float"),
make_pair(type_c<double>, "double")
);
std::string i = map[type_c<int>];
assert(i == "int");
CASE STUDY: SWITCH FOR boost::any
boost::any a = 3;
std::string result = switch_<std::string>(a)(
case_<int>([](int i) { return std::to_string(i); })
, case_<double>([](double d) { return std::to_string(d); })
, empty([] { return "empty"; })
, default_([] { return "default"; })
);
assert(result == "3");
FIRST
template <typename T>
auto case_ = [](auto f) {
return std::make_pair(hana::type_c<T>, f);
};
struct default_t;
auto default_ = case_<default_t>;
auto empty = case_<void>;
THE BEAST
template <typename Result = void, typename Any>
auto switch_(Any& a) {
return [&a](auto ...c) -> Result {
auto cases = hana::make_tuple(c...);