SlideShare a Scribd company logo
1/38
Метапрограммирование в C++11/14 и C++17
Новые инструменты — новые проблемы
Орлов Роман
St. Petersburg C++ User Group
2/38
План
Немного о метапрограммировании
Constexpr if statement
Метапрограммирование в C++11/14
Fold expressions
3/38
Метапрограммирование
Что это и зачем оно нужно?
Что такое метапрограммирование?
Это реализация алгоритмов, входными/выходными данными которых
являются другие программные сущности (классы, функции, …).
3/38
Метапрограммирование
Что это и зачем оно нужно?
Что такое метапрограммирование?
Это реализация алгоритмов, входными/выходными данными которых
являются другие программные сущности (классы, функции, …).
Метапрограммирование или generic programming?
Generic programming – это способ обобщения реализации (или реализаций)
для разнородных типов данных.
Метапрограммирование – это программирование, подразумевает
описание процесса вычислений.
3/38
Метапрограммирование
Что это и зачем оно нужно?
Что такое метапрограммирование?
Это реализация алгоритмов, входными/выходными данными которых
являются другие программные сущности (классы, функции, …).
Метапрограммирование или generic programming?
Generic programming – это способ обобщения реализации (или реализаций)
для разнородных типов данных.
Метапрограммирование – это программирование, подразумевает
описание процесса вычислений.
Зачем нужно метапрограммирование?
Ускорение выполнения программы, перенос части вычислений на этап
компиляции (zero-overhead abstrac ons).
4/38
План
Немного о метапрограммировании
Constexpr if statement
Метапрограммирование в C++11/14
Fold expressions
5/38
Constexpr if statement
Когда его ещё не придумали
template<class T>
std::enable_if_t<(std::is_floating_point_v<T> || sizeof(T) > 4), T>
div10(T x) {
return x / 10;
}
template<class T>
std::enable_if_t<(std::is_unsigned_v<T> && sizeof(T) <= 4), T>
div10(T x) {
return (0xcccccccdULL * x) >> 0x23;
}
template<class T>
std::enable_if_t<(std::is_signed_v<T> && sizeof(T) <= 4), T>
div10(T x) {
auto const t = 0x66666667LL * x;
using U = std::make_unsigned_t<decltype(t)>;
return (t >> 0x22) + ((U)t >> 0x3f);
}
6/38
Constexpr if statement
Synopsis
if constexpr ([init-statement] condition)
statement
[else
statement]
template<class T>
T div10(T x) {
if constexpr (std::is_floating_point_v<T> || sizeof(T) > 4) //[+]
return x / 10;
else if constexpr (std::is_unsigned_v<T>) //[+]
return (0xcccccccdULL * x) >> 0x23;
else {
auto const t = 0x66666667LL * x;
using U = std::make_unsigned_t<decltype(t)>;
return (t >> 0x22) + ((U)t >> 0x3f);
}
}
6/38
Constexpr if statement
Synopsis
if constexpr ([init-statement] condition)
statement
[else
statement]
template<class T>
T div10(T x) {
if constexpr (std::is_floating_point_v<T> || sizeof(T) > 4) //[+]
return x / 10;
else if (std::is_unsigned_v<T>) //[-]
return (0xcccccccdULL * x) >> 0x23;
else {
auto const t = 0x66666667LL * x;
using U = std::make_unsigned_t<decltype(t)>;
return (t >> 0x22) + ((U)t >> 0x3f);
}
}
6/38
Constexpr if statement
Synopsis
if constexpr ([init-statement] condition)
statement
[else
statement]
template<class T>
T div10(T x) {
if (std::is_floating_point_v<T> || sizeof(T) > 4) //[-]
return x / 10;
else if (std::is_unsigned_v<T>) //[-]
return (0xcccccccdULL * x) >> 0x23;
else {
auto const t = 0x66666667LL * x;
using U = std::make_unsigned_t<decltype(t)>;
return (t >> 0x22) + ((U)t >> 0x3f);
}
}
7/38
Constexpr if statement
AST для вещественного аргумента
-FunctionDecl used div10 ’double (double)’
|-TemplateArgument type ’double’
|-ParmVarDecl used x ’double’:’double’
‘-CompoundStmt
‘-IfStmt
|-BinaryOperator ’_Bool’ ’||’
| |-<<<...>>>
| ‘-<<<...>>>
|-ReturnStmt
| ‘-BinaryOperator ’double’ ’/’
| |-ImplicitCastExpr ’double’:’double’ <LValueToRValue>
| | ‘-DeclRefExpr lvalue ParmVar ’x’ ’double’:’double’
| ‘-ImplicitCastExpr ’double’ <IntegralToFloating>
| ‘-IntegerLiteral ’int’ 10
‘-<<<NULL>>> // <-- discarded
8/38
Constexpr if statement
AST для беззнакового целочисленного аргумента
-FunctionDecl used div10 ’unsigned int (unsigned int)’
|-TemplateArgument type ’unsigned int’
|-ParmVarDecl used x ’unsigned int’:’unsigned int’
‘-CompoundStmt
‘-IfStmt
|-BinaryOperator ’_Bool’ ’||’
| |-<<<...>>>
| ‘-<<<...>>>
|-NullStmt // <-- discarded
‘-IfStmt
|-ImplicitCastExpr ’_Bool’ <LValueToRValue>
| ‘-DeclRefExpr ’const _Bool’ ’is_unsigned_v’ ’const _Bool’
|-ReturnStmt
| ‘-ImplicitCastExpr ’unsigned int’:’unsigned int’ <IntegralCast>
| ‘-BinaryOperator ’unsigned long long’ ’>>’
| |-<<<...>>>
| ‘-IntegerLiteral ’int’ 35
‘-<<<NULL>>> // <-- discarded
9/38
Constexpr if statement
AST для целочисленного аргумента
-FunctionDecl used div10 ’int (int)’
|-TemplateArgument type ’int’
|-ParmVarDecl used x ’int’:’int’
‘-CompoundStmt
‘-IfStmt
|-BinaryOperator ’_Bool’ ’||’
| |-<<<...>>>
| ‘-<<<...>>>
|-NullStmt // <-- discarded
‘-IfStmt
|-ImplicitCastExpr ’_Bool’ <LValueToRValue>
| ‘-DeclRefExpr ’const _Bool’ ’is_unsigned_v’ ’const _Bool’
|-NullStmt // <-- discarded
‘-CompoundStmt
|-DeclStmt
| ‘-VarDecl used t ’const long long’:’const long long’ cinit
| ‘-<<<...>>>
|-DeclStmt
| ‘-TypeAliasDecl U ’std::make_unsigned_t<decltype(t)>’
| ‘-<<<...>>>
‘-ReturnStmt
‘-ImplicitCastExpr ’int’:’int’ <IntegralCast>
‘-<<<...>>>
10/38
Constexpr if statement
и стиль кодирования
auto foo(...) {
if (condition)
return statement1;
return statement2;
}
template<typename T>
auto bar(...) {
if constexpr (condition)
return statement1;
else
return statement2;
}
11/38
Constexpr if statement
Все или ничего
template<class ChT, class TrT, class T>
void print(std::basic_ostream<ChT, TrT>& os, T const& v) {
if constexpr (std::is_array_v<T>) {
os << ’[’;
auto first = std::begin(v), last = std::end(v);
if (first != last--) {
while (first != last) {
print(os, *first++);
os << ’,’;
}
print(os, *first);
}
os << ’]’;
} else {
os << v;
}
}
11/38
Constexpr if statement
Все или ничего
template<class ChT, class TrT, class T>
void print(std::basic_ostream<ChT, TrT>& os, T const& v) {
if constexpr (std::is_array_v<T>) {
os << ’[’;
auto first = std::begin(v), last = std::end(v);
if (first != last--) {
while (first != last) {
print(os, *first++);
os << ’,’;
}
print(os, *first);
}
os << ’]’;
} else if constexpr (std::is_enum_v<T>) {
os << ”enum{”;
print(os, static_cast<std::underlying_type_t<T>>(v));
os << ”}”;
} else {
os << v;
}
}
11/38
Constexpr if statement
Все или ничего
template<class ChT, class TrT, class T>
void print(std::basic_ostream<ChT, TrT>& os, T const& v) {
if constexpr (std::is_array_v<T>) {
os << ’[’;
auto first = std::begin(v), last = std::end(v);
if (first != last--) {
while (first != last) {
print(os, *first++);
os << ’,’;
}
print(os, *first);
}
os << ’]’;
} else if constexpr (std::is_enum_v<T>) {
os << ”enum{”;
print(os, static_cast<std::underlying_type_t<T>>(v));
os << ”}”;
} else if constexpr (...) {
...
} else {
os << v;
}
}
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color {
red,
green,
blue
};
color c[] = {color::red, color::green, color::blue};
print(std::cout, c);
> [enum{0},enum{1},enum{2}]
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color { ... };
template<class ChT, class TrT, class T>
void print(std::basic_ostream<ChT, TrT>& os, T v,
std::enable_if_t<std::is_enum_v<T>>* = nullptr) {
os << typeid(T).name() << ’=’;
print(os, static_cast<std::underlying_type_t<T>>(v));
}
color c[] = {color::red, color::green, color::blue};
print(std::cout, c);
> ???
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color { ... };
template<class ChT, class TrT, class T>
void print(std::basic_ostream<ChT, TrT>& os, T v,
std::enable_if_t<std::is_enum_v<T>>* = nullptr) {
os << typeid(T).name() << ’=’;
print(os, static_cast<std::underlying_type_t<T>>(v));
}
color c[] = {color::red, color::green, color::blue};
print(std::cout, c);
> error: call to ’print’ is ambiguous
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color { ... };
template<class ChT, class TrT, class T>
void print_enum(std::basic_ostream<ChT, TrT>& os, T v,
std::enable_if_t<std::is_enum_v<T>>* = nullptr) {
os << typeid(T).name() << ’=’;
print(os, static_cast<std::underlying_type_t<T>>(v));
}
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, color v) {
return print_enum(os, v);
}
color c[] = {color::red, color::green, color::blue};
print(std::cout, c);
> ???
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color { ... };
template<class ChT, class TrT, class T>
void print_enum(std::basic_ostream<ChT, TrT>& os, T v,
std::enable_if_t<std::is_enum_v<T>>* = nullptr) {
os << typeid(T).name() << ’=’;
print(os, static_cast<std::underlying_type_t<T>>(v));
}
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, color v) {
return print_enum(os, v);
}
color c[] = {color::red, color::green, color::blue};
print(std::cout, c);
> [5color=0,5color=1,5color=2]
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color { ... };
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, color v) {
return print_enum(os, v);
}
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, enum1 v) { ... }
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, enum2 v) { ... }
...
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, enumN v) { ... }
13/38
Constexpr if statement
Ответ РГ21
“Как раньше могли написать плохо, так и теперь можем написать
плохо. Просто по-разному.
”Александр Фокин,
председатель РГ21 C++
14/38
План
Немного о метапрограммировании
Constexpr if statement
Метапрограммирование в C++11/14
Fold expressions
15/38
Метапрограммирование в С++11/14
Выделить частный случай для минимального числа параметров
template<typename T>
void print_list(T t) {
std::cout << t << std::endl;
}
Обработать ”голову” списка параметров, рекурсивно повторить для ”хвоста”
template<typename T, typename U, typename... Tail>
void print_list(T t, U u, Tail... tail) {
std::cout << t << ”, ”; // handle HEAD
return print_list(u, tail...); // handle TAIL
}
16/38
Метапрограммирование в С++11/14
На примере контейнера флагов typed_flags
template<typename... Args>
class typed_flags;
Типы вместо констант и перечислений
class eats_meat;
class eats_grass;
class has_tail;
typed_flags<eats_meat, eats_grass, has_tail> wolf;
Массовая установка флагов
wolf.set<eats_grass>(false);
wolf.set<eats_meat, has_tail>();
Логические операции над множеством флагов
assert( (wolf.all<eats_meat, has_tail>()) );
assert( (wolf.any<eats_meat, has_tail, eats_grass>()) );
assert( (wolf.none<eats_grass>()) );
17/38
Поиск типа в списке в C++11/14
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
17/38
Поиск типа в списке в C++11/14
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
Решение на Haskell
is_same a b | a == b = 1
| otherwise = 0
counter _ [] = 0
counter x (h:t) = is_same x h + counter x t
17/38
Поиск типа в списке в C++11/14
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
Решение на Haskell
is_same a b | a == b = 1
| otherwise = 0
counter _ [] = 0
counter x (h:t) = is_same x h + counter x t
Решение на C++
template<typename T, typename Head=empty, typename... Tail>
struct counter {
static constexpr size_t value = std::is_same_v<T, Head>
+ counter<T, Tail...>::value;
};
template<typename T>
struct counter<T, empty> {
static constexpr size_t value = 0;
};
18/38
Поиск типа в списке в C++11/14
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
18/38
Поиск типа в списке в C++11/14
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
18/38
Поиск типа в списке в C++11/14
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
2 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’int’
| |-TemplateArgument pack
| | ‘-TemplateArgument type ’float’
18/38
Поиск типа в списке в C++11/14
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
2 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’int’
| |-TemplateArgument pack
| | ‘-TemplateArgument type ’float’
3 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’float’
| |-TemplateArgument pack
18/38
Поиск типа в списке в C++11/14
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
2 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’int’
| |-TemplateArgument pack
| | ‘-TemplateArgument type ’float’
3 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’float’
| |-TemplateArgument pack
4 ‘-ClassTemplateSpecializationDecl struct counter definition
|-TemplateArgument type ’void’
|-TemplateArgument type ’struct empty’
|-TemplateArgument pack
19/38
Проверка уникальности списка типов в C++11/14
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
19/38
Проверка уникальности списка типов в C++11/14
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
Решение на Haskell
is_unique [] = True
is_unique (h:t) = counter h t == 0 && is_unique t
19/38
Проверка уникальности списка типов в C++11/14
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
Решение на Haskell
is_unique [] = True
is_unique (h:t) = counter h t == 0 && is_unique t
Решение на C++
template<typename Head=empty, typename... Tail>
struct is_unique {
static constexpr bool value = counter<Head, Tail...>::value == 0
&& is_unique<Tail...>::value;
};
template<>
struct is_unique<empty> {
static constexpr bool value = true;
};
20/38
Проверка уникальности списка типов в C++11/14
AST для is_unique<char, char, int>
0 -ClassTemplateDecl is_unique
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
20/38
Проверка уникальности списка типов в C++11/14
AST для is_unique<char, char, int>
0 -ClassTemplateDecl is_unique
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct is_unique definition
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’char’
| | ‘-TemplateArgument type ’int’
| |-CXXRecordDecl implicit struct is_unique
| ‘-VarDecl referenced value ’const _Bool’ static inline constexpr
| ‘-BinaryOperator ’_Bool’ ’&&’
| |-BinaryOperator ’_Bool’ ’==’
| | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue>
| | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’
20/38
Проверка уникальности списка типов в C++11/14
AST для is_unique<char, char, int>
0 -ClassTemplateDecl is_unique
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct is_unique definition
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’char’
| | ‘-TemplateArgument type ’int’
| |-CXXRecordDecl implicit struct is_unique
| ‘-VarDecl referenced value ’const _Bool’ static inline constexpr
| ‘-BinaryOperator ’_Bool’ ’&&’
| |-BinaryOperator ’_Bool’ ’==’
| | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue>
| | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’
| | | ‘-ClassTemplateSpecializationDecl struct counter definition
| | | |-TemplateArgument type ’char’
| | | |-TemplateArgument type ’char’
| | | |-TemplateArgument pack
| | | | ‘-TemplateArgument type ’int’
20/38
Проверка уникальности списка типов в C++11/14
AST для is_unique<char, char, int>
0 -ClassTemplateDecl is_unique
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct is_unique definition
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’char’
| | ‘-TemplateArgument type ’int’
| |-CXXRecordDecl implicit struct is_unique
| ‘-VarDecl referenced value ’const _Bool’ static inline constexpr
| ‘-BinaryOperator ’_Bool’ ’&&’
| |-BinaryOperator ’_Bool’ ’==’
| | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue>
| | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’
| | | ‘-ClassTemplateSpecializationDecl struct counter definition
| | | |-TemplateArgument type ’char’
| | | |-TemplateArgument type ’char’
| | | |-TemplateArgument pack
| | | | ‘-TemplateArgument type ’int’
2 |-ClassTemplateSpecializationDecl struct is_unique definition
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | ‘-TemplateArgument type ’int’
3 ...
21/38
Поиск номера типа в списке в C++11/14
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
21/38
Поиск номера типа в списке в C++11/14
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
Решение на Haskell
index_of_impl _ _ [] = -1
index_of_impl x n (h:t) | x == h = n
| otherwise = index_of_impl x (n + 1) t
index_of x l = index_of_impl x 0 l
21/38
Поиск номера типа в списке в C++11/14
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
Решение на Haskell
index_of_impl _ _ [] = -1
index_of_impl x n (h:t) | x == h = n
| otherwise = index_of_impl x (n + 1) t
index_of x l = index_of_impl x 0 l
Решение на C++
template<typename T, typename... Args>
struct index_of {
static constexpr size_t value =
index_of_impl<T, 0, Args...>::value;
};
...
22/38
Поиск номера типа в списке в C++11/14
Решение на C++ (продолжение)
...
template<typename T, size_t I, typename H=empty, typename... Args>
struct index_of_impl {
static constexpr size_t value =
index_of_impl<T, I + 1, Args...>::value;
};
template<typename T, size_t I, typename... Args>
struct index_of_impl<T, I, T, Args...> {
static constexpr size_t value = I;
};
template<typename T, size_t I>
struct index_of_impl<T, I, empty> {
static constexpr size_t value = -1;
};
23/38
Поиск номера типа в списке в C++11/14
AST для index_of<void, char, void, int, float>
0 -ClassTemplateDecl index_of_impl
|-TemplateTypeParmDecl referenced typename T
|-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I
|-TemplateTypeParmDecl typename H
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Args
23/38
Поиск номера типа в списке в C++11/14
AST для index_of<void, char, void, int, float>
0 -ClassTemplateDecl index_of_impl
|-TemplateTypeParmDecl referenced typename T
|-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I
|-TemplateTypeParmDecl typename H
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Args
1 |-ClassTemplateSpecializationDecl struct index_of_impl definition
| |-TemplateArgument type ’void’
| |-TemplateArgument integral 0
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’void’
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
| |-CXXRecordDecl implicit struct index_of_impl
23/38
Поиск номера типа в списке в C++11/14
AST для index_of<void, char, void, int, float>
0 -ClassTemplateDecl index_of_impl
|-TemplateTypeParmDecl referenced typename T
|-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I
|-TemplateTypeParmDecl typename H
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Args
1 |-ClassTemplateSpecializationDecl struct index_of_impl definition
| |-TemplateArgument type ’void’
| |-TemplateArgument integral 0
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’void’
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
| |-CXXRecordDecl implicit struct index_of_impl
2 ‘-ClassTemplateSpecializationDecl struct index_of_impl definition
|-TemplateArgument type ’void’
|-TemplateArgument integral 1
|-TemplateArgument type ’void’
|-TemplateArgument pack
| |-TemplateArgument type ’int’
| ‘-TemplateArgument type ’float’
|-CXXRecordDecl implicit struct index_of_impl
‘-VarDecl referenced ’const size_t’:’const unsigned int’ static constexpr
‘-SubstNonTypeTemplateParmExpr ’unsigned int’
‘-IntegerLiteral ’unsigned int’ 1
24/38
План
Немного о метапрограммировании
Constexpr if statement
Метапрограммирование в C++11/14
Fold expressions
25/38
Fold expressions
Folding (свертка) – функция высшего порядка, выполняющая преобразование
структуры данных при помощи заданной функции.
le fold (левоассоциативная)
foldl f i [] = i
foldl f i (h:t) = foldl f (f i h) t
foldl (+) 0 [1,2,3,4,5] = ((((0 + 1) + 2) + 3) + 4) + 5
right fold (правоассоциативная)
foldr f i [] = i
foldr f i (h:t) = f h (foldr f i t)
foldr (+) 0 [1,2,3,4,5] = 1 + (2 + (3 + (4 + (5 + 0))))
25/38
Fold expressions
Folding (свертка) – функция высшего порядка, выполняющая преобразование
структуры данных при помощи заданной функции.
le fold (левоассоциативная)
foldl f i [] = i
foldl f i (h:t) = foldl f (f i h) t
foldl (+) 0 [1,2,3,4,5] = ((((0 + 1) + 2) + 3) + 4) + 5
right fold (правоассоциативная)
foldr f i [] = i
foldr f i (h:t) = f h (foldr f i t)
foldr (+) 0 [1,2,3,4,5] = 1 + (2 + (3 + (4 + (5 + 0))))
auto v = {1, 2, 3, 4, 5};
std::accumulate(std::begin(v), std::end(v), 0, std::plus<int>{});
std::accumulate(std::rbegin(v), std::rend(v), 0, std::plus<int>{});
26/38
Fold expressions в C++17
unary fold
unary le fold
(. . . op pack_expression) ⇒ (((P1 op P2) op . . .) op PN)
unary right fold
(pack_expression op . . .) ⇒ (P1 op (. . . op (PN−1 op PN)))
binary fold
binary le fold
(init op . . . op pack_expression) ⇒ ((((I op P1) op P2) op . . .) op PN)
binary right fold
(pack_expression op . . . op init) ⇒ (P1 op (. . . op (PN−1 op (PN op I))))
op ∈
+   -   *   /   %   ^   &   |   <<   >>
+=  -=  *=  /=  %=  ^=  &=  |=  <<=  >>=  =
==  !=  <   >   <=  >=  &&  ||  ,   .*   ->*
27/38
GSL and fold expressions (issue #749)
Рекурсия в шаблонном коде тормозит компиляцию.
template<class T>
void pbv(std::vector<T>& v) {}
template<class T, class H, class... Ts>
void pbv(std::vector<T>& v, H&& h, Ts&&... ts) {
v.push_back(h);
pbv(v, std::forward<Ts>(ts)...);
}
Рекурсии можно избежать при помощи
pack expansions
template<class T, class... Ts>
void pbv(std::vector<T>& v, Ts&&... ts) {
int _[] = {(v.push_back(ts), 0)...};
(void)_;
}
fold expressions
template<class T, class... Ts>
void pbv(std::vector<T>& v, Ts&&... ts) {
(..., v.push_back(ts));
}
28/38
Поиск типа в списке в C++17
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
28/38
Поиск типа в списке в C++17
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
Решение на Haskell
counter x l = foldl (r h -> r + is_same h x) 0 l
28/38
Поиск типа в списке в C++17
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
Решение на Haskell
counter x l = foldl (r h -> r + is_same h x) 0 l
Решение на C++
template<typename T, typename... Args>
struct counter {
static constexpr size_t value =
(0 + ... + std::is_same_v<T, Args>);
};
29/38
Поиск типа в списке в C++17
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename ... Args
|-CXXRecordDecl struct counter definition
| |-CXXRecordDecl implicit struct counter
| ‘-VarDecl value static inline constexpr cinit
| ‘-CXXFoldExpr’<dependent type>’ // New node type
| |-IntegerLiteral ’int’ 0
| ‘-DependentScopeDeclRefExpr ’<dependent type>’ lvalue
1 ‘-ClassTemplateSpecializationDecl struct counter definition
|-TemplateArgument type ’void’
|-TemplateArgument pack
| |-TemplateArgument type ’char’
| |-TemplateArgument type ’int’
| ‘-TemplateArgument type ’float’
|-CXXRecordDecl implicit struct counter
‘-VarDecl referenced value static inline constexpr cinit
‘-ImplicitCastExpr <IntegralCast>
‘-BinaryOperator ’int’ ’+’ // f(T3) + (f(T2) + (0 + f(T1)))
|-BinaryOperator ’int’ ’+’ // f(T2) + (0 + f(T1))
| |-BinaryOperator ’int’ ’+’ // 0 + f(T1)
| | |-IntegerLiteral ’int’ 0
30/38
Поиск типа в списке в C++17
Сравнительная скорость компиляции
10 20 30 40 50 60 70 80 90 100
0
0.5
1
sizeof...(Args)
t,c
C++11/14 Clang
C++11/14 GCC
C++17 Clang
C++17 GCC
31/38
Проверка уникальности списка типов в C++17
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
31/38
Проверка уникальности списка типов в C++17
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
Решение на Haskell
is_unique l = foldl (r h -> r && counter h l == 1) True l
31/38
Проверка уникальности списка типов в C++17
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
Решение на Haskell
is_unique l = foldl (r h -> r && counter h l == 1) True l
Решение на C++
template<typename... Args>
struct is_unique {
static constexpr bool value =
(true && ... && (counter<Args, Args...>::value == 1));
};
31/38
Проверка уникальности списка типов в C++17
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
Решение на Haskell
is_unique l = foldl (r h -> r && counter h l == 1) True l
Решение на C++
template<typename... Args>
struct is_unique {
static constexpr bool value =
(true && ... && (counter<Args, Args...>::value == 1));
};
Решение на C++ (unary le fold)
template<typename... Args>
struct is_unique {
static constexpr bool value =
(... && (counter<Args, Args...>::value == 1));
};
32/38
Проверка уникальности списка типов в C++17
Алгоритмическая проблема
class T1; class T2; class T3; class T4; class T5;
static_assert( is_unique<T1,T2,T3,T4,T5>::value);
Явная рекурсия
1 T1 T2 T3 T4 T5 | N − 1
2 T2 T3 T4 T5 | N − 2
3 T3 T4 T5 | N − 3
4 T4 T5 | N − 4
5 T5 | N − 5
Fold expressions
1 T1 T2 T3 T4 T5 | N
2 T1 T2 T3 T4 T5 | N
3 T1 T2 T3 T4 T5 | N
4 T1 T2 T3 T4 T5 | N
5 T1 T2 T3 T4 T5 | N
33/38
Проверка уникальности списка типов в C++17
Сравнительная скорость компиляции
10 20 30 40 50 60 70 80 90 100
0
20
40
sizeof...(Args)
t,c
C++11/14 Clang
C++11/14 GCC
C++17 Clang
C++17 GCC
34/38
Поиск номера типа в списке в C++17
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
34/38
Поиск номера типа в списке в C++17
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
Решение на Haskell
getn x (t,n) | x == t = n
| otherwise = -1
index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..])
34/38
Поиск номера типа в списке в C++17
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
Решение на Haskell
getn x (t,n) | x == t = n
| otherwise = -1
index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..])
Решение на C++
template<typename T, typename U, typename... Args>
struct index_of_impl;
template<typename T, typename... Args>
struct index_of {
static constexpr size_t value = index_of_impl<
T, std::index_sequence_for<Args...>, Args...>::value;
};
34/38
Поиск номера типа в списке в C++17
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
Решение на Haskell
getn x (t,n) | x == t = n
| otherwise = -1
index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..])
Решение на C++
template<typename T, typename U, typename... Args>
struct index_of_impl;
template<typename T, typename... Args>
struct index_of {
static constexpr size_t value = index_of_impl<
T, std::index_sequence_for<Args...>, Args...>::value;
};
template<typename T, size_t... I, typename... Args>
struct index_of_impl<T, std::index_sequence<I...>, Args...> {
static constexpr size_t value = (size_t(-1) OP
... OP (std::is_same_v<T, Args> ? I : size_t(-1)));
};
35/38
Поиск номера типа в списке в C++17
Реализация оператора свертки
template<typename T, typename Fn>
struct fold_arg {
T value;
constexpr fold_arg(T const& v): value{v} {}
constexpr explicit operator T() const { return value; }
constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const {
return Fn{}(value, a.value);
}
};
35/38
Поиск номера типа в списке в C++17
Реализация оператора свертки
template<typename T, typename Fn>
struct fold_arg {
T value;
constexpr fold_arg(T const& v): value{v} {}
constexpr explicit operator T() const { return value; }
constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const {
return Fn{}(value, a.value);
}
};
struct min {
template<typename T>
constexpr T const& operator ()(T const& lhs, T const& rhs) const {
return lhs < rhs ? lhs : rhs;
}
};
using index = fold_arg<size_t, min>;
35/38
Поиск номера типа в списке в C++17
Реализация оператора свертки
template<typename T, typename Fn>
struct fold_arg {
T value;
constexpr fold_arg(T const& v): value{v} {}
constexpr explicit operator T() const { return value; }
constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const {
return Fn{}(value, a.value);
}
};
struct min {
template<typename T>
constexpr T const& operator ()(T const& lhs, T const& rhs) const {
return lhs < rhs ? lhs : rhs;
}
};
using index = fold_arg<size_t, min>;
template<typename T, size_t... I, typename... Args>
struct index_of_impl<T, std::index_sequence<I...>, Args...> {
static constexpr size_t value = static_cast<size_t>((index(-1)
<< ... << index(std::is_same_v<T, Args> ? I : -1)));
};
36/38
Поиск номера типа в списке в C++17
Реализация оператора свертки (easy)
enum index: size_t {};
constexpr index operator << (index lhs, index rhs) {
return lhs < rhs ? lhs : rhs;
}
template<typename T, size_t... I, typename... Args>
struct index_of_impl<T, std::index_sequence<I...>, Args...> {
static constexpr size_t value = (index(-1) <<
... << index(std::is_same_v<T, Args> ? I : -1));
};
37/38
Поиск номера типа в списке в C++17
Сравнительная скорость компиляции
0 10 20 30 40 50 60 70 80 90 100
0.2
0.4
0.6
0.8
1
1.2
1.4
Позиция искомого типа
t,c
C++11/14 Clang
C++11/14 GCC
C++17 Clang
C++17 GCC
38/38
Забудьте все, чему вас учили?
Q: Как вывести последовательность аргументов с разделителем?
A1: Решите задачу рекурсивно
template<typename T>
void print_list(T t) {
std::cout << t << std::endl;
}
template<typename T, typename U, typename... Tail>
void print_list(T t, U u, Tail... tail) {
std::cout << t << ”, ”;
return print_list(u, tail...);
}
A2: Используйте fold expressions, они быстрее!
template<size_t... I, typename... Ts>
void print_list_impl(std::index_sequence<I...>, Ts... args) {
(..., (std::cout << args << (I + 1 < sizeof...(Ts) ? ”, ” : ””)));
}
template<typename... Ts>
void print_list(Ts... args) {
return print_list_impl(std::index_sequence_for<Ts...>{}, args...);
}

More Related Content

What's hot

Евгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализацияЕвгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализация
Platonov Sergey
 
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведенияДракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Platonov Sergey
 
Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптер
Sergey Platonov
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
Mikhail Kurnosov
 
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковНикита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Sergey Platonov
 
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программированияПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
Alexey Paznikov
 
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Platonov Sergey
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
Alexey Paznikov
 
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Platonov Sergey
 
Григорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияГригорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизация
Sergey Platonov
 
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
Alexey Paznikov
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksMikhail Kurnosov
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
Mikhail Kurnosov
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
Pavel Tsukanov
 
Parallel STL
Parallel STLParallel STL
Parallel STL
Evgeny Krutko
 
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
Alexey Paznikov
 
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4
Объектно-Ориентированное Программирование на C++, Лекции  3 и 4 Объектно-Ориентированное Программирование на C++, Лекции  3 и 4
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4
Dima Dzuba
 

What's hot (20)

Евгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализацияЕвгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализация
 
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведенияДракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
 
Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптер
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
 
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковНикита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
 
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программированияПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
 
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
 
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
 
Григорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияГригорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизация
 
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
Parallel STL
Parallel STLParallel STL
Parallel STL
 
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
 
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4
Объектно-Ориентированное Программирование на C++, Лекции  3 и 4 Объектно-Ориентированное Программирование на C++, Лекции  3 и 4
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4
 

Similar to Метапрограммирование в C++11/14 и C++17. Новые инструменты - новые проблемы.

Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11
Yandex
 
6.3 Специализация шаблонов
6.3 Специализация шаблонов6.3 Специализация шаблонов
6.3 Специализация шаблонов
DEVTYPE
 
Tricky Java Generics
Tricky Java GenericsTricky Java Generics
Tricky Java Generics
Alexander Matorin
 
6.2 Шаблоны функций
6.2 Шаблоны функций6.2 Шаблоны функций
6.2 Шаблоны функций
DEVTYPE
 
PascalABC.NET 2015-2016
PascalABC.NET 2015-2016PascalABC.NET 2015-2016
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
Alexey Paznikov
 
Опыт разработки статического анализатора кода
Опыт разработки статического анализатора кодаОпыт разработки статического анализатора кода
Опыт разработки статического анализатора кода
Andrey Karpov
 
Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8.
Dima Dzuba
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
Vasil Remeniuk
 
Rust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатноRust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатно
Open-IT
 
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Yandex
 
!Predictive analytics part_2
!Predictive analytics part_2!Predictive analytics part_2
!Predictive analytics part_2
Vladimir Krylov
 
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Yandex
 
стандартная библиотека с++: введение
стандартная библиотека с++: введениестандартная библиотека с++: введение
стандартная библиотека с++: введение
mcroitor
 
Автоматизированная разработка генераторов тестовых программ для микропроцессо...
Автоматизированная разработка генераторов тестовых программ для микропроцессо...Автоматизированная разработка генераторов тестовых программ для микропроцессо...
Автоматизированная разработка генераторов тестовых программ для микропроцессо...
CEE-SEC(R)
 
Статический анализ: вокруг Java за 60 минут
Статический анализ: вокруг Java за 60 минутСтатический анализ: вокруг Java за 60 минут
Статический анализ: вокруг Java за 60 минут
Andrey Karpov
 
Оптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templatesОптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templates
Platonov Sergey
 

Similar to Метапрограммирование в C++11/14 и C++17. Новые инструменты - новые проблемы. (20)

Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11
 
6.3 Специализация шаблонов
6.3 Специализация шаблонов6.3 Специализация шаблонов
6.3 Специализация шаблонов
 
Tricky Java Generics
Tricky Java GenericsTricky Java Generics
Tricky Java Generics
 
6.2 Шаблоны функций
6.2 Шаблоны функций6.2 Шаблоны функций
6.2 Шаблоны функций
 
PascalABC.NET 2015-2016
PascalABC.NET 2015-2016PascalABC.NET 2015-2016
PascalABC.NET 2015-2016
 
Document
DocumentDocument
Document
 
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
 
Опыт разработки статического анализатора кода
Опыт разработки статического анализатора кодаОпыт разработки статического анализатора кода
Опыт разработки статического анализатора кода
 
Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8.
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
 
Rust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатноRust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатно
 
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
 
!Predictive analytics part_2
!Predictive analytics part_2!Predictive analytics part_2
!Predictive analytics part_2
 
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий Леванов
 
стандартная библиотека с++: введение
стандартная библиотека с++: введениестандартная библиотека с++: введение
стандартная библиотека с++: введение
 
Автоматизированная разработка генераторов тестовых программ для микропроцессо...
Автоматизированная разработка генераторов тестовых программ для микропроцессо...Автоматизированная разработка генераторов тестовых программ для микропроцессо...
Автоматизированная разработка генераторов тестовых программ для микропроцессо...
 
Статический анализ: вокруг Java за 60 минут
Статический анализ: вокруг Java за 60 минутСтатический анализ: вокруг Java за 60 минут
Статический анализ: вокруг Java за 60 минут
 
Rgsu04
Rgsu04Rgsu04
Rgsu04
 
Rgsu04
Rgsu04Rgsu04
Rgsu04
 
Оптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templatesОптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templates
 

Метапрограммирование в C++11/14 и C++17. Новые инструменты - новые проблемы.

  • 1. 1/38 Метапрограммирование в C++11/14 и C++17 Новые инструменты — новые проблемы Орлов Роман St. Petersburg C++ User Group
  • 2. 2/38 План Немного о метапрограммировании Constexpr if statement Метапрограммирование в C++11/14 Fold expressions
  • 3. 3/38 Метапрограммирование Что это и зачем оно нужно? Что такое метапрограммирование? Это реализация алгоритмов, входными/выходными данными которых являются другие программные сущности (классы, функции, …).
  • 4. 3/38 Метапрограммирование Что это и зачем оно нужно? Что такое метапрограммирование? Это реализация алгоритмов, входными/выходными данными которых являются другие программные сущности (классы, функции, …). Метапрограммирование или generic programming? Generic programming – это способ обобщения реализации (или реализаций) для разнородных типов данных. Метапрограммирование – это программирование, подразумевает описание процесса вычислений.
  • 5. 3/38 Метапрограммирование Что это и зачем оно нужно? Что такое метапрограммирование? Это реализация алгоритмов, входными/выходными данными которых являются другие программные сущности (классы, функции, …). Метапрограммирование или generic programming? Generic programming – это способ обобщения реализации (или реализаций) для разнородных типов данных. Метапрограммирование – это программирование, подразумевает описание процесса вычислений. Зачем нужно метапрограммирование? Ускорение выполнения программы, перенос части вычислений на этап компиляции (zero-overhead abstrac ons).
  • 6. 4/38 План Немного о метапрограммировании Constexpr if statement Метапрограммирование в C++11/14 Fold expressions
  • 7. 5/38 Constexpr if statement Когда его ещё не придумали template<class T> std::enable_if_t<(std::is_floating_point_v<T> || sizeof(T) > 4), T> div10(T x) { return x / 10; } template<class T> std::enable_if_t<(std::is_unsigned_v<T> && sizeof(T) <= 4), T> div10(T x) { return (0xcccccccdULL * x) >> 0x23; } template<class T> std::enable_if_t<(std::is_signed_v<T> && sizeof(T) <= 4), T> div10(T x) { auto const t = 0x66666667LL * x; using U = std::make_unsigned_t<decltype(t)>; return (t >> 0x22) + ((U)t >> 0x3f); }
  • 8. 6/38 Constexpr if statement Synopsis if constexpr ([init-statement] condition) statement [else statement] template<class T> T div10(T x) { if constexpr (std::is_floating_point_v<T> || sizeof(T) > 4) //[+] return x / 10; else if constexpr (std::is_unsigned_v<T>) //[+] return (0xcccccccdULL * x) >> 0x23; else { auto const t = 0x66666667LL * x; using U = std::make_unsigned_t<decltype(t)>; return (t >> 0x22) + ((U)t >> 0x3f); } }
  • 9. 6/38 Constexpr if statement Synopsis if constexpr ([init-statement] condition) statement [else statement] template<class T> T div10(T x) { if constexpr (std::is_floating_point_v<T> || sizeof(T) > 4) //[+] return x / 10; else if (std::is_unsigned_v<T>) //[-] return (0xcccccccdULL * x) >> 0x23; else { auto const t = 0x66666667LL * x; using U = std::make_unsigned_t<decltype(t)>; return (t >> 0x22) + ((U)t >> 0x3f); } }
  • 10. 6/38 Constexpr if statement Synopsis if constexpr ([init-statement] condition) statement [else statement] template<class T> T div10(T x) { if (std::is_floating_point_v<T> || sizeof(T) > 4) //[-] return x / 10; else if (std::is_unsigned_v<T>) //[-] return (0xcccccccdULL * x) >> 0x23; else { auto const t = 0x66666667LL * x; using U = std::make_unsigned_t<decltype(t)>; return (t >> 0x22) + ((U)t >> 0x3f); } }
  • 11. 7/38 Constexpr if statement AST для вещественного аргумента -FunctionDecl used div10 ’double (double)’ |-TemplateArgument type ’double’ |-ParmVarDecl used x ’double’:’double’ ‘-CompoundStmt ‘-IfStmt |-BinaryOperator ’_Bool’ ’||’ | |-<<<...>>> | ‘-<<<...>>> |-ReturnStmt | ‘-BinaryOperator ’double’ ’/’ | |-ImplicitCastExpr ’double’:’double’ <LValueToRValue> | | ‘-DeclRefExpr lvalue ParmVar ’x’ ’double’:’double’ | ‘-ImplicitCastExpr ’double’ <IntegralToFloating> | ‘-IntegerLiteral ’int’ 10 ‘-<<<NULL>>> // <-- discarded
  • 12. 8/38 Constexpr if statement AST для беззнакового целочисленного аргумента -FunctionDecl used div10 ’unsigned int (unsigned int)’ |-TemplateArgument type ’unsigned int’ |-ParmVarDecl used x ’unsigned int’:’unsigned int’ ‘-CompoundStmt ‘-IfStmt |-BinaryOperator ’_Bool’ ’||’ | |-<<<...>>> | ‘-<<<...>>> |-NullStmt // <-- discarded ‘-IfStmt |-ImplicitCastExpr ’_Bool’ <LValueToRValue> | ‘-DeclRefExpr ’const _Bool’ ’is_unsigned_v’ ’const _Bool’ |-ReturnStmt | ‘-ImplicitCastExpr ’unsigned int’:’unsigned int’ <IntegralCast> | ‘-BinaryOperator ’unsigned long long’ ’>>’ | |-<<<...>>> | ‘-IntegerLiteral ’int’ 35 ‘-<<<NULL>>> // <-- discarded
  • 13. 9/38 Constexpr if statement AST для целочисленного аргумента -FunctionDecl used div10 ’int (int)’ |-TemplateArgument type ’int’ |-ParmVarDecl used x ’int’:’int’ ‘-CompoundStmt ‘-IfStmt |-BinaryOperator ’_Bool’ ’||’ | |-<<<...>>> | ‘-<<<...>>> |-NullStmt // <-- discarded ‘-IfStmt |-ImplicitCastExpr ’_Bool’ <LValueToRValue> | ‘-DeclRefExpr ’const _Bool’ ’is_unsigned_v’ ’const _Bool’ |-NullStmt // <-- discarded ‘-CompoundStmt |-DeclStmt | ‘-VarDecl used t ’const long long’:’const long long’ cinit | ‘-<<<...>>> |-DeclStmt | ‘-TypeAliasDecl U ’std::make_unsigned_t<decltype(t)>’ | ‘-<<<...>>> ‘-ReturnStmt ‘-ImplicitCastExpr ’int’:’int’ <IntegralCast> ‘-<<<...>>>
  • 14. 10/38 Constexpr if statement и стиль кодирования auto foo(...) { if (condition) return statement1; return statement2; } template<typename T> auto bar(...) { if constexpr (condition) return statement1; else return statement2; }
  • 15. 11/38 Constexpr if statement Все или ничего template<class ChT, class TrT, class T> void print(std::basic_ostream<ChT, TrT>& os, T const& v) { if constexpr (std::is_array_v<T>) { os << ’[’; auto first = std::begin(v), last = std::end(v); if (first != last--) { while (first != last) { print(os, *first++); os << ’,’; } print(os, *first); } os << ’]’; } else { os << v; } }
  • 16. 11/38 Constexpr if statement Все или ничего template<class ChT, class TrT, class T> void print(std::basic_ostream<ChT, TrT>& os, T const& v) { if constexpr (std::is_array_v<T>) { os << ’[’; auto first = std::begin(v), last = std::end(v); if (first != last--) { while (first != last) { print(os, *first++); os << ’,’; } print(os, *first); } os << ’]’; } else if constexpr (std::is_enum_v<T>) { os << ”enum{”; print(os, static_cast<std::underlying_type_t<T>>(v)); os << ”}”; } else { os << v; } }
  • 17. 11/38 Constexpr if statement Все или ничего template<class ChT, class TrT, class T> void print(std::basic_ostream<ChT, TrT>& os, T const& v) { if constexpr (std::is_array_v<T>) { os << ’[’; auto first = std::begin(v), last = std::end(v); if (first != last--) { while (first != last) { print(os, *first++); os << ’,’; } print(os, *first); } os << ’]’; } else if constexpr (std::is_enum_v<T>) { os << ”enum{”; print(os, static_cast<std::underlying_type_t<T>>(v)); os << ”}”; } else if constexpr (...) { ... } else { os << v; } }
  • 18. 12/38 Constexpr if statement Проблема расширения #include ”print.hpp” enum class color { red, green, blue }; color c[] = {color::red, color::green, color::blue}; print(std::cout, c); > [enum{0},enum{1},enum{2}]
  • 19. 12/38 Constexpr if statement Проблема расширения #include ”print.hpp” enum class color { ... }; template<class ChT, class TrT, class T> void print(std::basic_ostream<ChT, TrT>& os, T v, std::enable_if_t<std::is_enum_v<T>>* = nullptr) { os << typeid(T).name() << ’=’; print(os, static_cast<std::underlying_type_t<T>>(v)); } color c[] = {color::red, color::green, color::blue}; print(std::cout, c); > ???
  • 20. 12/38 Constexpr if statement Проблема расширения #include ”print.hpp” enum class color { ... }; template<class ChT, class TrT, class T> void print(std::basic_ostream<ChT, TrT>& os, T v, std::enable_if_t<std::is_enum_v<T>>* = nullptr) { os << typeid(T).name() << ’=’; print(os, static_cast<std::underlying_type_t<T>>(v)); } color c[] = {color::red, color::green, color::blue}; print(std::cout, c); > error: call to ’print’ is ambiguous
  • 21. 12/38 Constexpr if statement Проблема расширения #include ”print.hpp” enum class color { ... }; template<class ChT, class TrT, class T> void print_enum(std::basic_ostream<ChT, TrT>& os, T v, std::enable_if_t<std::is_enum_v<T>>* = nullptr) { os << typeid(T).name() << ’=’; print(os, static_cast<std::underlying_type_t<T>>(v)); } template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, color v) { return print_enum(os, v); } color c[] = {color::red, color::green, color::blue}; print(std::cout, c); > ???
  • 22. 12/38 Constexpr if statement Проблема расширения #include ”print.hpp” enum class color { ... }; template<class ChT, class TrT, class T> void print_enum(std::basic_ostream<ChT, TrT>& os, T v, std::enable_if_t<std::is_enum_v<T>>* = nullptr) { os << typeid(T).name() << ’=’; print(os, static_cast<std::underlying_type_t<T>>(v)); } template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, color v) { return print_enum(os, v); } color c[] = {color::red, color::green, color::blue}; print(std::cout, c); > [5color=0,5color=1,5color=2]
  • 23. 12/38 Constexpr if statement Проблема расширения #include ”print.hpp” enum class color { ... }; template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, color v) { return print_enum(os, v); } template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, enum1 v) { ... } template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, enum2 v) { ... } ... template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, enumN v) { ... }
  • 24. 13/38 Constexpr if statement Ответ РГ21 “Как раньше могли написать плохо, так и теперь можем написать плохо. Просто по-разному. ”Александр Фокин, председатель РГ21 C++
  • 25. 14/38 План Немного о метапрограммировании Constexpr if statement Метапрограммирование в C++11/14 Fold expressions
  • 26. 15/38 Метапрограммирование в С++11/14 Выделить частный случай для минимального числа параметров template<typename T> void print_list(T t) { std::cout << t << std::endl; } Обработать ”голову” списка параметров, рекурсивно повторить для ”хвоста” template<typename T, typename U, typename... Tail> void print_list(T t, U u, Tail... tail) { std::cout << t << ”, ”; // handle HEAD return print_list(u, tail...); // handle TAIL }
  • 27. 16/38 Метапрограммирование в С++11/14 На примере контейнера флагов typed_flags template<typename... Args> class typed_flags; Типы вместо констант и перечислений class eats_meat; class eats_grass; class has_tail; typed_flags<eats_meat, eats_grass, has_tail> wolf; Массовая установка флагов wolf.set<eats_grass>(false); wolf.set<eats_meat, has_tail>(); Логические операции над множеством флагов assert( (wolf.all<eats_meat, has_tail>()) ); assert( (wolf.any<eats_meat, has_tail, eats_grass>()) ); assert( (wolf.none<eats_grass>()) );
  • 28. 17/38 Поиск типа в списке в C++11/14 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2);
  • 29. 17/38 Поиск типа в списке в C++11/14 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2); Решение на Haskell is_same a b | a == b = 1 | otherwise = 0 counter _ [] = 0 counter x (h:t) = is_same x h + counter x t
  • 30. 17/38 Поиск типа в списке в C++11/14 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2); Решение на Haskell is_same a b | a == b = 1 | otherwise = 0 counter _ [] = 0 counter x (h:t) = is_same x h + counter x t Решение на C++ template<typename T, typename Head=empty, typename... Tail> struct counter { static constexpr size_t value = std::is_same_v<T, Head> + counter<T, Tail...>::value; }; template<typename T> struct counter<T, empty> { static constexpr size_t value = 0; };
  • 31. 18/38 Поиск типа в списке в C++11/14 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail
  • 32. 18/38 Поиск типа в списке в C++11/14 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’
  • 33. 18/38 Поиск типа в списке в C++11/14 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’ 2 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’int’ | |-TemplateArgument pack | | ‘-TemplateArgument type ’float’
  • 34. 18/38 Поиск типа в списке в C++11/14 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’ 2 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’int’ | |-TemplateArgument pack | | ‘-TemplateArgument type ’float’ 3 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’float’ | |-TemplateArgument pack
  • 35. 18/38 Поиск типа в списке в C++11/14 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’ 2 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’int’ | |-TemplateArgument pack | | ‘-TemplateArgument type ’float’ 3 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’float’ | |-TemplateArgument pack 4 ‘-ClassTemplateSpecializationDecl struct counter definition |-TemplateArgument type ’void’ |-TemplateArgument type ’struct empty’ |-TemplateArgument pack
  • 36. 19/38 Проверка уникальности списка типов в C++11/14 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value);
  • 37. 19/38 Проверка уникальности списка типов в C++11/14 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value); Решение на Haskell is_unique [] = True is_unique (h:t) = counter h t == 0 && is_unique t
  • 38. 19/38 Проверка уникальности списка типов в C++11/14 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value); Решение на Haskell is_unique [] = True is_unique (h:t) = counter h t == 0 && is_unique t Решение на C++ template<typename Head=empty, typename... Tail> struct is_unique { static constexpr bool value = counter<Head, Tail...>::value == 0 && is_unique<Tail...>::value; }; template<> struct is_unique<empty> { static constexpr bool value = true; };
  • 39. 20/38 Проверка уникальности списка типов в C++11/14 AST для is_unique<char, char, int> 0 -ClassTemplateDecl is_unique |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail
  • 40. 20/38 Проверка уникальности списка типов в C++11/14 AST для is_unique<char, char, int> 0 -ClassTemplateDecl is_unique |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct is_unique definition | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’char’ | | ‘-TemplateArgument type ’int’ | |-CXXRecordDecl implicit struct is_unique | ‘-VarDecl referenced value ’const _Bool’ static inline constexpr | ‘-BinaryOperator ’_Bool’ ’&&’ | |-BinaryOperator ’_Bool’ ’==’ | | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue> | | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’
  • 41. 20/38 Проверка уникальности списка типов в C++11/14 AST для is_unique<char, char, int> 0 -ClassTemplateDecl is_unique |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct is_unique definition | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’char’ | | ‘-TemplateArgument type ’int’ | |-CXXRecordDecl implicit struct is_unique | ‘-VarDecl referenced value ’const _Bool’ static inline constexpr | ‘-BinaryOperator ’_Bool’ ’&&’ | |-BinaryOperator ’_Bool’ ’==’ | | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue> | | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’ | | | ‘-ClassTemplateSpecializationDecl struct counter definition | | | |-TemplateArgument type ’char’ | | | |-TemplateArgument type ’char’ | | | |-TemplateArgument pack | | | | ‘-TemplateArgument type ’int’
  • 42. 20/38 Проверка уникальности списка типов в C++11/14 AST для is_unique<char, char, int> 0 -ClassTemplateDecl is_unique |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct is_unique definition | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’char’ | | ‘-TemplateArgument type ’int’ | |-CXXRecordDecl implicit struct is_unique | ‘-VarDecl referenced value ’const _Bool’ static inline constexpr | ‘-BinaryOperator ’_Bool’ ’&&’ | |-BinaryOperator ’_Bool’ ’==’ | | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue> | | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’ | | | ‘-ClassTemplateSpecializationDecl struct counter definition | | | |-TemplateArgument type ’char’ | | | |-TemplateArgument type ’char’ | | | |-TemplateArgument pack | | | | ‘-TemplateArgument type ’int’ 2 |-ClassTemplateSpecializationDecl struct is_unique definition | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | ‘-TemplateArgument type ’int’ 3 ...
  • 43. 21/38 Поиск номера типа в списке в C++11/14 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2);
  • 44. 21/38 Поиск номера типа в списке в C++11/14 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2); Решение на Haskell index_of_impl _ _ [] = -1 index_of_impl x n (h:t) | x == h = n | otherwise = index_of_impl x (n + 1) t index_of x l = index_of_impl x 0 l
  • 45. 21/38 Поиск номера типа в списке в C++11/14 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2); Решение на Haskell index_of_impl _ _ [] = -1 index_of_impl x n (h:t) | x == h = n | otherwise = index_of_impl x (n + 1) t index_of x l = index_of_impl x 0 l Решение на C++ template<typename T, typename... Args> struct index_of { static constexpr size_t value = index_of_impl<T, 0, Args...>::value; }; ...
  • 46. 22/38 Поиск номера типа в списке в C++11/14 Решение на C++ (продолжение) ... template<typename T, size_t I, typename H=empty, typename... Args> struct index_of_impl { static constexpr size_t value = index_of_impl<T, I + 1, Args...>::value; }; template<typename T, size_t I, typename... Args> struct index_of_impl<T, I, T, Args...> { static constexpr size_t value = I; }; template<typename T, size_t I> struct index_of_impl<T, I, empty> { static constexpr size_t value = -1; };
  • 47. 23/38 Поиск номера типа в списке в C++11/14 AST для index_of<void, char, void, int, float> 0 -ClassTemplateDecl index_of_impl |-TemplateTypeParmDecl referenced typename T |-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I |-TemplateTypeParmDecl typename H | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Args
  • 48. 23/38 Поиск номера типа в списке в C++11/14 AST для index_of<void, char, void, int, float> 0 -ClassTemplateDecl index_of_impl |-TemplateTypeParmDecl referenced typename T |-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I |-TemplateTypeParmDecl typename H | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Args 1 |-ClassTemplateSpecializationDecl struct index_of_impl definition | |-TemplateArgument type ’void’ | |-TemplateArgument integral 0 | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’void’ | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’ | |-CXXRecordDecl implicit struct index_of_impl
  • 49. 23/38 Поиск номера типа в списке в C++11/14 AST для index_of<void, char, void, int, float> 0 -ClassTemplateDecl index_of_impl |-TemplateTypeParmDecl referenced typename T |-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I |-TemplateTypeParmDecl typename H | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Args 1 |-ClassTemplateSpecializationDecl struct index_of_impl definition | |-TemplateArgument type ’void’ | |-TemplateArgument integral 0 | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’void’ | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’ | |-CXXRecordDecl implicit struct index_of_impl 2 ‘-ClassTemplateSpecializationDecl struct index_of_impl definition |-TemplateArgument type ’void’ |-TemplateArgument integral 1 |-TemplateArgument type ’void’ |-TemplateArgument pack | |-TemplateArgument type ’int’ | ‘-TemplateArgument type ’float’ |-CXXRecordDecl implicit struct index_of_impl ‘-VarDecl referenced ’const size_t’:’const unsigned int’ static constexpr ‘-SubstNonTypeTemplateParmExpr ’unsigned int’ ‘-IntegerLiteral ’unsigned int’ 1
  • 50. 24/38 План Немного о метапрограммировании Constexpr if statement Метапрограммирование в C++11/14 Fold expressions
  • 51. 25/38 Fold expressions Folding (свертка) – функция высшего порядка, выполняющая преобразование структуры данных при помощи заданной функции. le fold (левоассоциативная) foldl f i [] = i foldl f i (h:t) = foldl f (f i h) t foldl (+) 0 [1,2,3,4,5] = ((((0 + 1) + 2) + 3) + 4) + 5 right fold (правоассоциативная) foldr f i [] = i foldr f i (h:t) = f h (foldr f i t) foldr (+) 0 [1,2,3,4,5] = 1 + (2 + (3 + (4 + (5 + 0))))
  • 52. 25/38 Fold expressions Folding (свертка) – функция высшего порядка, выполняющая преобразование структуры данных при помощи заданной функции. le fold (левоассоциативная) foldl f i [] = i foldl f i (h:t) = foldl f (f i h) t foldl (+) 0 [1,2,3,4,5] = ((((0 + 1) + 2) + 3) + 4) + 5 right fold (правоассоциативная) foldr f i [] = i foldr f i (h:t) = f h (foldr f i t) foldr (+) 0 [1,2,3,4,5] = 1 + (2 + (3 + (4 + (5 + 0)))) auto v = {1, 2, 3, 4, 5}; std::accumulate(std::begin(v), std::end(v), 0, std::plus<int>{}); std::accumulate(std::rbegin(v), std::rend(v), 0, std::plus<int>{});
  • 53. 26/38 Fold expressions в C++17 unary fold unary le fold (. . . op pack_expression) ⇒ (((P1 op P2) op . . .) op PN) unary right fold (pack_expression op . . .) ⇒ (P1 op (. . . op (PN−1 op PN))) binary fold binary le fold (init op . . . op pack_expression) ⇒ ((((I op P1) op P2) op . . .) op PN) binary right fold (pack_expression op . . . op init) ⇒ (P1 op (. . . op (PN−1 op (PN op I)))) op ∈ +   -   *   /   %   ^   &   |   <<   >> +=  -=  *=  /=  %=  ^=  &=  |=  <<=  >>=  = ==  !=  <   >   <=  >=  &&  ||  ,   .*   ->*
  • 54. 27/38 GSL and fold expressions (issue #749) Рекурсия в шаблонном коде тормозит компиляцию. template<class T> void pbv(std::vector<T>& v) {} template<class T, class H, class... Ts> void pbv(std::vector<T>& v, H&& h, Ts&&... ts) { v.push_back(h); pbv(v, std::forward<Ts>(ts)...); } Рекурсии можно избежать при помощи pack expansions template<class T, class... Ts> void pbv(std::vector<T>& v, Ts&&... ts) { int _[] = {(v.push_back(ts), 0)...}; (void)_; } fold expressions template<class T, class... Ts> void pbv(std::vector<T>& v, Ts&&... ts) { (..., v.push_back(ts)); }
  • 55. 28/38 Поиск типа в списке в C++17 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2);
  • 56. 28/38 Поиск типа в списке в C++17 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2); Решение на Haskell counter x l = foldl (r h -> r + is_same h x) 0 l
  • 57. 28/38 Поиск типа в списке в C++17 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2); Решение на Haskell counter x l = foldl (r h -> r + is_same h x) 0 l Решение на C++ template<typename T, typename... Args> struct counter { static constexpr size_t value = (0 + ... + std::is_same_v<T, Args>); };
  • 58. 29/38 Поиск типа в списке в C++17 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename ... Args |-CXXRecordDecl struct counter definition | |-CXXRecordDecl implicit struct counter | ‘-VarDecl value static inline constexpr cinit | ‘-CXXFoldExpr’<dependent type>’ // New node type | |-IntegerLiteral ’int’ 0 | ‘-DependentScopeDeclRefExpr ’<dependent type>’ lvalue 1 ‘-ClassTemplateSpecializationDecl struct counter definition |-TemplateArgument type ’void’ |-TemplateArgument pack | |-TemplateArgument type ’char’ | |-TemplateArgument type ’int’ | ‘-TemplateArgument type ’float’ |-CXXRecordDecl implicit struct counter ‘-VarDecl referenced value static inline constexpr cinit ‘-ImplicitCastExpr <IntegralCast> ‘-BinaryOperator ’int’ ’+’ // f(T3) + (f(T2) + (0 + f(T1))) |-BinaryOperator ’int’ ’+’ // f(T2) + (0 + f(T1)) | |-BinaryOperator ’int’ ’+’ // 0 + f(T1) | | |-IntegerLiteral ’int’ 0
  • 59. 30/38 Поиск типа в списке в C++17 Сравнительная скорость компиляции 10 20 30 40 50 60 70 80 90 100 0 0.5 1 sizeof...(Args) t,c C++11/14 Clang C++11/14 GCC C++17 Clang C++17 GCC
  • 60. 31/38 Проверка уникальности списка типов в C++17 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value);
  • 61. 31/38 Проверка уникальности списка типов в C++17 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value); Решение на Haskell is_unique l = foldl (r h -> r && counter h l == 1) True l
  • 62. 31/38 Проверка уникальности списка типов в C++17 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value); Решение на Haskell is_unique l = foldl (r h -> r && counter h l == 1) True l Решение на C++ template<typename... Args> struct is_unique { static constexpr bool value = (true && ... && (counter<Args, Args...>::value == 1)); };
  • 63. 31/38 Проверка уникальности списка типов в C++17 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value); Решение на Haskell is_unique l = foldl (r h -> r && counter h l == 1) True l Решение на C++ template<typename... Args> struct is_unique { static constexpr bool value = (true && ... && (counter<Args, Args...>::value == 1)); }; Решение на C++ (unary le fold) template<typename... Args> struct is_unique { static constexpr bool value = (... && (counter<Args, Args...>::value == 1)); };
  • 64. 32/38 Проверка уникальности списка типов в C++17 Алгоритмическая проблема class T1; class T2; class T3; class T4; class T5; static_assert( is_unique<T1,T2,T3,T4,T5>::value); Явная рекурсия 1 T1 T2 T3 T4 T5 | N − 1 2 T2 T3 T4 T5 | N − 2 3 T3 T4 T5 | N − 3 4 T4 T5 | N − 4 5 T5 | N − 5 Fold expressions 1 T1 T2 T3 T4 T5 | N 2 T1 T2 T3 T4 T5 | N 3 T1 T2 T3 T4 T5 | N 4 T1 T2 T3 T4 T5 | N 5 T1 T2 T3 T4 T5 | N
  • 65. 33/38 Проверка уникальности списка типов в C++17 Сравнительная скорость компиляции 10 20 30 40 50 60 70 80 90 100 0 20 40 sizeof...(Args) t,c C++11/14 Clang C++11/14 GCC C++17 Clang C++17 GCC
  • 66. 34/38 Поиск номера типа в списке в C++17 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2);
  • 67. 34/38 Поиск номера типа в списке в C++17 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2); Решение на Haskell getn x (t,n) | x == t = n | otherwise = -1 index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..])
  • 68. 34/38 Поиск номера типа в списке в C++17 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2); Решение на Haskell getn x (t,n) | x == t = n | otherwise = -1 index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..]) Решение на C++ template<typename T, typename U, typename... Args> struct index_of_impl; template<typename T, typename... Args> struct index_of { static constexpr size_t value = index_of_impl< T, std::index_sequence_for<Args...>, Args...>::value; };
  • 69. 34/38 Поиск номера типа в списке в C++17 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2); Решение на Haskell getn x (t,n) | x == t = n | otherwise = -1 index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..]) Решение на C++ template<typename T, typename U, typename... Args> struct index_of_impl; template<typename T, typename... Args> struct index_of { static constexpr size_t value = index_of_impl< T, std::index_sequence_for<Args...>, Args...>::value; }; template<typename T, size_t... I, typename... Args> struct index_of_impl<T, std::index_sequence<I...>, Args...> { static constexpr size_t value = (size_t(-1) OP ... OP (std::is_same_v<T, Args> ? I : size_t(-1))); };
  • 70. 35/38 Поиск номера типа в списке в C++17 Реализация оператора свертки template<typename T, typename Fn> struct fold_arg { T value; constexpr fold_arg(T const& v): value{v} {} constexpr explicit operator T() const { return value; } constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const { return Fn{}(value, a.value); } };
  • 71. 35/38 Поиск номера типа в списке в C++17 Реализация оператора свертки template<typename T, typename Fn> struct fold_arg { T value; constexpr fold_arg(T const& v): value{v} {} constexpr explicit operator T() const { return value; } constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const { return Fn{}(value, a.value); } }; struct min { template<typename T> constexpr T const& operator ()(T const& lhs, T const& rhs) const { return lhs < rhs ? lhs : rhs; } }; using index = fold_arg<size_t, min>;
  • 72. 35/38 Поиск номера типа в списке в C++17 Реализация оператора свертки template<typename T, typename Fn> struct fold_arg { T value; constexpr fold_arg(T const& v): value{v} {} constexpr explicit operator T() const { return value; } constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const { return Fn{}(value, a.value); } }; struct min { template<typename T> constexpr T const& operator ()(T const& lhs, T const& rhs) const { return lhs < rhs ? lhs : rhs; } }; using index = fold_arg<size_t, min>; template<typename T, size_t... I, typename... Args> struct index_of_impl<T, std::index_sequence<I...>, Args...> { static constexpr size_t value = static_cast<size_t>((index(-1) << ... << index(std::is_same_v<T, Args> ? I : -1))); };
  • 73. 36/38 Поиск номера типа в списке в C++17 Реализация оператора свертки (easy) enum index: size_t {}; constexpr index operator << (index lhs, index rhs) { return lhs < rhs ? lhs : rhs; } template<typename T, size_t... I, typename... Args> struct index_of_impl<T, std::index_sequence<I...>, Args...> { static constexpr size_t value = (index(-1) << ... << index(std::is_same_v<T, Args> ? I : -1)); };
  • 74. 37/38 Поиск номера типа в списке в C++17 Сравнительная скорость компиляции 0 10 20 30 40 50 60 70 80 90 100 0.2 0.4 0.6 0.8 1 1.2 1.4 Позиция искомого типа t,c C++11/14 Clang C++11/14 GCC C++17 Clang C++17 GCC
  • 75. 38/38 Забудьте все, чему вас учили? Q: Как вывести последовательность аргументов с разделителем? A1: Решите задачу рекурсивно template<typename T> void print_list(T t) { std::cout << t << std::endl; } template<typename T, typename U, typename... Tail> void print_list(T t, U u, Tail... tail) { std::cout << t << ”, ”; return print_list(u, tail...); } A2: Используйте fold expressions, они быстрее! template<size_t... I, typename... Ts> void print_list_impl(std::index_sequence<I...>, Ts... args) { (..., (std::cout << args << (I + 1 < sizeof...(Ts) ? ”, ” : ””))); } template<typename... Ts> void print_list(Ts... args) { return print_list_impl(std::index_sequence_for<Ts...>{}, args...); }