Initializer list weirdly depends on order of parameters? - c++

I have the following snippet of code:
#include <type_traits>
#include <limits>
#include <initializer_list>
#include <cassert>
template <typename F, typename... FIn>
auto min_on(F f, const FIn&... v) -> typename std::common_type<FIn...>::type
{
using rettype = typename std::common_type<FIn...>::type;
rettype result = std::numeric_limits<rettype>::max();
(void)std::initializer_list<int>{((f(v) < result) ? (result = static_cast<rettype>(v), 0) : 0)...};
return result;
}
int main()
{
auto mod2 = [](int a)
{
return a % 2;
};
assert(min_on(mod2, 2) == 2); // PASSES as it should
assert(min_on(mod2, 3) == 3); // PASSES as it should
assert(min_on(mod2, 2, 3) == 3); // PASSES but shouldn't - should be 2
assert(min_on(mod2, 2, 3) == 2); // FAILS but shouldn't - should be 2
}
The idea behind template function min_on is that it should return the parameter x from list of parameters passed to it v so that it gives the smallest values for expression f(v).
The problem that I have observed is that somehow the order of parameters inside the std::initializer_list is important so the the code above will fail whereas this code:
assert(min_on(mod2, 3, 2) == 2);
will work. What might be wrong in here?

Your function sets result to v if f(v) < result. With mod2 as f, f(v) will only ever result in a 0, 1 or a -1. Which means that if all of your values are greater than 1, result will be set to the last v which was tested, because f(v) will always be less than result. Try putting a negative number in the middle of a bunch of positive numbers, and the negative number will always be the result, no matter where you place it.
assert(min_on(mod2, 2, 3, 4, -3, 7, 6, 5) == -3);
Perhaps you want this instead:
std::initializer_list<int>{((f(v) < f(result)) ? (result = static_cast<rettype>(v), 0) : 0)...};
The difference is I am testing f(v) < f(result), instead of f(v) < result. Although, the function is still not correct generally because it assumes that f(std::numeric_limits<rettype>::max()) is the max possible value. In the case of mod2 it works. But with something like this:
[](int a) { return -a; }
it would clearly be wrong. So perhaps you could instead require a first argument:
template <typename F, typename FirstT, typename... FIn>
auto min_on(F f, const FirstT& first, const FIn&... v)
-> typename std::common_type<FirstT, FIn...>::type
{
using rettype = typename std::common_type<FirstT, FIn...>::type;
rettype result = first;
(void)std::initializer_list<int>{((f(v) < f(result)) ? (result = static_cast<rettype>(v), 0) : 0)...};
return result;
}
Or, if you're want to avoid unnecessary calls to f:
template <typename F, typename FirstT, typename... FIn>
auto min_on(F f, const FirstT& first, const FIn&... v)
-> typename std::common_type<FirstT, FIn...>::type
{
using rettype = typename std::common_type<FirstT, FIn...>::type;
rettype result = first;
auto result_trans = f(result);
auto v_trans = result_trans;
(void)std::initializer_list<int>{(
(v_trans = f(v), v_trans < result_trans)
? (result = static_cast<rettype>(v), result_trans = v_trans, 0) : 0)...};
return result;
}

Related

create tuple of nth element in the cartesian product of input vectors

I have a python function which returns the nth-element in the cartesian product of a number of input arrays
def prod(n, arrs):
out = []
for i,arr in enumerate(arrs):
denom = numpy.prod([ len(p) for p in arrs[i+1:] ], dtype=int)
idx = n // denom % len(arr)
out.append( arr[idx] )
return out
This works great:
a = [ 1000, 1100, 1200, 1300, 1400 ]
b = [ 1.0, 1.5, 2.0, 2.5, 3.0, 3.5 ]
c = [ -2, -1, 0, 1, 2 ]
for n in range(20, 30):
i = prod(n, [a, b, c])
print(n, i)
[1000, 3.0, -2]
[1000, 3.0, -1]
[1000, 3.0, 0]
[1000, 3.0, 1]
[1000, 3.0, 2]
[1000, 3.5, -2]
[1000, 3.5, -1]
[1000, 3.5, 0]
[1000, 3.5, 1]
[1000, 3.5, 2]
Now I would like to translate this to C++ (max standard C++-17)
template<typename... Ts>
auto prod(std::size_t n, const std::vector<Ts>&... vs)
-> std::tuple<const std::decay_t<typename std::vector<Ts>::value_type>&...>
{
// template magic here
}
Can someone help me with the template magic required to construct the tuple using the above formula?
First, let's just auto-deduce the function's return type for simplicity.
Next, index-sequences are neat. With them, it can be done.
With C++20, we could get the indices from the sequence in a lambda. Before that, we need an extra function.
Finally, we have to start creating the indices from the end, either storing the indices and then using them in reverse order or reversing the resulting tuple.
template <class T, std::size_t... Ns>
static auto prod_impl(std::size_t n, T tuple, std::index_sequence<Ns...>) {
auto f = [&](auto N){ auto r = n % N; n /= N; return r; };
auto x = std::forward_as_tuple(std::get<(sizeof...(Ns)) - Ns - 1>(tuple)[f(std::get<(sizeof...(Ns)) - Ns - 1>(tuple).size())]...);
return std::forward_as_tuple(std::get<(sizeof...(Ns)) - Ns - 1>(x)...);
}
template<class... Ts>
auto prod(std::size_t n, const std::vector<Ts>&... vs) {
return prod_impl(n, std::forward_as_tuple(vs...), std::make_index_sequence<sizeof...(vs)>());
}
Simpler alternative for the inner function using an array of indices:
template <class T, std::size_t... Ns>
static auto prod_impl(std::size_t n, T tuple, std::index_sequence<Ns...>) {
auto f = [&](auto N){ auto r = n % N; n /= N; return r; };
std::size_t Is[] = { f(std::get<sizeof...(Ns) - Ns - 1>(tuple).size())... , 0};
return std::forward_as_tuple(std::get<Ns>(tuple)[sizeof...(Ns) - Ns - 1]...);
}
The other answers have already mentioned the indices trick. Here is my attempt at it, converted as directly as possible from your python code:
template <typename T, std::size_t... I>
auto prod_impl(std::size_t n, T tuple, std::index_sequence<I...>) {
std::array sizes{ std::size(std::get<I>(tuple))... };
auto enumerator = [&sizes,n](std::size_t i, auto&& arr) -> decltype(auto) {
auto denom = std::accumulate(std::begin(sizes) + i + 1, std::end(sizes), 1, std::multiplies<>{});
auto idx = (n / denom) % std::size(arr);
return arr[idx];
};
return std::forward_as_tuple(enumerator(I, std::get<I>(tuple))...);
}
template<typename... Ts, typename Is = std::index_sequence_for<Ts...>>
auto prod(std::size_t n, const std::vector<Ts>&... vs) {
return prod_impl(n, std::forward_as_tuple(vs...), Is{});
}
(Live example: http://coliru.stacked-crooked.com/a/a8b975c29d429054)
We can use the "indices trick" to get us indices associated with the different vectors.
This gets us the following C++14 solution:
#include <tuple>
#include <vector>
#include <cstdlib>
using std::array;
using std::size_t;
template <size_t NDim>
constexpr array<size_t, NDim> delinearize_coordinates(
size_t n, array<size_t, NDim> dimensions)
{
// This might be optimizable into something nicer, maybe even a one-liner
array<size_t, NDim> result{};
for(size_t i = 0; i < NDim; i++) {
result[NDim-1-i] = n % dimensions[NDim-1-i];
n = n / dimensions[NDim-1-i];
};
return result;
}
template<size_t... Is, typename... Ts>
auto prod_inner(
std::index_sequence<Is...>,
size_t n,
const std::vector<Ts>&... vs)
-> std::tuple<const std::decay_t<typename std::vector<Ts>::value_type>&...>
{
auto vs_as_tuple = std::make_tuple( vs ... );
auto coordinates = delinearize_coordinates<sizeof...(Ts)>(n, { vs.size()... });
return { std::get<Is>(vs_as_tuple)[coordinates[Is]] ... };
}
template<typename... Ts>
auto prod(size_t n, const std::vector<Ts>&... vs)
-> std::tuple<const std::decay_t<typename std::vector<Ts>::value_type>&...>
{
return prod_inner(std::make_index_sequence<sizeof...(Ts)>{}, n,
std::forward<const std::vector<Ts>&>(vs)...);
}
Notes:
Unlike in your code, I've factored-out the function which translates a single number into a sequence of coordinates.
I think you can get this down to C++11 if you provide your own make_index_sequence.

Test if all elements are equal with C++17 fold-expression

I have a function taking a variadic parameter pack and at the beginning I want to check that all elements compare equal. Can I somehow use the new C++17 fold-expressions to write that succinctly as a one-liner? I was thinking
template<typename... Args>
void func (Args... args)
{
ASSERT ((args == ...));
// more code here...
}
but this doesn't work, as it compiles to code that first properly compares the last two arguments, but then compares the third-last argument to the result of the first comparison, which is a bool. What use-case could this type of fold expression possibly have (similar for args < ...)? Is there any chance I can avoid writing a dedicated recursive template to do this?
The reason that doesn't work, unfortunately, is due to the fact that boolean operators don't chain in C++ like they do in other languages. So the expression:
a == (b == c)
(what your fold-expression would expand to) would compare a to either true or false, nothing to do with what b or c actually are. I was hoping the operator<=> would add chaining but apparently that part was dropped.
The fixes are that you have to break up the comparisons:
(a == b) && (b == c)
Of course that doesn't lend itself to folding very well, but you could instead compare everything to the first element:
(a == b) && (a == c)
Which is to say:
((a0 == args) && ... )
At that point, we just need to be able to pull out the first element. No problem, that's obviously what lambdas are for:
template <class... Args>
constexpr bool all_equal(Args const&... args) {
if constexpr (sizeof...(Args) == 0) {
return true;
} else {
return [](auto const& a0, auto const&... rest){
return ((a0 == rest) && ...);
}(args...);
}
}
As suggested by Piotr Skotnicki, a simple solution is separate the first argument from the followings and check it with using && as fold operator
By example, the following function that return true if all arguments are equals
template <typename A0, typename ... Args>
bool foo (A0 const & a0, Args const & ... args)
{ return ( (args == a0) && ... && true ); }
Unfortunately this can't work with an empty list of arguments
std::cout << foo(1, 1, 1, 1) << std::endl; // print 1
std::cout << foo(1, 1, 2, 1) << std::endl; // print 0
std::cout << foo() << std::endl; // compilation error
but you can add the special empty argument foo()
bool foo ()
{ return true; }
If, for some reason, you can't split the args in a a0 and the following args?
Well... you can obviously use the preceding foo() function (with special empty version)
template<typename... Args>
void func (Args... args)
{
ASSERT (foo(args));
// more code here...
}
or you can use the C++17 fold expression with comma operator and assignment as in the following bar()
template <typename ... Args>
bool bar (Args const & ... args)
{
auto a0 = ( (0, ..., args) );
return ( (args == a0) && ... && true );
}
Observe the initial zero in a0 assignment that permit the use of this solution also with an empty list of arguments.
Unfortunately, from the preceding auto a0 assignment I get a lot of warnings ("expression result unused", from clang++, and "left operand of comma operator has no effect", from g++) that I don't know how to avoid.
The following is a full working example
#include <iostream>
template <typename A0, typename ... Args>
bool foo (A0 const & a0, Args const & ... args)
{ return ( (args == a0) && ... && true ); }
bool foo ()
{ return true; }
template <typename ... Args>
bool bar (Args const & ... args)
{
auto a0 = ( (0, ..., args) );
return ( (args == a0) && ... && true );
}
int main ()
{
std::cout << foo(1, 1, 1, 1) << std::endl; // print 1
std::cout << foo(1, 1, 2, 1) << std::endl; // print 0
std::cout << foo() << std::endl; // print 1 (compilation error
// witout no argument
// version)
std::cout << bar(1, 1, 1, 1) << std::endl; // print 1
std::cout << bar(1, 1, 2, 1) << std::endl; // print 0
std::cout << bar() << std::endl; // print 1 (no special version)
}
-- EDIT --
As pointed by dfri (thanks!), for and empty args... pack, the values for the following folded expressions
( (args == a0) && ... )
( (args == a0) || ... )
are, respectively, true and false.
So return instruction of foo() and bar() can be indifferently written
return ( (args == a0) && ... && true );
or
return ( (args == a0) && ... );
and this is true also in case sizeof...(args) == 0U.
But I tend to forget this sort of details and prefer to explicit (with the final && true) the empty-case value.
Here is how I do it in gcl library :
template <auto ... values>
constexpr static auto equal_v = []() consteval {
static_assert(sizeof...(values) > 0, "gcl::mp::value_traits::equal_v : no arguments");
constexpr auto first_value = std::get<0>(std::tuple{values...});
static_assert(
(std::equality_comparable_with<decltype(values), decltype(first_value)> && ...),
"gcl::mp::value_traits::equal_v : cannot compare values");
return ((values == first_value) && ...);
}();
or replacing static_assert by concept requirement :
template <typename ... Ts>
concept are_equality_comparable = requires(Ts ... values)
{
{
std::conditional_t<(std::equality_comparable_with<decltype(std::get<0>(std::tuple{values...})), decltype(values)> && ...), std::true_type, std::false_type>{}
} -> std::same_as<std::true_type>;
};
template <auto ... values>
requires(are_equality_comparable<decltype(values)...>)
constexpr static auto equal_v = []() consteval {
static_assert(sizeof...(values) > 0, "gcl::mp::value_traits::equal_v : no arguments");
constexpr auto first_value = std::get<0>(std::tuple{values...});
return ((values == first_value) && ...);
}();

function template: default first template argument to second

Is is possible to make the first template argument of a function template default to the second one if the first one is not specified?
Here is a small example:
#include <algorithm>
#include <list>
#include <vector>
template <typename ContainerOut, typename ContainerIn>
ContainerOut KeepNegatives(const ContainerIn& xs)
{
ContainerOut result;
auto itOut = std::inserter(result, std::end(result));
auto isNegative = [](auto x){ return x < 0; };
std::copy_if(std::begin(xs), std::end(xs), itOut, isNegative);
return result;
}
int main()
{
typedef std::vector<int> IntVector;
typedef std::list<int> IntList;
IntVector intVector = { 1, -2, -3, 4 };
IntList intList = { 1, -2, -3, 4 };
auto intVec2 = KeepNegatives<IntVector>(intList);
auto intList2 = KeepNegatives<IntList>(intVector);
auto intVec3 = KeepNegatives<IntVector>(intVector);
}
This works, but what I want is, that the type of the return value of KeepNegatives (i.e. ContainerOut) is the same as the type of the input value (i.e. ContainerIn) in case ContainerOut is not specified. So that the following line of code would compile (right now it does not) and return an IntVector.
auto intVec4 = KeepNegatives(intVector);
You could simply add an overload for this special case:
template <typename ContainerIn>
ContainerIn KeepNegatives(const ContainerIn& xs)
{
return KeepNegatives<ContainerIn, ContainerIn>(xs);
}
This, however, can cause an ambiguity in your intVec3 case. Here's one way around it:
#include <algorithm>
#include <list>
#include <vector>
template <typename ContainerOut, typename ContainerIn,
typename = std::enable_if_t<!std::is_same<ContainerOut, ContainerIn>::value>>
ContainerOut KeepNegatives(const ContainerIn& xs)
{
ContainerOut result;
auto itOut = std::inserter(result, std::end(result));
auto isNegative = [](auto x){ return x < 0; };
std::copy_if(std::begin(xs), std::end(xs), itOut, isNegative);
return result;
}
template <typename ContainerIn>
ContainerIn KeepNegatives(const ContainerIn& xs)
{
return KeepNegatives<ContainerIn, ContainerIn, void>(xs);
}
int main()
{
typedef std::vector<int> IntVector;
typedef std::list<int> IntList;
IntVector intVector = { 1, -2, -3, 4 };
IntList intList = { 1, -2, -3, 4 };
auto intVec2 = KeepNegatives<IntVector>(intList);
auto intList2 = KeepNegatives<IntList>(intVector);
auto intVec3 = KeepNegatives<IntVector>(intVector);
auto intVec4 = KeepNegatives(intVector);
}
Live
A better approach, taken by, e.g., std::experimental::make_array, is to not use ContainerOut as the return type directly; this allows you to specify a tag type as the default (void is a simple choice), and then compute the return type.
template <typename ContainerOut = void, typename ContainerIn,
typename ret_t = std::conditional_t<std::is_void<ContainerOut>{},
ContainerIn, ContainerOut>>
ret_t KeepNegatives(const ContainerIn& xs){
// ...
}
You could combine SFINAE with an overload as below:
template <typename ContainerOut, typename ContainerIn>
std::enable_if_t<!std::is_same<ContainerOut, ContainerIn>::value, ContainerOut>
KeepNegatives(const ContainerIn& xs) {
ContainerOut result;
auto itOut = std::inserter(result, std::end(result));
auto isNegative = [](auto x) { return x < 0; };
std::copy_if(std::begin(xs), std::end(xs), itOut, isNegative);
return result;
}
template <typename ContainerIn>
ContainerIn
KeepNegatives(const ContainerIn& xs) {
ContainerIn result;
auto itOut = std::inserter(result, std::end(result));
auto isNegative = [](auto x) { return x < 0; };
std::copy_if(std::begin(xs), std::end(xs), itOut, isNegative);
return result;
}
and in main:
auto intVec2 = KeepNegatives<IntVector>(intList);
auto intList2 = KeepNegatives<IntList>(intVector);
auto intVec3 = KeepNegatives(intVector);
LIVE DEMO
Providing just a simple overload as:
template <typename ContainerIn>
ContainerIn KeepNegatives(const ContainerIn& xs) {...}
You'll get an ambiguous call to overloaded function, if the user provides explicitly the same template argument as:
auto intVec3 = KeepNegatives<IntVector>(intVector);

Hash function for sequences of tuples - Duplicate elimination

I want to eliminate duplicates of sequences of tuples. These sequences look like this:
1. (1,1)(2,5,9)(2,3,10)(2,1)
2. (1,2)(3,2,1)(2,5,9)(2,1)
3. (1,1)(2,5,9)(2,3,10)(2,1)
4. (2,1)(2,3,10)(2,5,9)(1,1)
5. (2,1)(2,3,10)(1,1)
6. (1,1)(2,5,9)(2,3,10)(2,2)
The number of entries per tuple varies as does the number of tuple per sequence. Since I have lots of sequences which I ultimately want to deal with in parallel using CUDA, I thought that calculating a hash per sequence would be an efficient way to identify duplicate sequences.
How would such a hash function be implemented?
And: How big is the collision probability of two different sequences producing the same final hash value?
I have two requirements which I am not sure if they can be fulfilled:
a) Can such a hash be calculated on the fly?
I want to avoid the storage of the full sequences, therefore I'd like to do something like this:
h = 0; // init hash
...
h = h + hash(1,1);
...
h = h + hash(2,5,9);
...
h = h + hash(2,3,10)
...
h = h + hash(2,1)
where + is any operator which combines hashes of tuples.
b) Can such a hash be independent of the "direction" of the sequence?
In the above example sequences 1. and 4. consist of the same tuples but the order is reversed, but I like to identify them as duplicates.
For hashing you can use std::hash<std::size_t> or whatever (unsigned) integer type you use. The collision probability is somewhere around 1.0/std::numeric_limits<std::size_t>::max(), which is very small. To make the usability a bit better you can write your own tuple hasher:
namespace hash_tuple
{
std::size_t hash_combine(std::size_t l, std::size_t r) noexcept
{
constexpr static const double phi = 1.6180339887498949025257388711906969547271728515625;
static const double val = std::pow(2ULL, 4ULL * sizeof(std::size_t));
static const std::size_t magic_number = val / phi;
l ^= r + magic_number + (l << 6) + (l >> 2);
return l;
}
template <typename TT>
struct hash
{
std::size_t operator()(TT const& tt) const noexcept
{
return std::hash<TT>()(tt);
}
};
namespace
{
template <class TupleT, std::size_t Index = std::tuple_size<TupleT>::value - 1ULL>
struct HashValueImpl
{
static std::size_t apply(std::size_t seed, TupleT const& tuple) noexcept
{
seed = HashValueImpl<TupleT, Index - 1ULL>::apply(seed, tuple);
seed = hash_combine(seed, std::get<Index>(tuple));
return seed;
}
};
template <class TupleT>
struct HashValueImpl<TupleT, 0ULL>
{
static std::size_t apply(size_t seed, TupleT const& tuple) noexcept
{
seed = hash_combine(seed, std::get<0>(tuple));
return seed;
}
};
}
template <typename ... TT>
struct hash<std::tuple<TT...>>
{
std::size_t operator()(std::tuple<TT...> const& tt) const noexcept
{
std::size_t seed = 0;
seed = HashValueImpl<std::tuple<TT...> >::apply(seed, tt);
return seed;
}
};
}
Thus you can write code like
using hash_tuple::hash;
auto mytuple = std::make_tuple(3, 2, 1, 0);
auto hasher = hash<decltype(mytuple)>();
std::size_t mytuple_hash = hasher(mytuple);
To fulfill your constraint b we need for each tuple 2 hashes, the normal hash and the hash of the reversed tuple.
So at first we need to deal with how to reverse one:
template<typename T, typename TT = typename std::remove_reference<T>::type, size_t... I>
auto reverse_impl(T&& t, std::index_sequence<I...>)
-> std::tuple<typename std::tuple_element<sizeof...(I) - 1 - I, TT>::type...>
{
return std::make_tuple(std::get<sizeof...(I) - 1 - I>(std::forward<T>(t))...);
}
template<typename T, typename TT = typename std::remove_reference<T>::type>
auto reverse(T&& t)
-> decltype(reverse_impl(std::forward<T>(t),
std::make_index_sequence<std::tuple_size<TT>::value>()))
{
return reverse_impl(std::forward<T>(t),
std::make_index_sequence<std::tuple_size<TT>::value>());
}
Then we can calculate our hashes
auto t0 = std::make_tuple(1, 2, 3, 4, 5, 6);
auto t1 = std::make_tuple(6, 5, 4, 3, 2, 1);
using hash_tuple::hash;
auto hasher = hash<decltype(t0)>();
std::size_t t0hash = hasher(t0);
std::size_t t1hash = hasher(t1);
std::size_t t0hsah = hasher(reverse(t0));
std::size_t t1hsah = hasher(reverse(t1));
And if hash_combine(t0hash, t1hash) == hash_combine(t1hsah, t0hsah) you found what you want. You can apply this "inner-tuple-hashing-mechanic" to the hashes of many tuples pretty easily. Play around with this online!

C++ writing a generic functions for all std::sets

I need to write a little function that makes a new std::set taking the last n elements from an existing one.
Here is the code:
template <typename S, typename T, typename Z>
std::set<T,S,Z> get_first_subset(std::set<T,S,Z> const& set, size_t size) {
if (size == 0)
return std::set<T,S,Z>();
typename std::set<T,S,Z>::reverse_iterator j = set.rbegin();
std::advance(j, size - 1);
return std::set<T,S,Z> ((++j).base(), set.end());
}
It works, however since I do not need to access the type T, S, and Z I was wondering if there is a way to simply say "any std::set" without three template parameters.
What about having it even more generic:
#include <iterator>
template <typename T>
T get_first_subset(T const& set, size_t size) {
if (size == 0)
return T();
typename T::reverse_iterator j = set.rbegin();
std::advance(j, size - 1);
return T ((++j).base(), set.end());
}
Then:
int main() {
std::set<int> s{10, 2,4,6,7,8,9}, s1;
s1 = get_first_subset(s, 4);
for (auto i:s1) std::cout << i << " ";
std::cout << std::endl;
}
outputs:
7 8 9 10
You can also use variadic templates (C++11), brace initialization and auto keyword to avoid repeating yourself:
template <typename ...S>
std::set<S...> get_first_subset(std::set<S...> const& set, size_t size) {
if (size == 0) return {};
auto j = set.rbegin();
std::advance(j, size - 1);
return {(++j).base(), set.end()};
}