Skip to content
Snippets Groups Projects
Commit 9a1ad86f authored by Praetorius, Simon's avatar Praetorius, Simon
Browse files

presentation updated

parent 0da9905d
Branches master
No related tags found
No related merge requests found
...@@ -16,526 +16,10 @@ ...@@ -16,526 +16,10 @@
<!-- <script type="text/javascript" src="../external/MathJax/MathJax.js?config=TeX-AMS_HTML"></script> --> <!-- <script type="text/javascript" src="../external/MathJax/MathJax.js?config=TeX-AMS_HTML"></script> -->
</head> </head>
<body> <body>
<textarea id="source">
class: center, middle
# Variadic XXX
## functions, templates, macros, operators, ...
Simon Praetorius *simon.praetorius@tu-dresden.de*
*Institute for Scientific Computing*<br />
*Technische Universität Dresden*
---
# Motivating Example
## Type-Safe *printf*
Old C-style:
```c++
printf("Hello %s, %d is less than %f\n", "Bob", 7, 8.5);
```
New `C++` (17) style:
```c++
std::cout << "Hello %s, %d is less than %f\n"_format("Bob", 7, 8.5);
```
- Type-safe
- Compiler-error if types are not convertible in format specifier
- Python-like syntax.
```python
print "Hello {}, {:d} is less than {:f}\n".format("Bob", 7, 8.5)
```
---
# Variadic functions
Functions that take a variable number of arguments
- Ellipsis (`...`) as last argument (comma is optional)
- number of passed arguments must be known
- function macros `va_start`, `va_arg`, `va_list`, `va_end` to access the args
- Undefined behavior if something is not correct (wrong type given, read above the end, ...)
- **variadic parameters have the lowest rank in overload resolution**
```
int sum_f(int count, ...)
{
int result = 0;
va_list args;
va_start(args, count);
for (int i = 0; i < count; ++i) {
result += va_arg(args, int);
}
va_end(args);
return result;
}
std::cout << sum_f(4, 25, 25, 50, 50) << '\n';
```
---
# Variadic macros
Use Ellipsis (...) and `__VA_ARGS__` to define a variadic macro and expand the
variadic arguments
Example: Pidgeonhole principle to count number of passed arguments
```c++
#define COUNT_ARGS(...) PP_NARG_IMPL(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_IMPL(...) PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define PP_RSEQ_N() 10,9,8,7,6,5,4,3,2,1,0
```
Implementation of varidatic function without `count` argument:
```c++
#define SUM(...) sum_f(COUNT_ARGS(__VA_ARGS__), __VA_ARGS__)
```
- Trick introduced in [comp.std.c forum](https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s)
---
# Variadic templates
Templates that take a variable number of template type parameters
### Pre `C++11`:
- Specify each template separately
```c++
template <class T0> int sum_t(T0 a0);
template <class T0, class T1> int sum_t(T0 a0, T1 a1);
template <class T0, class T1, class T2> int sum_t(T0 a0, T1 a1, T2 a2);
```
- Use Macro-hacks to generate a large number of overloads with different nr. of types:
(may be automatized with `Boost.Preprocessor`)
```c++
#define SUM_FUNCTION(z, n, _) \
template< BOOST_PP_ENUM_PARAMS_Z(z, n, class T) > \
int sum_t( BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, a) );
BOOST_PP_REPEAT_FROM_TO(1, 11, SUM_FUNCTION, nil)
```
---
# Variadic templates
Templates that take a variable number of template type parameters
### From `C++11`:
- Ellipsis mark parameter packs (list of parameters)
```
template <class... Ts> // types
template <int... Is> // non-type parameters
template <template <class T> class... Us> // template template parameters
// Function parameter packs (expand types in argument list)
template <class... Ts> sum_t(Ts... as);
```
- Variadic parameters can be mixed with classical parameters
- No runtime-overhead due to variadic argument list
- Empty packs are allowed!
--
### Problems:
- Individual types and arguments can not be accessed (directly)
- Work with variadic templates is often based on recursion
- Break condition is based on fixed nr. of arguments (or empty list)
---
# Recursion with variadic templates
## Example: Access the i'th tuple element
- Tuple as list of types:
```
template <class... Ts> struct Tuple {};
```
- Get the ith element using template specialization and recursion:
```
template <int i, class List> Get; // primary template
template <int i, class T0, class... Ts> // specialization for tuples
struct Get<i, Tuple<T0, Ts...>> : public Get<i-1, Tuple<Ts...>> {};
template <class T0, class... Ts> // break condition
struct Get<0, Tuple<T0, Ts...>> { using type = T0; };
```
- Usage:
```
using T = Tuple<int, double, float, long>;
using T1 = typename Get<1, T>::type; // = double
```
---
# Parameter pack expansion
## Only use of parameter pack: expand it!
- Expansion: Name of the pack + Ellipsis
```
template <class... Ts>
auto sum_t(Ts... args) {
// expand template parameter pack `Ts` first,
// then function parameter pack `args`
std::tuple<Ts...> argsTuple{args...};
// ...
}
sum(3.5, 1, 4u);
```
- Corresponds to
```
auto sum_t(double args_1, int args_2, unsigned args_3) {
std::tuple<double, int, unsigned> argsTuple{args_1, args_2, args_3};
//...
}
```
---
# sizeof...
- Return number of elements in a parameter pack.
- Works with types and arguments
```
template <class... Ts>
auto sum_t(Ts... args) {
return sum_f(sizeof...(args), args...);
}
```
---
# initializer lists
- `initializer_list` = tuple of elements of the **same type**
- `auto` with `{}` gives `initializer_list`:
```
auto l = {1,2,3,4,5}; // -> std::initializer_list<int>
```
- More simple implementation of `sum(...)` function:
```c++
template <class... Ts>
int sum_t(Ts... ts) {
int result = 0;
for (auto el : {ts...}) // all elements in initializer_list
result += el;
return result;
}
```
---
# Apply function to elements:
- Example: Append all argument to a vector
```c++
template <class... Ts>
auto make_vec(Ts... ts) {
using T = std::common_type_t<Ts...>;
std::vector<T> vec;
auto x = {(vec.push_back(ts),0)...}; // warning: unused variable
return vec;
}
```
--
- Better: use `initializer_list` explicitly:
```
template <class... Ts>
auto make_vec(Ts... ts) {
using T = std::common_type_t<Ts...>;
std::vector<T> vec;
(void)std::initializer_list<int>{
((void)vec.push_back(ts),0)...
};
return vec;
}
```
---
# Apply function to elements:
- Generic implementation:
```
template <class F, class... Ts>
void apply(F f, Ts... ts) {
(void)std::initializer_list<int>{
((void)f(ts),0)...
};
}
template <class... Ts>
auto make_vec(Ts... ts) {
using T = std::common_type_t<Ts...>;
std::vector<T> vec;
apply([&vec](T t) { vec.push_back(t); }, ts...);
return vec;
}
```
---
# Fold expressions
Instead of looping over all elements in a initializter_list or tuple, to add the values up,
apply the `operator+` directly to all elements:
```
template <class... Ts>
auto sum_o(Ts... ts) {
return (ts + ...); // brackets () are necessary
}
```
- (unary) right fold: `(args + ...) = (arg1 + (arg2 + (... + argN)))`
- (unary) left fold: `(... | args) = (((arg1 | arg2) | ... ) | argN)`
- Binary (right) fold: `(args * ... * X) = (arg1 * ( ... * (argN * X))`
- Special conditions for empty packs (not allowed for all operators)
---
# Fold expressions
Instead of looping over all elements in a initializter_list or tuple, to add the values up,
apply the `operator+` directly to all elements:
```
template <class... Ts>
auto sum_o(Ts... ts) {
return (ts + ...); // brackets () are necessary
}
```
- Can be combined with function call:
```
template <class T> T sqr(T t) { return t*t; }
template <class... Ts>
auto norm(Ts... ts) {
return std::sqrt((sqr(ts) + ...)); // additional brackets () are necessary
}
```
--
- Also the `,`-operator can be used, instead of initializer lists
```
template <class F, class... Ts>
void apply(F f, Ts... ts)
{
((void)f(ts), ...);
}
```
---
# User defined (string) literals
- User defined literals (available in different form, here template variadic form):
- Transform sequence of chars to value
```
template <char... Chars>
auto operator""_kg() { ... }
```
- Usage:
```
auto weight = 100_kg + 10_kg;
```
- In `C++17`: User defined string literals:
```
template <class CharT, CharT... Chars>
auto operator""_format() { ... }
```
- Usage:
```
auto s = "this is a string"_format;
```
---
# User defined literals
## Example: Generate integral constants
```c++
constexpr unsigned char2digit(const char c) {
return unsigned(c) - unsigned('0'); // maybe provide explicit mapping
}
template <int N>
constexpr int chars2int(const char (&arr)[N]) {
int result=0, power=1;
for (int i = 0; i < N; ++i) {
result+= char2digit( arr[N - 1 - i] ) * power;
power *= 10;
}
return result;
}
template <char... digits>
constexpr auto operator"" _c() {
return std::integral_constant<int,chars2int<sizeof...(digits)>({digits...})>{};
}
// Usage:
auto i = 123_c; // i = std::integral_constant<int, 123>
```
- See [Boost.hana](www.boost.org/doc/libs/release/libs/hana)
---
# Finally: Type-safe *printf*
1. **Idea:** Use user defined string literal to transform format string into char sequence
2. Parse char sequence and add all pairs `%X` with `X` a format specifier, to a list of types, i.e.
`%d -> int`
3. Wrapping call to `printf` in function with argument types given by this calculated list of types.
4. Finally call `std::printf`. Now all types are checked.
---
# Finally: Type-safe *printf*
- Helper class that stored Types:
```c++
template <class... Ts> struct Types {
template <class T> using push_front = Types<T, Ts...>;
template <template <class...> class F> using apply = F<Ts...>;
};
```
--
- Generator for class `Types` from char sequence:
```c++
template <char... Chars>
struct FormatTypes : Types< /*implementation detail*/ >;
```
--
- Functor that calls `printf`, parametrized with argument types:
```c++
template <class... Args>
struct Formatter {
int operator()(Args... args) {
return std::printf(str, args...);
}
const char *str;
};
```
---
# Finally: Type-safe *printf*
- The actual string literal:
```
template <class CharT, CharT... Chars>
typename FormatTypes<Chars...>::template apply<Formatter>
operator""_printf() {
static const char str[] = { Chars..., 0 };
return { str };
}
```
- Idea from: N3599 (Proposal for C++17 standard)
- Some material from [Arne-Metz Blog](https://arne-mertz.de/2016/11/modern-c-features-variadic-templates)
---
class: center, middle
# Thank you for your attention
---
# Appendix:
- Simplified implementation
```c++
// Select a type from a format character.
template<char K> struct format_type_impl;
template<> struct format_type_impl<'d'> { using type = int; };
template<> struct format_type_impl<'f'> { using type = double; };
template<> struct format_type_impl<'s'> { using type = const char *; };
// ...
template<char K> using format_type = typename format_type_impl<K>::type;
// Build a tuple of types from a format string.
template<char... Chars>
struct FormatTypes;
template<>
struct FormatTypes<> : Types<> {};
template<char Char, char... Chars>
struct FormatTypes<Char, String...> : FormatTypes<Chars...> {};
template<char... Chars>
struct FormatTypes<'%', '%', String...>
: FormatTypes<Chars...> {};
template<char Fmt, char... Chars>
struct FormatTypes<'%', Fmt, String...>
: FormatTypes<String...>::template push_front<format_type<Fmt>> {};
```
- Full implementation can be found in the GitLab repository
</textarea>
<script src="../external/remark/out/remark.min.js" type="text/javascript"></script> <script src="../external/remark/out/remark.min.js" type="text/javascript"></script>
<script type="text/javascript"> <script type="text/javascript">
var slideshow = remark.create({ var slideshow = remark.create({
sourceUrl: 'file:///home/spraetor/material/cpp-shots/vortrag_05-09/variadic.md',
ratio: "4:3", ratio: "4:3",
highlightLanguage: "cpp" highlightLanguage: "cpp"
}); });
...@@ -551,7 +35,7 @@ struct FormatTypes<'%', Fmt, String...> ...@@ -551,7 +35,7 @@ struct FormatTypes<'%', Fmt, String...>
// return(elem.SourceElement()); // return(elem.SourceElement());
// }).parent().addClass('has-jax'); // }).parent().addClass('has-jax');
// }); // });
// //
// MathJax.Hub.Configured(); // MathJax.Hub.Configured();
</script> </script>
</body> </body>
......
class: center, middle
# Variadic XXX
## functions, templates, macros, operators, ...
Simon Praetorius *simon.praetorius@tu-dresden.de*
*Institute for Scientific Computing*<br />
*Technische Universität Dresden*
---
# Motivating Example
## Type-Safe *printf*
Old C-style:
```c++
printf("Hello %s, %d is less than %f\n", "Bob", 7, 8.5);
```
New type-safe `printf` with `C++` (17) style:
```c++
std::cout << "Hello %s, %d is less than %f\n"_format("Bob", 7, 8.5);
```
- Type-safe
- Compiler-error if types are not convertible in format specifier
- Python-like syntax.
```python
print "Hello {}, {:d} is less than {:f}\n".format("Bob", 7, 8.5)
```
---
# Variadic functions
Functions that take a variable number of arguments
- Ellipsis (`...`) as last argument (comma is optional)
- number of passed arguments must be known
- function macros `va_start`, `va_arg`, `va_list`, `va_end` to access the args
- Undefined behavior if something is not correct (wrong type given, read above the end, ...)
- **variadic parameters have the lowest rank in overload resolution**
```
int sum_f(int count, ...)
{
int result = 0;
va_list args;
va_start(args, count);
for (int i = 0; i < count; ++i) {
result += va_arg(args, int);
}
va_end(args);
return result;
}
std::cout << sum_f(4, 25, 25, 50, 50) << '\n';
```
---
# Variadic macros
Use Ellipsis (...) and `__VA_ARGS__` to define a variadic macro and expand the
variadic arguments
Example: Pidgeonhole principle to count number of passed arguments
```c++
#define COUNT_ARGS(...) PP_NARG_IMPL(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_IMPL(...) PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define PP_RSEQ_N() 10,9,8,7,6,5,4,3,2,1,0
```
Implementation of varidatic function without `count` argument:
```c++
#define SUM(...) sum_f(COUNT_ARGS(__VA_ARGS__), __VA_ARGS__)
```
- Trick introduced in [comp.std.c forum](https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s)
---
# Variadic templates
Templates that take a variable number of template type parameters
### Pre `C++11`:
- Specify each template separately
```c++
template <class T0> int sum_t(T0 a0);
template <class T0, class T1> int sum_t(T0 a0, T1 a1);
template <class T0, class T1, class T2> int sum_t(T0 a0, T1 a1, T2 a2);
```
- Use Macro-hacks to generate a large number of overloads with different nr. of types:
(may be automatized with `Boost.Preprocessor`)
```c++
#define SUM_FUNCTION(z, n, _) \
template< BOOST_PP_ENUM_PARAMS_Z(z, n, class T) > \
int sum_t( BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, a) );
BOOST_PP_REPEAT_FROM_TO(1, 11, SUM_FUNCTION, nil)
```
---
# Variadic templates
Templates that take a variable number of template type parameters
### From `C++11`:
- Ellipsis mark parameter packs (list of parameters)
```
template <class... Ts> // types
template <int... Is> // non-type parameters
template <template <class T> class... Us> // template template parameters
// Function parameter packs (expand types in argument list)
template <class... Ts> sum_t(Ts... as);
```
- Variadic parameters can be mixed with classical parameters
- No runtime-overhead due to variadic argument list
- Empty packs are allowed!
--
### Challenges:
- Individual types and arguments can not be accessed (directly)
- Work with variadic templates is often based on recursion
- Break condition is based on fixed nr. of arguments (or empty list)
---
# Recursion with variadic templates
## Example: Access the i'th tuple element
- Tuple as list of types:
```
template <class... Ts> struct Tuple {};
```
- Get the ith element using template specialization and recursion:
```
template <int i, class List> Get; // primary template
template <int i, class T0, class... Ts> // specialization for tuples
struct Get<i, Tuple<T0, Ts...>> : public Get<i-1, Tuple<Ts...>> {};
template <class T0, class... Ts> // break condition
struct Get<0, Tuple<T0, Ts...>> { using type = T0; };
```
- Usage:
```
using T = Tuple<int, double, float, long>;
using T1 = typename Get<1, T>::type; // = double
```
---
# Parameter pack expansion
## Only use of parameter pack: expand it!
- Expansion: Name of the pack + Ellipsis
```
template <class... Ts>
auto sum_t(Ts... args) {
// expand template parameter pack `Ts` first,
// then function parameter pack `args`
std::tuple<Ts...> argsTuple{args...};
// ...
}
sum(3.5, 1, 4u);
```
- Corresponds to
```
auto sum_t(double args_1, int args_2, unsigned args_3) {
std::tuple<double, int, unsigned> argsTuple{args_1, args_2, args_3};
//...
}
```
---
# sizeof...
- Return number of elements in a parameter pack.
- Works with types and arguments
```
template <class... Ts>
auto sum_t(Ts... args) {
return sum_f(sizeof...(args), args...);
}
```
---
# initializer lists
- `initializer_list` = tuple of elements of the **same type**
- `auto` with `{}` gives `initializer_list`:
```
auto l = {1,2,3,4,5}; // -> std::initializer_list<int>
```
- More simple implementation of `sum(...)` function:
```c++
template <class... Ts>
int sum_t(Ts... ts) {
int result = 0;
for (auto el : {ts...}) // all elements in initializer_list
result += el;
return result;
}
```
---
# Apply function to elements:
- Example: Append all argument to a vector
```c++
template <class... Ts>
auto make_vec(Ts... ts) {
using T = std::common_type_t<Ts...>;
std::vector<T> vec;
auto x = {(vec.push_back(ts),0)...}; // warning: unused variable
return vec;
}
```
--
- Better: use `initializer_list` explicitly:
```
template <class... Ts>
auto make_vec(Ts... ts) {
using T = std::common_type_t<Ts...>;
std::vector<T> vec;
(void)std::initializer_list<int>{
((void)vec.push_back(ts),0)...
};
return vec;
}
```
---
# Apply function to elements:
- Generic implementation:
```
template <class F, class... Ts>
void apply(F f, Ts... ts) {
(void)std::initializer_list<int>{
((void)f(ts),0)...
};
}
template <class... Ts>
void print(Ts... ts) {
apply([](T t) { std::cout << t << ' '; }, ts...);
}
```
--
- or a bit more stylish:
```
template <class T0, class... Ts>
void print(T0 t0, Ts... ts) {
std::cout << "[" << t0;
apply([](T t) { std::cout << ", " << t; }, ts...);
std::cout << "]\n";
}
```
---
class: center, middle
# Part II
## fold expressions, user defined literals, printf
---
# Fold expressions
Instead of looping over all elements in an `initializter_list` or `tuple` to add the values up,
apply the `operator+` directly to all elements:
```
template <class... Ts>
auto sum_o(Ts... ts) {
return (ts + ...); // brackets () are necessary
}
```
- unary right fold: `(args + ...) = (arg1 + (arg2 + (... + argN)))`
- unary left fold: `(... | args) = (((arg1 | arg2) | ... ) | argN)`
- binary right fold: `(args * ... * X) = (arg1 * ( ... * (argN * X))`
- binary left fold: analogously
- Special conditions for empty packs (not allowed for all operators)
---
# Fold expressions
Instead of looping over all elements in an `initializter_list` or `tuple` to add the values up,
apply the `operator+` directly to all elements:
```
template <class... Ts>
auto sum_o(Ts... ts) { return (ts + ...); }
```
- Can be combined with function call:
```
template <class T> T sqr(T t) { return t*t; }
template <class... Ts>
auto norm(Ts... ts) {
return std::sqrt((sqr(ts) + ...)); // additional brackets () are necessary
}
```
--
- Also the `,`-operator can be used, instead of initializer lists
```
template <class F, class... Ts>
void apply(F f, Ts... ts) {
((void)f(ts), ...);
}
```
---
# User defined (string) literals
- User defined literals (available in different form, here template variadic form):
- Transform sequence of chars to value
```
template <char... Chars>
auto operator""_kg() { ... }
```
- Usage:
```
auto weight = 100_kg + 10_kg;
```
- In `C++17`: User defined string literals:
```
template <class CharT, CharT... Chars>
auto operator""_format() { ... }
```
- Usage:
```
auto s = "this is a string"_format;
```
---
# User defined literals
## Example: Generate integral constants
```c++
constexpr unsigned char2digit(const char c) {
return unsigned(c) - unsigned('0'); // maybe provide explicit mapping
}
template <int N>
constexpr int chars2int(const char (&arr)[N]) {
int result=0, power=1;
for (int i = 0; i < N; ++i) {
result+= char2digit( arr[N - 1 - i] ) * power;
power *= 10;
}
return result;
}
template <char... digits>
constexpr auto operator"" _c() {
return std::integral_constant<int,chars2int<sizeof...(digits)>({digits...})>{};
}
// Usage:
auto i = 123_c; // i = std::integral_constant<int, 123>
```
- See [Boost.hana](www.boost.org/doc/libs/release/libs/hana)
---
# Finally: Type-safe *printf*
1. **Idea:** Use user defined string literal to transform format string into char sequence
2. Parse char sequence and add all pairs `%X` with `X` a format specifier, to a list of types, i.e.
`%d -> int`
3. Wrapping call to `printf` in function with argument types given by this calculated list of types.
4. Finally call `std::printf`. Now all types are checked.
---
# Finally: Type-safe *printf*
- Helper class that stored Types:
```c++
template <class... Ts> struct Types {
template <class T> using push_front = Types<T, Ts...>;
template <template <class...> class F> using apply = F<Ts...>;
};
```
--
- Generator for class `Types` from char sequence:
```c++
template <char... Chars>
struct FormatTypes : Types< /*implementation detail*/ >;
```
--
- Functor that calls `printf`, parametrized with argument types:
```c++
template <class... Args>
struct Formatter {
int operator()(Args... args) {
return std::printf(str, args...);
}
const char *str;
};
```
---
# Finally: Type-safe *printf*
- The actual string literal:
```
template <class CharT, CharT... Chars>
typename FormatTypes<Chars...>::template apply<Formatter>
operator""_printf() {
static const char str[] = { Chars..., 0 };
return { str };
}
```
- Idea from: N3599 (Proposal for C++17 standard)
- Some material from [Arne-Metz Blog](https://arne-mertz.de/2016/11/modern-c-features-variadic-templates)
---
class: center, middle
# Thank you for your attention
---
count: false
# Appendix:
- Simplified implementation
```c++
// Select a type from a format character.
template<char K> struct format_type_impl;
template<> struct format_type_impl<'d'> { using type = int; };
template<> struct format_type_impl<'f'> { using type = double; };
template<> struct format_type_impl<'s'> { using type = const char *; };
// ...
template<char K> using format_type = typename format_type_impl<K>::type;
// Build a tuple of types from a format string.
template<char... Chars>
struct FormatTypes;
template<>
struct FormatTypes<> : Types<> {};
template<char Char, char... Chars>
struct FormatTypes<Char, String...> : FormatTypes<Chars...> {};
template<char... Chars>
struct FormatTypes<'%', '%', String...>
: FormatTypes<Chars...> {};
template<char Fmt, char... Chars>
struct FormatTypes<'%', Fmt, String...>
: FormatTypes<String...>::template push_front<format_type<Fmt>> {};
```
- Full implementation can be found in the GitLab repository
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment