How to create std::initializer_list with integer_sequence? - c++

I have an integer_sequence and I need to create std::initializer_list for constructor of std::map. The idea is below:
template<uint8_t... Indices>
static constexpr auto GenerateRegMap(std::integer_sequence<uint8_t, Indices...>)
-> std::initializer_list<std::pair<uint8_t const, uint8_t>>
{
return {{Indices, 0}...};
}
constexpr auto s = std::integer_sequence<uint8_t, 2, 4, 5, 7, 8, 10, 11, 13, 15>{};
std::map<uint8_t, uint8_t> m (GenerateRegMap(s));
std::cout << m.size() << std::endl;
for (const auto &pair : m)
{
std::cout << static_cast<int>(pair.first) << " has value " << static_cast<int>(pair.second) << std::endl;
}
There std::initializer_list is normally generated, but it cannot be returned: it uses local storage. I cannot imagine how to write the content of GenerateRegMap in place.
Really m is private field of some class and it would be nice to init it in the member initializer list.
Why don't I return std::map<uint8_t, uint8_t> from GenerateRegMap? Because I've got an error: a constexpr function cannot have a nonliteral return type.

Instead of returning a local initializer_list from the GenerateRegMap, you can use C++14 variable template to generate the initializer_list directly
template<uint8_t... Indices>
constexpr std::initializer_list<std::pair<uint8_t const, uint8_t>> my_list =
{{Indices, 0}...};
constexpr auto s = my_list<2, 4, 5, 7, 8, 10, 11, 13, 15>;
std::map<uint8_t, uint8_t> m(s);
If you want the variable template to take integer_sequence as the template type, you can use template partial specialization to get values of integer_sequence to initialize initializer_list, for example
using L = std::initializer_list<std::pair<uint8_t const, uint8_t>>;
template<class Seq>
constexpr L my_list = {};
template<class Seq>
constexpr L my_list<const Seq> = my_list<Seq>;
template<uint8_t... Indices>
constexpr L my_list<std::integer_sequence<uint8_t, Indices...>> = {{Indices, 0}...};
This enables you to generate initializer_list with pre-defined integer_sequence
constexpr auto s = std::integer_sequence<uint8_t, 2, 4, 5, 7, 8, 10, 11, 13, 15>{};
std::map<uint8_t, uint8_t> m(my_list<decltype(s)>);

Related

Why isn't initializer_list being deduced as an argument for a concept it matches?

I was playing with concepts (I wanted to give this question a C++ey answer) and came across a behavior with std::initializer_list that puzzles me. Although the following code works:
#include<utility>
#include <limits>
#include<iostream>
#include <type_traits>
#include <vector>
#include <initializer_list>
#include <string_view>
template <class C>
struct iterable_sfinae
{
template <class U>
static auto check(U*u)->decltype(u->begin()!=u->end());
template <class...>
static void check(...);
static constexpr bool value = !std::is_void_v<decltype(check((C*)0))>;
};
template <class C>
static constexpr bool is_iterable_v = iterable_sfinae<C>::value;
template <class C>
concept Iterable = iterable_sfinae<C>::value;
template <class N>
constexpr bool is_numeric_v = std::numeric_limits<std::decay_t<N>>::is_specialized;
template <class N>
concept Number = std::numeric_limits<std::decay_t<N>>::is_specialized;
// alternative 2
template <Iterable C>
auto findmax(C const &c)
requires is_numeric_v<decltype(*(c.begin()))>
{
auto rv = std::numeric_limits<std::decay_t<decltype(*(c.begin()))>>::lowest();
for (auto e: c)
if (e > rv) rv = e;
return rv;
}
int main() {
std::vector<int> v = {9, 255, 86, 4, 89, 6, 1, 422, 5, 29};
std::cout << "using vector via concept: " << findmax(v) << '\n';
std::initializer_list<int> il = {9, 255, 86, 4, 89, 6, 1, 422, 5, 29};
std::cout << "using initializer_list: " << findmax(il) << '\n';
std::boolalpha(std::cout);
std::cout << "initializer_list is iterable: " << is_iterable_v<std::initializer_list<int>> << '\n';
}
output:
using vector via concept: 422
using initializer_list: 422
initializer_list is iterable: true
But if I want to add
// alternative 3
std::cout << "using braced-init-list: " << findmax({9, 255, 86, 4, 89, 6, 1, 422, 5, 29}) << '\n';
the program won't compile. The reason given by MSVC, gcc, and clang is "Couldn't deduce template argument". But if you add an overload for std::initializer_list, it does:
template <Number N>
auto findmax(std::initializer_list<N> const &c)
{
auto rv = std::numeric_limits<std::decay_t<decltype(*(c.begin()))>>::min();
for (auto e: c)
if (e > rv) rv = e;
return rv;
}
Output:
using braced-init-list: 422
Now, cppreference says (at https://en.cppreference.com/w/cpp/utility/initializer_list) that
A std::initializer_list object is automatically constructed when...a braced-init-list is used...as a function call argument, and the corresponding assignment operator/function accepts an std::initializer_list parameter.
initializer_list matches the Iterable concept, and the concept-based overload does, however, accept the explicitly-declared initializer_list variable.
, and this implies to me that the bare braced-init-list should generate an iniitalizer_list and select the concept-based overload. But it doesn't.
The concept-based overload accepts a std::initializer_list parameter, so why can't that overload deduce and create an initializer_list argument from the bare braced-init-list?
The full code is here,
So I found a workaround that underscores how weird this all is. If we replace the findmax specialization for initializer_list with
template <class C>
inline std::initializer_list<C> const &make_il(std::initializer_list<C> const &i)
{
return i;
}
we can clue in the main template to accept
// alternative 3
std::cout << "using make_il: " << findmax(make_il({9, 255, 86, 4, 89, 6, 1, 422, 5, 29})) << '\n';
The "magic" of creating an initializer_list from a braced-init-list seems to only happen when initializer_list is mentioned explicitly, and not when looking up a generic template argument (even if it is constrained). Why is that?
The expression {9, 255, 86, 4, 89, 6, 1, 422, 5, 29} is not implicitly a std::initializer_list.
Its meaning and its type is intended to be contextually inferred from its usage.
If you pass it to a function that expects a std::initializer_list<int>, then that's what it is.
If you pass it to a function that expects a std::array<int,10>, then that's what it is.
If you pass it to a function that expects an aggregate type, then that's what it is.
You, however, are passing it as a deduced template argument template <Iterable C> auto findmax(C const &c)
findmax({9, 255, 86, 4, 89, 6, 1, 422, 5, 29}) won't compile because the function relies on the call site to specify what type is being passed, and your call site is depending on the function to specify what type is being passed.
std::initializer_list isn't mentioned at either location, so the compiler does not know that you want std::initializer_list.
{..} has no type and can only be deduced as const T(&)[N] or (const ref of) std::initializer_list<T>.
Type expected from findmax is not of the above form, it accepts const T&
template <Iterable C> auto findmax(C const &c).

C++ variadic tempates with default argument

I'm trying to write a template that will measure the execution time of any function:
template<typename Func, typename... Args>
void measure(const Func& f, Args... args);
I have a templated function with a default parameter compare:
template<typename T, typename Compare = std::function<bool(T, T)>>
void mergeSort(std::vector<T>& v, const Compare& compare = std::less<T>());
Then I try to measure the sort time like this:
std::vector<int> v = { 3, 5, 7, 8, 3, 24, 7, 2, 8, 0, 8 };
measure(mergeSort<int>, v);
But getting compile error: 'Func (__cdecl &)': too few arguments for call.
Running this:
std::vector<int> v = { 3, 5, 7, 8, 3, 24, 7, 2, 8, 0, 8 };
measure(mergeSort<int>, v, std::less<int>());
all works as it should.
Is there a way to make the first version workable?
Wrap the function in a lambda with a single parameter: [](auto &vec){mergeSort<int>(vec);}.
Also, typename Compare = std::function<bool(T, T)> is a bad idea, since the type erasure in std::function has some overhead. You should just use std::less<T> as the type.

Accessing std array element passed as non-type template parameter gives a non compile-time constant value on msvc

I'm trying to pass a static constexpr std::array as a non-type template parameter. However accessing an array element gives a non compile-time constant value on msvc compiler.
MSVC (16.4.5, 16.5) fails to compile the sample, but clang (9, 10) and gcc (9.1, 9.2, 9.3) compile fine.
What is the correct behaviour according to the current standards (>=c++20)? Is this a MSVC compiler bug?
Here is the sample https://godbolt.org/z/7YsnAF
#include <array>
#include <iostream>
template <typename T, size_t N, const std::array<T, N> & arr, typename Fn, size_t Index = 0>
void iterate_static_array(Fn && fn) {
static constexpr T e = arr[Index];
fn.template operator()<e>();
if constexpr (Index + 1 < N) {
iterate_static_array<T, N, arr, Fn, Index + 1>(std::forward<Fn>(fn));
}
}
int main() {
static constexpr std::array<int, 10> values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
iterate_static_array<int, 10, values>([]<int e>() {
std::cout << "compile-time e = " << e << "\n";
});
}
I would say it is a bug.
The only C++20 feature you are using is the template syntax for lambdas, which according to Microsoft's documentation should already be supported since VS 2019 16.2.
If you remove that and test e.g. the following program:
#include <array>
#include <iostream>
template <typename T, size_t N, const std::array<T, N> & arr, typename Fn, size_t Index = 0>
void iterate_static_array(Fn && fn) {
static constexpr T e = arr[Index];
fn.operator()();
if constexpr (Index + 1 < N) {
iterate_static_array<T, N, arr, Fn, Index + 1>(std::forward<Fn>(fn));
}
}
int main() {
static constexpr std::array<int, 10> values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
iterate_static_array<int, 10, values>([]() {
});
}
then there is no C++20 feature left and since C++17 this should compile, because you are allowed to pass a reference to an object with static storage duration as template argument and its usage as arr[Index] is also a constant expression, as the reference was initialized with a constant expression and the referenced object is also constant initialized.
Yet MSVC still gives the same error message, whether in c++2a or c++17 mode. Both GCC and Clang compile this in C++17 mode.

Integer sequences implementation C++

I have been reading up on compile time sequences and I have a hard time understanding how the program below works. Can anyone out there explain the program below in detail? Thanks!
#include<iostream>
template<int...>
struct ints
{
};
template<int N, int...args>
struct rev_seq : rev_seq<N - 1, 2 * args..., 1> {};
template<int...args>
struct rev_seq<0, args...>
{
using type = ints<args...>;
};
template<int N>
using RS = typename rev_seq<N + 1>::type;
template<int... args>
void fU(ints<args...>&& s)
{
for (const auto& i : { args... }) std::cout << i << " ";
std::cout << std::endl;
}
int main()
{
fU(RS<5>());
}
RS<2>() instantiates rev_seq<2, 2>::type
rev_seq<2, 2>::type is the primary template (not the specialized one) for rev_seq:
template<int C, int N, int... Is>
struct rev_seq : rev_seq<C - 1, N, N - C, Is...>{}
This is a recursive declaration, so it derives from a version of itself like so:
rev_seq<2, 2, (empty int... Is pack)>
derives from
rev_seq<2-1, 2, 2 - 2>
which is rev_seq<1, 2, 0>
That 0 on the end is part of the int... Is pack on the base class
This again recurses
rev_seq<1, 2, 0>
derives from
rev_seq<1-1, 2, 2-1, 0>
which is rev_seq<0, 2, (1, 0)>
See how the last parameter argument gets appended to the pack?
rev_seq<0, 2, (1, 0)> matches the following template specialization for rev_seq:
template<int N, int... Is>
struct rev_seq<0, N, Is...>
{
using type = ints<N, Is...>;
};
Note that this struct doesn't derive from anything
At this point, the type type in the class becomes
ints<2, 1, 0>
See how the specialization causes us to append N to the front of the sequence?
Finally, we pass the constructed ints to a function:
template<int... Is>
void fU(ints<Is...>&& s)
{
for (auto i : { Is... }) std::cout << i << " ";
std::cout << std::endl;
}
The loop iterates over the integers
for (auto i : { Is... })
Here { Is...} is a pack expansion, creating an initializer list for us to iterate over. It's fun to note that this is one of the very few places you can just create and use an initializer list, almost all other cases are relegated to matching a std::initializer_list constructor overload for some class (e.g., std::vector)
Assuming you already know the theory behind variadic template, the best approach I may suggest is to "manually unroll" template definitions.
Most of the cases variadic templates exploit a recursive approaches.
Let's try to do this exercise.
The core part is: RS<5>(). That's just an instance of rev_seq<5, 5>::type. Well, let's go deep into rev_seq.
Its declaration:
template<int C, int N, int... Is>
struct rev_seq // ...
So that instance will be "mapped":
template<5, 5, $>
struct rev_seq // ...
where $ is just a placeholder symbol to indicate an empty variadic list.
rev_seq inherits recursively:
template<5, 5, $>
struct rev_seq : rev_seq <4, 5, 0, $> {}
Of course rev_seq <4, 5, 0, $> (i.e., rev_seq<4, 5, {0}>) inherits and so on.
<5, 5, $> ->
<4, 5, {0}> ->
<3, 5, {1, 0}> ->
<2, 5, {2, 1, 0}> ->
<1, 5, {3, 2, 1, 0}> ->
<0, 5, {4, 3, 2, 1, 0}>
When the first template parameter is 0 we stop. Because in that case we have a partial template specialization.
Here you can see the analogy with the "base case" in the recursion strategy.
Therefore, we finally get this inheritance:
struct rev_seq<0, N, Is...>
{
using type = ints<N, Is...>;
};
In your case:
struct rev_seq<0, 5, {4, 3, 2, 1, 0}>
{
using type = ints<5, {4, 3, 2, 1, 0}>;
};
Note that ints is just a variadic list. That is: ints<5, {4, 3, 2, 1, 0}> actually is ints<{5, 4, 3, 2, 1, 0}>.
So, in the end, you are just calling the "print function" with this particular instance of ints:
template<{5, 4, 3, 2, 1, 0}>
void fU(ints<{5, 4, 3, 2, 1, 0}>&& s)
{
for (auto i : { 5, 4, 3, 2, 1, 0 }) std::cout << i << " ";
std::cout << std::endl;
}
Please note that is not a valid C++ syntax, but more just a "graphical representation" aimed to show the recursive process.

How to filter a std::integer_sequence

If I theoretically have a sequence of integers like
std::integer_sequence<int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9>
How can I filter it with some compile-time predicate to get a potentially smaller std::integer_sequence<int, ...>?
For the sake of argument, let's say that I want only the even values,
which leads to the question of "How can I make the following static_assert (or something close) pass?"
static_assert(std::is_same_v<std::integer_sequence<int, 0, 2, 4, 6, 8>,
decltype(FilterEvens(std::integer_sequence<int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9>{}))>,
"Integer sequences should be equal");
This question was inspired by thinking about how we might accomplish removing duplicates between two bitsets (this question), assuming we could represent the bitsets as integer_sequences containing only 0 and 1. Bonus points if you can solve that one in this manner, too
Filtering a sequence is equivalent to transforming a sequence of values into a sequence of sequences of at most one value and then concatenating them. That is, filtering the even values from <0,1,2,3> would be the same as transforming that into the sequence <<0>,<>,<2>,<>> and concatenating to yield <0,2>.
With C++17, this takes remarkably little code. We'll start with our own value and sequence type (you can easily convert a std::integer_sequence to a value_sequence):
template <auto >
struct value { };
template <auto... Vals>
struct value_sequence { };
The reason we use our own is so we can add operators to it. Like +:
template <auto... As, auto... Bs>
constexpr value_sequence<As..., Bs...> operator+(value_sequence<As...>,
value_sequence<Bs...> )
{
return {};
}
We'll use that for concatenation. Next, we add a function to transform a single value into a sequence of zero or one element:
template <auto Val, class F>
constexpr auto filter_single(value<Val>, F predicate) {
if constexpr (predicate(Val)) {
return value_sequence<Val>{};
}
else {
return value_sequence<>{};
}
}
And lastly, we just need our top-level filter to put it all together:
template <auto... Vals, class F>
constexpr auto filter(value_sequence<Vals...>, F predicate) {
return (filter_single(value<Vals>{}, predicate) + ...);
}
Usage from the original example:
constexpr auto evens = filter(
value_sequence<0, 1, 2, 3, 4, 5, 6, 7, 8, 9>{},
[](int i) constexpr { return i%2 == 0; });
How cool is C++17!
Edit 2
After some back and forth on Barry's answer, I've come up with the following answer that merges the concepts and handles some empty-sequence edge cases (Full code):
We are allowed to pass a predicate to a function only if it is a constexpr lambda, as only literal types are allowed in constexpr functions, and normal free-floating functions aren't literal types (although I suppose you could wrap one within your lambda).
Our generic filter function will accept a sequence and a predicate, and return a new sequence. We will use constexpr if to handle empty sequence cases (which also requires the maybe_unused attribute on the predicate, because it's unused) :
template<class INT, INT... b, class Predicate>
constexpr auto Filter(std::integer_sequence<INT, b...>, [[maybe_unused]] Predicate pred)
{
if constexpr (sizeof...(b) > 0) // non empty sequence
return concat_sequences(FilterSingle(std::integer_sequence<INT, b>{}, pred)...);
else // empty sequence case
return std::integer_sequence<INT>{};
}
The Filter function calls FilterSingle for each element in the provided sequence, and concatenates the result of all of them:
template<class INT, INT a, class Predicate>
constexpr auto FilterSingle(std::integer_sequence<INT, a>, Predicate pred)
{
if constexpr (pred(a))
return std::integer_sequence<INT, a>{};
else
return std::integer_sequence<INT>{};
}
To concatenate sequences, the basic approach is thus:
template<typename INT, INT... s, INT... t>
constexpr std::integer_sequence<INT,s...,t...>
concat_sequences(std::integer_sequence<INT, s...>, std::integer_sequence<INT, t...>){
return {};
}
Although because of template expansion we'll have more than 2 sequences a lot of time, so we need a recursive case:
template<typename INT, INT... s, INT... t, class... R>
constexpr auto
concat_sequences(std::integer_sequence<INT, s...>, std::integer_sequence<INT, t...>, R...){
return concat_sequences(std::integer_sequence<INT,s...,t...>{}, R{}...);
}
And since we may try to concatenate an empty sequence with nothing (can happen if no elements pass the filter), we need another base case:
template<typename INT>
constexpr std::integer_sequence<INT>
concat_sequences(std::integer_sequence<INT>){
return {};
}
Now, for our predicate we will use a constexpr lambda. Note that we do not need to specify it as constexpr explicitly because it already satisfies the criteria to automatically become constexpr
auto is_even = [](int _in) {return _in % 2 == 0;};
So our full test looks like this:
auto is_even = [](int _in) {return _in % 2 == 0;};
using expected_type = std::integer_sequence<int, 0, 2, 4, 6, 8>;
using test_type = std::integer_sequence<int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9>;
constexpr auto result = Filter(test_type{}, is_even);
using result_type = std::decay_t<decltype(result)>;
static_assert(std::is_same_v<expected_type, result_type>, "Integer sequences should be equal");
Previous approach
My approach is repeatedly construct and concatenate sub-sequences, where the base case (sequence of one) will either return an empty sequence or the same sequence if the predicate is satisfied.
For writing the predicate, I'll take advantage of C++17's constexpr if for defining a predicate.
Predicate:
// base case; empty sequence
template<class INT>
constexpr auto FilterEvens(std::integer_sequence<INT>)
{
return std::integer_sequence<INT>{};
}
// base case; one element in the sequence
template<class INT, INT a>
constexpr auto FilterEvens(std::integer_sequence<INT, a>)
{
if constexpr (a % 2 == 0)
return std::integer_sequence<INT, a>{};
else
return std::integer_sequence<INT>{};
}
// recursive case
template<class INT, INT a, INT... b>
constexpr auto FilterEvens(std::integer_sequence<INT, a, b...>)
{
return concat_sequence(FilterEvens(std::integer_sequence<INT, a>{}),
FilterEvens(std::integer_sequence<INT, b...>{}));
}
Concatenation logic:
template <typename INT, INT ...s, INT ...t>
constexpr auto
concat_sequence(std::integer_sequence<INT,s...>,std::integer_sequence<INT,t...>){
return std::integer_sequence<INT,s...,t...>{};
}
And the test:
int main()
{
static_assert(std::is_same_v<std::integer_sequence<int, 0, 2, 4, 6, 8>, decltype(FilterEvens(std::integer_sequence<int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9>{}))>, "Integer sequences should be equal");
}
Live Demo
Edit:
I used this approach to solve the "Bonus" question for removing matched bits here: https://stackoverflow.com/a/41727221/27678
An alternative solution leveraging tuples:
template <auto Pred, class Type, Type... I>
struct filter_integer_sequence_impl
{
template <class... T>
static constexpr auto Unpack(std::tuple<T...>)
{
return std::integer_sequence<Type, T::value...>();
}
template <Type Val>
using Keep = std::tuple<std::integral_constant<Type, Val>>;
using Ignore = std::tuple<>;
using Tuple = decltype(std::tuple_cat(std::conditional_t<(*Pred)(I), Keep<I>, Ignore>()...));
using Result = decltype(Unpack(Tuple()));
};
template <auto Pred, class Type, Type... I>
constexpr auto filter_integer_sequence(std::integer_sequence<Type, I...>)
{
return typename filter_integer_sequence_impl<Pred, Type, I...>::Result();
}
template <class Pred, class Type, Type... I>
constexpr auto filter_integer_sequence(std::integer_sequence<Type, I...> sequence, Pred)
{
return filter_integer_sequence<(Pred*)nullptr>(sequence);
}
Used like this:
constexpr auto predicate = [](int val) { return (val % 2) == 0; };
constexpr auto start = std::integer_sequence<int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9>();
constexpr auto filtered = filter_integer_sequence(start, predicate);
constexpr auto expected = std::integer_sequence<int, 0, 2, 4, 6, 8>();
static_assert(std::is_same_v<decltype(filtered), decltype(expected)>);
The second overload of filter_integer_sequence is only necessary for C++17 which doesn't allow us to use lambdas for non-type template parameters. C++20 lifts that restriction, so in that case only the first overload is needed. Note that the first overload is still necessary in C++17 for handling regular function pointers.
Like the other solutions here, the second overload only works for non-capturing lambdas. This is why we can safely evaluate (*(Pred*)nullptr)(I) as constexpr since the lambda body doesn't actually use the this pointer.