Related
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)>);
I am trying to get a simple example to work to understand how to use std::enable_if, here is the problem:
I am reading the textbook C++ Templates The Complete Guide by David Vandevoorde, Nicolai M.Josuttis, Chapter 7, Section 4.
This chapter mentions: "You can use type traits to detect whether an array (or a pointer) was passed", and its following code:
template<typename T,
std::enable_if_t<std::is_array_v<T>>>
void foo(T&& arg1, T&& arg2)
{
}
My question is, how should the above code be called and used? for example, I tried:
int x[] = {1, 2, 3};
int y[] = {1, 2, 3, 4, 5};
foo(x, y);
but didn't compile, i couldn't find similar usage online, can someone please give me some hints or code guidance on how to use above code?
You should specify a default value for the 2nd template parameter, or move it to the return type.
With forwarding reference T might be deduced as lvalue-reference (when passed lvalues like x and y), which is not an array and yields false for std::is_array.
x is of type int[3], y is of type int[5]. They're different types and causes the type deduction on T failing.
So
template<typename T,
std::enable_if_t<std::is_array_v<std::remove_reference_t<T>>>* = nullptr>
void foo(T&& arg1, T&& arg2)
{
}
or
template<typename T>
std::enable_if_t<std::is_array_v<std::remove_reference_t<T>>> foo(T&& arg1, T&& arg2)
{
}
then
int x[] = {1, 2, 3};
int y[] = {4, 5, 6};
foo(x, y);
LIVE
I am trying to implement matrix-like class, using an std::array to actually store the data. All of the data is known at compile-time.
I want to be able to use initializer-lists to initialize the Matrix. Something along the lines of
Matrix m = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
I also want to be able to set the dimensions of my matrix during instantiation.
My current code is similar to the following:
template<int m, int n>
class Matrix {
public:
using initializer_list2d = std::initializer_list< std::initializer_list<float> >;
using array2d = std::array< std::array<float, m>, n >;
consteval Matrix(initializer_list2d initList) {
// static_assert to check initList length [...]
int i = 0;
for (auto &row : initList) {
// static_assert to check row length [...]
std::copy(row.begin(), row.end(), data_[i].begin());
i++;
}
}
// Definitions of operators and methods [...]
private:
array2d data_;
};
In order to use this though, I have to set the dimensions through the template:
Matrix<3, 3> m = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
It seems to me, that since the constructor is consteval (and all of its parameters have to be known at compile-time anyway) it should somehow bet possible to deduce the dimensions through the initializer-list alone.
Using a dynamic data type (e.g. vector) is not possible for my application, since this program has to be able to run without access to the heap.
Is there some way to achieve this?
Thanks for any help in advance!
Before C++17 there is no deduction of template arguments for a class template, so this is not possible.
From C++17, you can write this deduction guide:
template<int m, int n>
Matrix(float const (&)[n][m]) -> Matrix<m, n>;
and add an extra pair of braces when constructing the Matrix:
Matrix m =
{
{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}
};
Here's a demo.
I found another interesting solution, using variadic templates:
template<int m, class T, class... U>
struct Matrix {
Matrix(const T (&t)[m], const U (& ...u)[m]) {
std::copy(t, t+m, arr[0]);
int i = 0;
(std::copy(u, u+m, arr[++i]), ...);
}
T (arr[m])[sizeof...(U) + 1];
};
It even allows for getting rid of the second brace required in cigien's answer and doesn't need a template deduction guide (Although it still needs C++17 in order for int m to be automatically deduced):
Matrix mat = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
Also, the compiler seems to be able to optimze this quite a bit, as seen here
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.
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.