Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
cpp-shots
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Container Registry
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Ansgar Burchardt
cpp-shots
Commits
9a1ad86f
Commit
9a1ad86f
authored
7 years ago
by
Praetorius, Simon
Browse files
Options
Downloads
Patches
Plain Diff
presentation updated
parent
0da9905d
Branches
master
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
vortrag_05-09/variadic.html
+2
-518
2 additions, 518 deletions
vortrag_05-09/variadic.html
vortrag_05-09/variadic.md
+529
-0
529 additions, 0 deletions
vortrag_05-09/variadic.md
with
531 additions
and
518 deletions
vortrag_05-09/variadic.html
+
2
−
518
View file @
9a1ad86f
...
@@ -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>
...
...
This diff is collapsed.
Click to expand it.
vortrag_05-09/variadic.md
0 → 100644
+
529
−
0
View file @
9a1ad86f
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
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment