C++ Making tuple from two tuples by randomly choosing their elements - c++

WHAT I WANT EXACTLY: I have a pair of tuples with unknown types and unknown number of arguments, but types of these two tuples and variable count is exactly the same, e.g.:
std::pair<std::tuple<Ts...>, std::tuple<Ts...>>
Let's call those tuples A and B
Now I want to make a new tuple from those two with the exactly same types and argument count (std::tuple<Ts...>) in the following way:
1. Randomly choose tuple A or B for an element (I guess I can do it by calling std::experimental::rand_int(0,1) or another generator).
2. Place this element in new tuple (let's call it C) (maybe std::experimental::apply could be of any help to point 2. and 3.?)
3. Repeat steps 1-2 until the end of tuple A and B
EXAMPLE:
A = std::tuple<double, int, long, int> {2.7, 1, 22, 4};
B = std::tuple<double, int, long, int> {12.4, 7, -19, 18};
After algorithm I want to receive, for example:
C = std::tuple<double, int, long, int> {12.4, 1, 22, 18};
(or any other combination of A and B following this scheme)

Shouldn't be too hard:
template <typename Tuple, std::size_t ...I>
Tuple rand_tuple_aux(const Tuple & a, const Tuple & b, std::index_sequence<I...>)
{
return Tuple(std::get<I>(flip_coin() ? a : b)...);
}
template <typename ...Args>
std::tuple<Args...> rand_tuple(const std::tuple<Args...> & a,
const std::tuple<Args...> & b)
{
return rand_tuple_aux(a, b, std::index_sequence_for<Args...>());
}

I know there's a marked answer so I won't change it, but I wanted to add my solution (it's quite hackish so not really advised, but I wanted to make this function a lambda).
Parents type:
std::pair<std::pair<std::tuple<Ts...>>, std::pair<std::tuple<Ts...>>>
(that's why I've used auto...)
Here's the code:
auto generate_child=[&](const auto& parents){
auto genotype=std::experimental::apply(
[&](const auto&... mother){
return std::experimental::apply(
[&](const auto&... father){
return std::make_tuple(
(std::experimental::randint(0,1)?mother:father)...
);
},
parents.second.second
);
},
parents.first.second
);
return std::make_pair(
std::experimental::apply(function,genotype),
genotype
);
};

Related

Concatenate entries of tuples

I have two tuples like this
std::tuple<std::vector<int>, std::vector<int>> t1;
std::tuple<std::vector<int>, std::vector<int>> t2;
I now want to concat the entries of the tuples (so that I have one tuple containing two vectors with the entries of the the first/second vectors of the tuples). It is fine if the tuple is mutated.
I can do this like this:
std::get<0>(t1).insert(std::get<0>(t1).end(), std::get<0>(t2).begin(), std::get<0>(t2).end());
for each entry, but if i have a lot if entries in the tuple, it becomes very ugly.
Iterating the tuple with a normal for loop does not work since std::get requires a constant. I did not get it to work with std::apply because only one argument can be passed.
Use nested fold-expression, one to expand the tuples and one to expand the elements of the tuple
#include <tuple>
#include <vector>
template<class FirstTuple, class... RestTuples>
void concatenate(FirstTuple& first, const RestTuples&... rest) {
constexpr auto N = std::tuple_size_v<FirstTuple>;
static_assert(((N == std::tuple_size_v<RestTuples>) && ...));
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
([&](const auto& other) {
(std::get<Is>(first).insert(
std::get<Is>(first).end(),
std::get<Is>(other).begin(),
std::get<Is>(other).end()), ...);
}(rest), ...);
}(std::make_index_sequence<N>{});
}
Demo
For a more concise answer, see what 康桓瑋 suggests.
In case you don't have access to C++20's templated lambda expressions, here is the code that uses C++17's features.
First, I would like to produce a new tuple that will consist of merged entries, that is:
auto t1 = std::tuple<std::vector<int>, std::vector<int>>{
{1, 2, 3}, {7, 8}
};
auto t2 = std::tuple<std::vector<int>, std::vector<int>>{
{4, 5, 6}, {9}
};
auto merged = merge_inner(t1, t2);
Thus, merge_inner needs to accept any number of said tuples:
template <
template <typename> typename Tuple,
template <typename> typename... Tuples,
typename... Vectors
>
auto merge_inner(Tuple<Vectors...> first, Tuples<Vectors...> const&... args) {
constexpr auto number_of_vectors = sizeof...(Vectors);
append_values_elementwise(first, std::make_index_sequence<number_of_vectors>(), args...);
return first;
}
We copy the first argument and take the rest by const&. To all vectors (elements) of first, we append those which are inside args... by calling append_values_elementwise. I used std::index_sequence in order to help fold-expressions making my life a little easier:
template <
template <typename> typename Tuple,
std::size_t... Ns,
template <typename> typename... Tuples,
typename... Vectors
>
auto append_values_elementwise(
Tuple<Vectors...>& first,
std::index_sequence<Ns...> sequence,
Tuple<Vectors...> const& first_arg,
Tuples<Vectors...> const&... args
) {
((std::get<Ns>(first).insert(
std::get<Ns>(first).end(),
std::get<Ns>(first_arg).begin(), std::get<Ns>(first_arg).end()
)), ...);
append_values_elementwise(first, sequence, args...);
}
This is just an implementation detail that extracts the next tuple from args... and inserts the elements of its vectors into first. sequence represents a meta-list of indexes suitable to be used with std::get. This is a metaprogramming way of looping over tuples values.
We then recurse with the rest of the arguments.
Lastly, we need a fallback to when we only have one tuple left to merge. The terminal case, which no longer recurses:
template <
template <typename> typename Tuple,
std::size_t... Ns,
template <typename> typename... Tuples,
typename... Vectors
>
auto append_values_elementwise(
Tuple<Vectors...>& first,
std::index_sequence<Ns...>,
Tuple<Vectors...> const& first_arg
) {
((std::get<Ns>(first).insert(
std::get<Ns>(first).end(),
std::get<Ns>(first_arg).begin(), std::get<Ns>(first_arg).end()
)), ...);
}
Demo.

Compare two tuples but only the first two args

I have a std::tuple e.g.
std::tuple<uint16_t, uint16_t, uint32_t> key{};
std::tuple<uint16_t, uint16_t, uint32_t> key2{};
const auto [k, p, r] = key;
and I want to compare with a second tuple but only the first two args. Something like that:
if(std::tie(k, p, std::ignore) < std::tie(key)) { ... }
If I do it in the this way, I get the following error:
error C2338: cannot compare tuples of different sizes
How can I do that?
EDIT1:
Both tuple have the same size. I seen how two compare on EQUAlITY with std::tie() but it would be nicer I could write:
if(std::tie(k, p, std::ignore) == std::tie(key)) { ... }
EDIT2:
Also what if I want this:
if(std::tie(k, std::ignore, p) == std::tie(key)) { ... }
Also what if I want this:
if(std::tie(k, std::ignore, p) == std::tie(key)) { ... }
So you want compare sub-tuples with some elements of original tuples? Not necessarily the first two elements?
What about a function that, given a tuple object an template variadic index list, return the corresponding sub-tuple?
template <std::size_t ... Is, typename T>
auto getSubTuple (T const & t)
{ return std::tie(std::get<Is>(t)...); }
So you can write
std::tuple<std::uint16_t, std::uint16_t, std::uint32_t> key{0, 1, 2};
std::tuple<std::uint16_t, std::uint16_t, std::uint32_t> key2{0, 3, 2};
std::cout << (getSubTuple<0u, 2u>(key) == getSubTuple<0u, 2u>(key2)) << std::endl;
In your example, you are comparing a tuple of size 3 (std::tie(k, p, std::ignore)) with a tuple of size 1 (std::tie(key), which has the type std::tuple<std::tuple<uint16_t, uint16_t, uint32_t>>). You shouldn't call tie on a tuple, because it will create a tuple with a single item in it.
The following code also fail with GCC 8.2.1 because the compiler doesn't know how to handle the std::ignore comparison with an uint32_t. In this case I would write a specialized compare function for this type (std::tuple<std::tuple<uint16_t, uint16_t, uint32_t>>).
#include<tuple>
int main() {
uint16_t k = 0;
uint16_t p = 0;
std::tuple<uint16_t, uint16_t, uint32_t> key{};
if(std::tie(k, p, std::ignore) < key) {
// TODO
}
return 0;
}

C++ Transform a std::tuple<A, A, A...> to a std::vector or std::deque

In the simple parser library I am writing, the results of multiple parsers is combined using std::tuple_cat. But when applying a parser that returns the same result multiple times, it becomes important to transform this tuple into a container like a vector or a deque.
How can this be done? How can any tuple of the kind std::tuple<A>, std::tuple<A, A>, std::tuple<A, A, A> etc be converted into a std::vector<A>?
I think this might be possible using typename ...As and sizeof ...(As), but I am not sure how to create a smaller tuple to call the function recursively. Or how to write an iterative solution that extracts elements from the tuple one by one. (as std::get<n>(tuple) is constructed at compile-time).
How to do this?
With the introduction of std::apply(), this is very straightforward:
template <class Tuple,
class T = std::decay_t<std::tuple_element_t<0, std::decay_t<Tuple>>>>
std::vector<T> to_vector(Tuple&& tuple)
{
return std::apply([](auto&&... elems){
return std::vector<T>{std::forward<decltype(elems)>(elems)...};
}, std::forward<Tuple>(tuple));
}
std::apply() is a C++17 function but is implementable in C++14 (see link for possible implementation). As an improvement, you could add either SFINAE or a static_assert that all the types in the Tuple are actually T.
As T.C. points out, this incurs an extra copy of every element, since std::initializer_list is backed by a const array. That's unfortunate. We win some on not having to do boundary checks on every element, but lose some on the copying. The copying ends up being too expensive, an alternative implementation would be:
template <class Tuple,
class T = std::decay_t<std::tuple_element_t<0, std::decay_t<Tuple>>>>
std::vector<T> to_vector(Tuple&& tuple)
{
return std::apply([](auto&&... elems) {
using expander = int[];
std::vector<T> result;
result.reserve(sizeof...(elems));
expander{(void(
result.push_back(std::forward<decltype(elems)>(elems))
), 0)...};
return result;
}, std::forward<Tuple>(tuple));
}
See this answer for an explanation of the expander trick. Note that I dropped the leading 0 since we know the pack is non-empty. With C++17, this becomes cleaner with a fold-expression:
return std::apply([](auto&&... elems) {
std::vector<T> result;
result.reserve(sizeof...(elems));
(result.push_back(std::forward<decltype(elems)>(elems)), ...);
return result;
}, std::forward<Tuple>(tuple));
Although still relatively not as nice as the initializer_list constructor. Unfortunate.
Here's one way to do it:
#include <tuple>
#include <algorithm>
#include <vector>
#include <iostream>
template<typename first_type, typename tuple_type, size_t ...index>
auto to_vector_helper(const tuple_type &t, std::index_sequence<index...>)
{
return std::vector<first_type>{
std::get<index>(t)...
};
}
template<typename first_type, typename ...others>
auto to_vector(const std::tuple<first_type, others...> &t)
{
typedef typename std::remove_reference<decltype(t)>::type tuple_type;
constexpr auto s =
std::tuple_size<tuple_type>::value;
return to_vector_helper<first_type, tuple_type>
(t, std::make_index_sequence<s>{});
}
int main()
{
std::tuple<int, int> t{2,3};
std::vector<int> v=to_vector(t);
std::cout << v[0] << ' ' << v[1] << ' ' << v.size() << std::endl;
return 0;
}
Although, this doesn't answer the question completely, This still might be suitable in some cases. Only when the number of elements in tuple is around 5, 6. (And you know the size).
tuple<int, int, int, int> a = make_tuple(1, 2, 3, 4);
auto [p, q, r, s] = a;
vector<int> arr(p, q, r, s); // Now, arr has the same elements as in tuple a
Note that, this is C++ 17 feature. More info here

Pushing and popping the first element of a std::tuple

I am writing a function in C++ with a variable number of arguments (and different types) in this way
template<typename ...Ts>
void myFunction(Ts ...args)
{
//create std::tuple to access and manipulate single elements of the pack
auto myTuple = std::make_tuple(args...);
//do stuff
return;
}
What i would like to do, but I don't know how, is to push and pop elements from the tuple, in particular the first element... something like
//remove the first element of the tuple thereby decreasing its size by one
myTuple.pop_front()
//add addThis as the first element of the tuple thereby increasing its size by one
myTuple.push_front(addThis)
Is this possible?
You may do something like
template <typename T, typename Tuple>
auto push_front(const T& t, const Tuple& tuple)
{
return std::tuple_cat(std::make_tuple(t), tuple);
}
template <typename Tuple, std::size_t ... Is>
auto pop_front_impl(const Tuple& tuple, std::index_sequence<Is...>)
{
return std::make_tuple(std::get<1 + Is>(tuple)...);
}
template <typename Tuple>
auto pop_front(const Tuple& tuple)
{
return pop_front_impl(tuple,
std::make_index_sequence<std::tuple_size<Tuple>::value - 1>());
}
Demo
Note that it is really basic and doesn't handle tuple of reference, or tuple of const qualified type, but it might be sufficient.
With generic lambdas, you can do quite elegant:
template<typename Tuple>
constexpr auto pop_front(Tuple tuple) {
static_assert(std::tuple_size<Tuple>::value > 0, "Cannot pop from an empty tuple");
return std::apply(
[](auto, auto... rest) { return std::make_tuple(rest...); },
tuple);
}
Length and types of a std::tuple are determined at compile time. No run-time popping or pushing is possible. You could use a std::vector to provide for the run-time modifications.
The data type for your vector could be a std::variant (C++17) or boost::variant. Both types take a list of supported types at compile time, and can be filled with any value of matching type.
Alternatively, you could use std::any (also C++17) or boost::any to store any type, but with different access semantics.
typedef boost::variant<int, std::string, double> value;
std::vector<value> data;
data.push_back(value(42));
data.psuh_back(value(3.14));
You cannot 'lengthen' the tuple by adding elements - that is not what a tuple represents. The point of a tuple is the binding connection between the different values that make it up, like 'firstname', 'lastname', 'phone'.
What you seem to want is much easier accomplished by using a vector - you can easily add elements to it, and remove them - its size can be arbitrarily changed as needed.

Converting a lambda to a std::tr1::function

Using visual studio 2008 with the tr1 service pack and Intel C++ Compiler 11.1.071 [IA-32], this is related to my other question
I'm attempting to write a functional map for c++ which would work somewhat like the ruby version
strings = [2,4].map { |e| e.to_s }
So i've defined the following function in the VlcFunctional namespace
template<typename Container, typename U>
vector<U> map(const Container& container, std::tr1::function<U(Container::value_type)> f)
{
vector<U> transformedValues(container.size());
int index = -1;
BOOST_FOREACH(const auto& element, container)
{
transformedValues.at(++index) = f(element);
}
return transformedValues;
}
and you can call this like so (Note that the function template arguments are defined explicitly):
vector<int> test;
test.push_back(2); test.push_back(4);
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, [](int i) -> string
{
return ToString(i);
});
Or like so (Note that the function template arguments aren't defined explicitly)
std::tr1::function f = [](int i) -> string { return ToString(i); };
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, f);
But crucially, NOT LIKE THIS
vector<string> mappedData2 = VlcFunctional::map(test, [](int i) -> string { return ToString(i); });
Without the explicit definition of hte template arguments, it doesn't know which template to use and falls over with a compile error
..\tests\VlcFunctional_test.cpp(106): error: no instance of function template "VlcFunctional::map" matches the argument list, argument types are: (std::vector<int, std::allocator<int>>, __lambda3)
Having to define the template arguments makes it a much more bulky syntax and I'm aiming for minimal cruft at the call site - any ideas on why it doesn't know how do the conversion? Is this a compiler issue or does the language not allow for this type of template argument inference?
The problem is that a lambda is not a std::function even if it can be converted. When deducing type arguments, the compiler is not allowed to perform conversions on the actual provided arguments. I would look for a way to have the compiler detect the type U and let the second argument free for the compiler to deduce:
template <typename Container, typename Functor>
std::vector< XXX > VlcFunctional::map( Container &, Functor )...
Now the issue is what to write in XXX. I don't have the same compiler that you do, and all C++0x features are still a little tricky. I would first try to use decltype:
template <typename Container, typename Functor>
auto VlcFunctional::map( Container & c, Functor f ) -> std::vector< decltype(f(*c.begin())) > ...
Or maybe type traits if the compiler does not support decltype yet.
Also note that the code you are writting is quite unidiomatic in C++. Usually when manipulating containers the functions are implemented in terms of iterators, and your whole map is basically the old std::transform:
std::vector<int> v = { 1, 2, 3, 4, 5 };
std::vector<std::string> s;
std::transform( v.begin(), v.end(), std::back_inserter(s), [](int x) { return ToString(x); } );
Where std::transform is the C++ version of your map function. While the syntax is more cumbersome, the advantage is that you can apply it to any container, and produce the output to any other container, so the transformed container is not fixed to std::vector.
EDIT:
A third approach, probably easier to implement with your current compiler support is manually providing just the return type of the lambda as template argument, and letting the compiler deduce the rest:
template <typename LambdaReturn, typename Container, typename Functor>
std::vector<LambdaReturn> map( Container const & c, Functor f )
{
std::vector<LambdaReturn> ret;
std::transform( c.begin(), c.end(), std::back_inserter(ret), f );
return ret;
}
int main() {
std::vector<int> v{ 1, 2, 3, 4, 5 };
auto strs = map<std::string>( v, [](int x) {return ToString(x); });
}
Even if you want to add syntactic sugar to your map function, there is no need to manually implement it when you can use existing functionality.