----
====================
## We must rethink metaprogramming
====================
## But how?
### Here's my take
Note:
Hopefully, the audience should be convinced by now that there's a better way
to write metaprograms. My goal is now to convince them that Hana is that way.
==============================================================================
### Heard of `integral_constant`?
```c++
template
struct integral_constant {
static constexpr T value = v;
using value_type = T;
using type = integral_constant;
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; }
};
```
Note:
This section introduces extensions to `std::integral_constant` that follow
the general philosophy of the paradigm shift. I am preparing the grounds for
the next section, which will introduce the type/value unification per-se.
====================
### Compile-time arithmetic: classic approach
```c++
template
struct plus {
using type = integral_constant;
};
static_assert(std::is_same, integral_constant>::type,
integral_constant
>::value, "");
```
----
### That's ok, but...
----
### What if?
```c++
template
constexpr auto
operator+(integral_constant, integral_constant)
{ return integral_constant{}; }
template
constexpr auto
operator==(integral_constant, integral_constant)
{ return integral_constant{}; }
// ...
```
----
### Tadam!
```c++
static_assert(decltype(
integral_constant{} + integral_constant{}
==
integral_constant{}
)::value, "");
```
----
### (or simply)
```c++
static_assert(integral_constant{} + integral_constant{}
==
integral_constant{}
, "");
```
----
### Pass me the sugar, please
```c++
template
constexpr integral_constant int_c{};
static_assert(int_c + int_c == int_c, "");
```
----
### More sugar
```c++
template
constexpr auto operator"" _c() {
// parse the characters and return an integral_constant
}
static_assert(1_c + 4_c == 5_c, "");
```
====================
### Euclidean distance
$$
\mathrm{distance}\left((x_1, y_1), (x_2, y_2)\right)
:= \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}
$$
----
### Compile-time arithmetic: then
```cpp
template
struct distance {
using xs = typename minus::type;
using ys = typename minus::type;
using type = typename sqrt::type,
typename multiplies::type
>::type
>::type;
};
static_assert(equal_to, int_>, point, int_>>::type,
int_
>::value, "");
```
----
### Compile-time arithmetic: now
```cpp
template
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);
}
static_assert(distance(point(3_c, 5_c), point(7_c, 2_c)) == 5_c, "");
```
----
### But runtime arithmetic works too
```cpp
auto p1 = point(3, 5); // dynamic values now
auto p2 = point(7, 2); //
assert(distance(p1, p2) == 5); // same function works!
```
====================
## That's not all
====================
### Loop unrolling
```c++
template
struct integral_constant {
// ...
template
void times(F f) const {
f(); f(); ... f(); // n times
}
};
```
----
### Sceptical?
```cpp
__attribute__((noinline)) void f() { }
int main() {
int_c.times(f);
}
```
Assembly with -O3
```
_main:
; snip
pushq %rbp
movq %rsp, %rbp
callq __Z1fv
callq __Z1fv
callq __Z1fv
callq __Z1fv
callq __Z1fv
xorl %eax, %eax
popq %rbp
retq
```
====================
### Tuple access
```c++
template
struct tuple {
// ...
template
constexpr decltype(auto) operator[](N const&) {
return std::get<:value>(*this);
}
};
```
----
### Compare
```c++
tuple values = {1, 'x', 3.4f};
char a = std::get(values);
char b = values[1_c];
```
====================
### Why stop here?
- `std::ratio`
- `std::integer_sequence`
==============================================================================
### Heard of ``?
```c++
template
struct add_pointer {
using type = T*;
};
using IntPtr = add_pointer::type;
```
Note:
This section introduces the type/value unification.
====================
### Let's try something
```cpp
template
struct type { };
template
constexpr type add_pointer(type const&)
{ return {}; }
template
constexpr std::false_type is_pointer(type const&)
{ return {}; }
template
constexpr std::true_type is_pointer(type const&)
{ return {}; }
```
Note:
Make explicit the link between this and what we did in the previous section
with integral constants.
----
### Tadam!
```cpp
type t{};
auto p = add_pointer(t);
static_assert(is_pointer(p), "");
```
----
### Sugar
```cpp
template
constexpr type type_c{};
auto t = type_c;
auto p = add_pointer(t);
static_assert(is_pointer(p), "");
```
====================
## But what does that buy us?
====================
### Types are now first class citizens!
```cpp
auto xs = make_tuple(type_c, type_c, type_c);
auto c = xs[1_c];
// sugar:
auto ys = tuple_t;
```
====================
### Full language can be used
__Before__
```cpp
using ts = vector;
using us = copy_if,
std::is_reference<_1>>>::type;
```
----
__After__
```cpp
auto ts = make_tuple(type_c, type_c, type_c);
auto us = filter(ts, [](auto t) {
return is_pointer(t) || is_reference(t);
});
```
====================
### Only one library is required
__Before__
```cpp
// types (MPL)
using ts = mpl::vector;
using us = mpl::copy_if,
std::is_reference<_1>>>::type;
// values (Fusion)
auto vs = fusion::make_vector(1, 'c', nullptr, 3.5);
auto ws = fusion::filter_if<:is_integral>>(vs);
```
----
__After__
```cpp
// types
auto ts = tuple_t;
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)
```cpp
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__
```cpp
auto map = make_map(
"char", "int", "long", "float", "double", "void"
);
std::string i = at_key(map);
assert(i == "int");
```
----
__After__
```cpp
auto map = make_map(
make_pair(type_c, "char"),
make_pair(type_c, "int"),
make_pair(type_c, "long"),
make_pair(type_c, "float"),
make_pair(type_c, "double")
);
std::string i = map[type_c];
assert(i == "int");
```
====================
### Case study: switch for `boost::any`
```cpp
boost::any a = 3;
std::string result = switch_<:string>(a)(
case_([](int i) { return std::to_string(i); })
, case_([](double d) { return std::to_string(d); })
, empty([] { return "empty"; })
, default_([] { return "default"; })
);
assert(result == "3");
```
----
### First
```cpp
template
auto case_ = [](auto f) {
return std::make_pair(hana::type_c, f);
};
struct default_t;
auto default_ = case_;
auto empty = case_;
```
----
### The beast
```cpp
template
auto switch_(Any& a) {
return [&a](auto ...c) -> Result {
auto cases = hana::make_tuple(c...);
auto default_ = hana::find_if(cases, [](auto const& c) {
return c.first == hana::type_c;
});
static_assert(!hana::is_nothing(default_),
"switch is missing a default_ case");
auto rest = hana::filter(cases, [](auto const& c) {
return c.first != hana::type_c;
});
return hana::unpack(rest, [&](auto& ...rest) {
return impl(a, a.type(), default_->second, rest...);
});
};
}
```
----
### Processing cases
```cpp
template
Result impl(Any& a, std::type_index const& t, Default& default_,
Case& case_, Rest& ...rest)
{
using T = typename decltype(case_.first)::type;
if (t == typeid(T)) {
return hana::if_(hana::type_c == hana::type_c,
[](auto& c, auto& a) {
return c.second();
},
[](auto& c, auto& a) {
return c.second(*boost::unsafe_any_cast(&a));
}
)(case_, a);
}
else
return impl(a, t, default_, rest...);
}
```
----
### Base case
```cpp
template
Result impl(Any&, std::type_index const& t, Default& default_) {
return default_();
}
```
----
### About 70 LOC
----
### And your coworkers could understand
### (mostly)
==============================================================================
## My proposal, your deliverance: Hana
- Heterogeneous + type level computations
- 80+ algorithms
- 8 heterogeneous containers
- Improved compile-times
====================
### Working on C++14 compilers
- Clang >= 3.5
- GCC 5 on the way
====================
### Newly accepted in Boost!
#### Soon part of the distribution
====================
## Embrace the future
## Embrace Hana
====================
# Thank you
http://ldionne.com
http://github.com/ldionne
==============================================================================
### Bonus: Implementing heterogeneous algorithms
====================
### `transform`
```cpp
template
auto transform_impl(std::tuple const& tuple, F const& f,
std::index_sequence)
{
return std::make_tuple(f(std::get(tuple))...);
}
template
auto transform(std::tuple const& tuple, F const& f) {
return transform_impl(tuple, f,
std::make_index_sequence{});
}
```
====================
## Easy so far
### Brace yourselves
====================
### Here comes `filter`
```cpp
template
auto filter_impl(Tuple const& tuple, Predicate predicate,
std::index_sequence)
{
using Indices = filter_indices(tuple)))::value...
>;
return slice(tuple, Indices{});
}
template
auto filter(std::tuple const& tuple, Predicate predicate) {
return filter_impl(tuple, predicate,
std::make_index_sequence{});
}
```
----
```cpp
template
auto slice(Tuple const& tuple, std::index_sequence) {
return std::make_tuple(std::get(tuple)...);
}
```
----
```cpp
template
struct filter_indices_helper {
// ...
};
template
using filter_indices = typename filter_indices_helper::
template as_index_sequence;
```
----
```cpp
static constexpr bool results_array[sizeof...(results)] = {results...};
static constexpr std::size_t N =
count(results_array, results_array + sizeof...(results), true);
static constexpr array<:size_t n> compute_indices() {
array<:size_t n> indices{};
std::size_t* out = &indices[0];
for (std::size_t i = 0; i computed_indices
= compute_indices();
```
----
```cpp
template >
struct as_index_sequence;
template <:size_t>
struct as_index_sequence<:index_sequence>>
: std::index_sequence
{ };
```