Create a tuple from a vector of strings in c++ - c++

I have a vector of strings, each of which is the result of applying std::to_string to some basic datatype (eg char, int, double). I would like a function to undo this into a tuple of the appropriate types.
I have a simple function template to invert std::to_string:
template<typename T>
T from_string(std::string s)
{
}
template<>
int from_string<int>(std::string s)
{
return std::stoi(s);
}
template<>
double from_string<double>(std::string s)
{
return std::stod(s);
}
//... and more such specializations for the other basic types
I want a function like:
template<typename... Ts>
std::tuple<Ts> undo(const std::vector<std::string>>& vec_of_str)
{
// somehow call the appropriate specializations of from_string to the elements of vector_of_str and pack the results in a tuple. then return the tuple.
}
The function should behave like this:
int main()
{
auto ss = std::vector<std::string>>({"4", "0.5"});
auto tuple1 = undo<int, double>(ss);
std::tuple<int, double> tuple2(4, 0.5);
// tuple1 and tuple2 should be identical.
}
I think that I have to "iterate" over the parameters in Ts (perhaps the correct term is "unpack"), call the previous function, from_string for each one, and then package the results of each application of from_string into a tuple. I've seen (and used) examples that unpack a template parameter pack - they are usually recursive (but not in the usual way of a function calling itself), but I don't see how to do the rest.

An example:
#include <vector>
#include <string>
#include <tuple>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/trim.hpp>
template<class... Ts, size_t... Idxs>
std::tuple<Ts...>
parse(std::vector<std::string> const& values, std::index_sequence<Idxs...>) {
return {boost::lexical_cast<Ts>(boost::algorithm::trim_copy(values[Idxs]))...};
}
template<class... Ts>
std::tuple<Ts...> undo(std::vector<std::string> const& values) {
assert(sizeof...(Ts) == values.size());
return parse<Ts...>(values, std::make_index_sequence<sizeof...(Ts)>{});
}
int main() {
auto ss = std::vector<std::string>({"4", "0.5"});
auto tuple1 = undo<int, double>(ss);
std::tuple<int, double> tuple2(4, 0.5);
std::cout << (tuple1 == tuple2) << '\n';
assert(tuple1 == tuple2);
}
If the string values do not contain leading and/or trailing whitespace, then that call to boost::algorithm::trim_copy can be removed. It is there because boost::lexical_cast fails on whitespace.
Without boost::lexical_cast you will need to re-implement it, something like:
template<class T> T from_string(std::string const& s);
template<> int from_string<int>(std::string const& s) { return std::stoi(s); }
template<> double from_string<double>(std::string const& s) { return std::stod(s); }
// And so on.
template<class... Ts, size_t... Idxs>
std::tuple<Ts...>
parse(std::vector<std::string> const& values, std::index_sequence<Idxs...>) {
return {from_string<Ts>(values[Idxs])...};
}

For C++11 -- useful if you don't have C++14 (required by Maxim's solution), or in case you want to learn to implement recursive variadic templates:
#include <string>
#include <vector>
#include <tuple>
#include <cassert>
template <std::size_t N, typename T>
struct Undo
{
static void f(T& tuple, const std::vector<std::string>& vec_of_str)
{
Undo<N - 1, T>::f(tuple, vec_of_str);
std::get<N - 1>(tuple) = from_string<
typename std::tuple_element<N - 1, T>::type
>(vec_of_str[N - 1]);
}
};
template <typename T>
struct Undo<0, T>
{
static void f(T&, const std::vector<std::string>&)
{
}
};
template <typename... Ts>
std::tuple<Ts...> undo(const std::vector<std::string>& vec_of_str)
{
assert(vec_of_str.size() == sizeof...(Ts));
std::tuple<Ts...> ret;
Undo<sizeof...(Ts), std::tuple<Ts...>>::f(ret, vec_of_str);
return ret;
}

Related

Use a std::vector to initialize part of another std::vector

Is there a way to "expand" a vector variable in an initialization list to achieve something to the effect of
std::vector<int> tmp1{1,1,2,3,5};
std::vector<int> tmp2{11,tmp1,99};
//tmp2 == {11,1,1,2,3,5,99}
I know it could be done with std::copy or insert. Was wondering if there was list-initialization way achieve that. something like [11,*tmp1,99] in python
With this solution you can have something close the the syntax you want.
(And yes some of the work will be done at runtime)
#include <type_traits>
#include <vector>
namespace details
{
// recursive part
template<typename type_t, typename arg_t, typename... args_t>
void append_to_vector(std::vector<type_t>& vec, arg_t value, args_t&&... values)
{
if constexpr (std::is_same_v<arg_t, std::vector<type_t>>)
{
vec.insert(vec.end(), value.begin(), value.end());
}
else
{
vec.push_back(value);
}
if constexpr (sizeof...(args_t) > 0)
{
append_to_vector(vec, std::forward<args_t>(values)...);
}
}
}
template<typename type_t, typename... args_t>
std::vector<type_t> make_vector(args_t&&... values)
{
std::vector<type_t> retval;
if constexpr (sizeof...(args_t) > 0)
{
details::append_to_vector(retval, std::forward<args_t>(values)...);
}
return retval;
};
int main()
{
auto tmp1 = make_vector<int>(1, 1, 2, 3, 5);
auto tmp2 = make_vector<int>(11, tmp1, 99);
return 0;
}
One option could be to create a make_vector variadic function template that checks if the current argument supports std::begin(). If it supports std::begin(), use the vector's insert member function, otherwise, use emplace_back. That should make it possible to make a vector from any container (with the correct T).
First, one type trait to check if the current argument supports std::begin() and one that checks if it supports std::size(). std::size() will be used to calculate how much space the final vector will need. Reserving space is often used to avoid reallocating space (and moving elements) when populating vectors when the final (or minimal) amount of elements is known - as it is in this case.
#include <iterator>
#include <type_traits>
template<typename T>
class has_begin {
template<typename TT>
static auto test(int) ->
decltype( std::begin(std::declval<const TT&>()), std::true_type() );
template<typename>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<class T>
inline constexpr bool has_begin_v = has_begin<T>::value;
template<typename T>
class has_size {
template<typename TT>
static auto test(int) ->
decltype( std::size(std::declval<const TT&>()), std::true_type() );
template<typename>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<class T>
inline constexpr bool has_size_v = has_size<T>::value;
Then, the actual make_vector function template and helpers to calculate the actual size and determine what to do with one specific argument.
#include <utility>
#include <vector>
namespace detail {
template<class T, class Arg>
size_t make_vector_capacity(Arg&& arg) {
if constexpr(std::is_same_v<T, std::remove_cv_t<std::remove_reference_t<Arg>>>) {
// same type as in the vector, return 1
return 1;
} else if constexpr(has_size_v<Arg>) {
// a container supporting std::size
return std::size(arg);
} else if constexpr(has_begin_v<Arg>) {
// a container but not supporting std::size
return std::distance(std::begin(arg), std::end(arg));
} else {
// fallback
return 1;
}
}
template<class T, class Arg>
void make_vector_helper(std::vector<T>& v, Arg&& arg) {
if constexpr(std::is_same_v<T, std::remove_cv_t<std::remove_reference_t<Arg>>>) {
// same type as in the vector, insert it as-is
v.emplace_back(std::forward<Arg>(arg));
} else if constexpr(has_begin_v<Arg>) {
// arg supports std::begin, use insert
v.insert(v.end(), std::begin(arg), std::end(arg));
} else {
// fallback
v.emplace_back(std::forward<Arg>(arg));
}
}
} // namespace detail
template<class T, class... Args>
std::vector<T> make_vector(Args&&... args) {
std::vector<T> rv;
// a fold expression to calculate the capacity needed:
rv.reserve( (detail::make_vector_capacity<T>(args) + ...) );
// a fold expression to call make_vector_helper for each argument
(detail::make_vector_helper(rv, std::forward<Args>(args)), ...);
return rv;
}
A usage example, mixing different containers and single values of std::string:
#include <initializer_list>
#include <iostream>
#include <list>
#include <vector>
#include <string>
int main() {
using namespace std::string_literals;
const std::string arr[] = {"3"s, "4"s};
const std::vector vec{"5"s, "6"s, "7"s , "8"s};
const std::list list{"9"s,"10"s};
auto init = {"13"s, "14"s, "15"s};
const auto tmp2 = make_vector<std::string>("1"s, "2"s, arr, vec, list,
"11"s, "12"s, init );
for(const auto& e: tmp2) std::cout << e <<' ';
}
Output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Demo
No, it is not possible but you can make a helper that allows can do it:
#include <array>
#include <type_traits>
// Containerize MaybeContainer type if it not equal to T.
template<typename T, typename MaybeContainer>
using impl_containerize = std::conditional_t<std::is_same_v<T,std::decay_t<MaybeContainer>>,std::array<T,1>,MaybeContainer>;
// Concatenate containers
template<typename Container,typename...Args>
Container impl_construct(Args&&...args){
Container c;
(c.insert(c.end(), std::begin(args), std::end(args)), ...);
return c;
}
template<typename Container,typename...Args>
Container construct(Args&&...args){
using T = typename Container::value_type;
return impl_construct<Container, impl_containerize<T,Args>...>({std::forward<Args>(args)}...);
}
#include <iostream>
#include <vector>
int main()
{
std::vector<int> tmp1{1,3,5,7,9};
const auto tmp2 = construct<std::vector<int>>(2,4,tmp1,6);
for(const auto& e: tmp2){
std::cout<<e<<' ';
}
}
Output
2 4 1 3 5 7 9 6

operator>> on a tied tuple with std::ignore

I have stumbled upon the following problem while designing a 'generic' reader:
The following Code works perfectly fine (you need c++1z support to compile as it uses constexpr if, but with minor modifications it should also compile with c++11):
#include <vector>
#include <string>
#include <type_traits>
#include <tuple>
#include <sstream>
using namespace std;
template<int N, class tuple_type>
struct fill_tuple {
static void write(std::vector<std::string>& container, tuple_type& tuple)
{
// use operator >> to fill the N-1'th member of the tuple
std::stringstream(container[N - 1]) >> std::get<N - 1>(tuple);
if constexpr(N > 1){ // Continue if there are till fields to read
fill_tuple<N - 1, tuple_type>::write(container, tuple);
}
}
};
template<class tuple_type>
void read (std::vector<std::string>& container, tuple_type obj){
fill_tuple<std::tuple_size<tuple_type>::value, tuple_type>::write(container, obj);
}
struct some_data {
char a;
char b;
char c;
char d;
auto content() {
return std::tie(a,b,c,d);
}
};
int main()
{
std::vector<std::string> some_strings = {"a","b","c","d"};
// Read some_strings into some_data
some_data foo;
read(some_strings, foo.content());
}
For the sake of simplicity, any bound checks (such as tuple_size <= container size) are omitted.
If i wanted to parse a struct that only has members a,b and d using a container with a size of 4, my intuition was to simply rewrite std::tie(a,b,c,d) to std::tie(a,b,std::ignore,d).
This, however, fails as std::ignore (or gcc's implementation) does not seem to have a operator>> function.
I have already tried checking against std::ignore using std::is_same: std::is_same<typename std::remove_reference<typename std::tuple_element<N - 1,tuple_type>::type>::type, std::ignore>::value, but this also fails.
My question is: Is there a way to check against std::ignore, or even better, replace it entirely without relying prior modifications to the container vector?
replace it entirely without relying prior modifications to the container vector?
instead of using is_same, you may just overload against decltype(ignore); in C++17:
template<typename T>
void read_element( std::string const& s, T& t ) { std::stringstream{s} >> t; }
void read_element( std::string const&, decltype(std::ignore) const& ) { /*do nothing*/ }
std::apply( [&](auto&... args)
{
auto it = some_vector_of_strings.begin();
( read_element( *it++, args ), ... );
}, tuple );
the same idea applies to your C++11 code as well.
As state in comment, std::ignore is not a type, but an object, you have to use decltype(std::ignore) to get the type.
template <typename T>
void read_simple(const std::string& s, T& obj)
{
std::stringstream(s) >> obj;
}
void read_simple(const std::string&, const decltype(std::ignore)&) {}
template <std::size_t ... Is, typename Tuple>
void read(const std::vector<std::string>& container,
Tuple&& obj,
std::index_sequence<Is...>)
{
(read_simple(container[Is], std::get<Is>(obj)), ...);
}
template <typename Tuple>
void read(const std::vector<std::string>& container, Tuple&& obj)
{
read(container,
obj,
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}
Demo

Converting Tuple to string

Since I discovered boost::lexical_cast all conversions are a breeze. That's until trying to convert a tuples elements into a string. Like Int2String or Double2String I want a way to generate a single string from a tuple of arbitrary number of elements
Since the subject of conversion has arbitrary (yet compile time known) dimension, after some research I've looked into boost::fusion and found this solution :
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/noncopyable.hpp>
#include <boost/fusion/include/for_each.hpp>
template <class Sequence>
std::string StringFromSequence(const Sequence &seq)
{
std::string result;
boost::fusion::for_each(seq, toString(result));
return result;
}
Where toString is a functor applying the lexical cast to the objects that's been called for :
struct toString: boost::noncopyable
{
explicit toString(std::string& res) : result(res)
{
}
template <class T>
void operator()(const T& v)
{
result += boost::lexical_cast<std::string>(v);
}
private:
std::string &result;
};
When trying to use that though
std::tuple<int, char, double> tup{ 1, 'a', 2.2 };
toString(tup);
I get a compilation error
error C2893: Failed to specialize function template 'enable_if, void>::type boost::fusion::for_each(const Sequence &,const F &)'
Can someone spot and correct the error ?
Are there any (maybe boost free) alternatives ? (it would take compile time recursion and I was hopefully trying to avoid all that boilerplate code)
Since this question is tagged C++11, here's my take at it:
#include <iostream>
#include <string>
#include <tuple>
template<typename T, T...>
struct integer_sequence { };
template<std::size_t N, std::size_t... I>
struct gen_indices : gen_indices<(N - 1), (N - 1), I...> { };
template<std::size_t... I>
struct gen_indices<0, I...> : integer_sequence<std::size_t, I...> { };
template<typename H>
std::string& to_string_impl(std::string& s, H&& h)
{
using std::to_string;
s += to_string(std::forward<H>(h));
return s;
}
template<typename H, typename... T>
std::string& to_string_impl(std::string& s, H&& h, T&&... t)
{
using std::to_string;
s += to_string(std::forward<H>(h));
return to_string_impl(s, std::forward<T>(t)...);
}
template<typename... T, std::size_t... I>
std::string to_string(const std::tuple<T...>& tup, integer_sequence<std::size_t, I...>)
{
std::string result;
int ctx[] = { (to_string_impl(result, std::get<I>(tup)...), 0), 0 };
(void)ctx;
return result;
}
template<typename... T>
std::string to_string(const std::tuple<T...>& tup)
{
return to_string(tup, gen_indices<sizeof...(T)>{});
}
int main(int argc, char** argv)
{
std::tuple<int, double, float> tup(1, 2.1, 3.2);
std::cout << to_string(tup) << std::endl;
}
If you want to stick with boost::lexical_cast, replace to_string with lexical_cast.
Live output on ideone
Sorry but I'm too lazy to enter the details of where you're making the mistakes, but here's a solution using fusion and C++14 polymorphic lambdas:
#include <tuple>
#include <string>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/fusion/adapted/std_tuple.hpp> // you're missing this huh? it's needed to use fusion with std::tuple
#include <boost/fusion/algorithm/iteration/for_each.hpp>
int main() {
using namespace std;
using namespace boost::fusion;
string result;
for_each(make_tuple(1, 'a', 2.2), [&result](auto &s) {
result += boost::lexical_cast<string>(s) + ' ';
});
cout << result << endl;
}
http://coliru.stacked-crooked.com/a/f110238a317eede9

Template tuple - calling a function on each element

My question is in the code:
template<typename... Ts>
struct TupleOfVectors {
std::tuple<std::vector<Ts>...> tuple;
void do_something_to_each_vec() {
//Question: I want to do this:
// "for each (N)": do_something_to_vec<N>()
//How?
}
template<size_t N>
void do_something_to_vec() {
auto &vec = std::get<N>(tuple);
//do something to vec
}
};
You can quite easily do that with some indices machinery. Given a meta-function gen_seq for generating compile-time integer sequences (encapsulated by the seq class template):
namespace detail
{
template<int... Is>
struct seq { };
template<int N, int... Is>
struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };
template<int... Is>
struct gen_seq<0, Is...> : seq<Is...> { };
}
And the following function templates:
#include <tuple>
namespace detail
{
template<typename T, typename F, int... Is>
void for_each(T&& t, F f, seq<Is...>)
{
auto l = { (f(std::get<Is>(t)), 0)... };
}
}
template<typename... Ts, typename F>
void for_each_in_tuple(std::tuple<Ts...> const& t, F f)
{
detail::for_each(t, f, detail::gen_seq<sizeof...(Ts)>());
}
You can use the for_each_in_tuple function above this way:
#include <string>
#include <iostream>
struct my_functor
{
template<typename T>
void operator () (T&& t)
{
std::cout << t << std::endl;
}
};
int main()
{
std::tuple<int, double, std::string> t(42, 3.14, "Hello World!");
for_each_in_tuple(t, my_functor());
}
Here is a live example.
In your concrete situation, this is how you could use it:
template<typename... Ts>
struct TupleOfVectors
{
std::tuple<std::vector<Ts>...> t;
void do_something_to_each_vec()
{
for_each_in_tuple(t, tuple_vector_functor());
}
struct tuple_vector_functor
{
template<typename T>
void operator () (T const &v)
{
// Do something on the argument vector...
}
};
};
And once again, here is a live example.
Update
If you're using C++14 or later, you can replace the seq and gen_seq classes above with std::integer_sequence like so:
namespace detail
{
template<typename T, typename F, int... Is>
void
for_each(T&& t, F f, std::integer_sequence<int, Is...>)
{
auto l = { (f(std::get<Is>(t)), 0)... };
}
} // namespace detail
template<typename... Ts, typename F>
void
for_each_in_tuple(std::tuple<Ts...> const& t, F f)
{
detail::for_each(t, f, std::make_integer_sequence<int, sizeof...(Ts)>());
}
If you're using C++17 or later you can do this (from this comment below):
std::apply([](auto ...x){std::make_tuple(some_function(x)...);} , the_tuple);
In C++17 you can do this:
std::apply([](auto ...x){std::make_tuple(some_function(x)...);} , the_tuple);
given that some_function has suitable overloads for all the types in the tuple.
This already works in Clang++ 3.9, using std::experimental::apply.
In addition to the answer of #M. Alaggan, if you need to call a function on tuple elements in order of their appearanceā€  in the tuple, in C++17 you can also use a fold expression like this:
std::apply([](auto& ...x){(..., some_function(x));}, the_tuple);
(live example).
ā€ Because otherwise order of evaluation of function arguments is unspecified.
Here's one approach which may work well in your case:
template<typename... Ts>
struct TupleOfVectors {
std::tuple<std::vector<Ts>...> tuple;
void do_something_to_each_vec()
{
// First template parameter is just a dummy.
do_something_to_each_vec_helper<0,Ts...>();
}
template<size_t N>
void do_something_to_vec()
{
auto &vec = std::get<N>(tuple);
//do something to vec
}
private:
// Anchor for the recursion
template <int>
void do_something_to_each_vec_helper() { }
// Execute the function for each template argument.
template <int,typename Arg,typename...Args>
void do_something_to_each_vec_helper()
{
do_something_to_each_vec_helper<0,Args...>();
do_something_to_vec<sizeof...(Args)>();
}
};
The only thing that is a bit messy here is the extra dummy int template parameter to do_something_to_each_vec_helper. It is necessary to make the do_something_to_each_vec_helper still be a template when no arguments remain. If you had another template parameter you wanted to use, you could use it there instead.
If you are not particularly wedded to a solution in the form of generic
"for each" function template then you can use one like this:
#ifndef TUPLE_OF_VECTORS_H
#define TUPLE_OF_VECTORS_H
#include <vector>
#include <tuple>
#include <iostream>
template<typename... Ts>
struct TupleOfVectors
{
std::tuple<std::vector<Ts>...> tuple;
template<typename ...Args>
TupleOfVectors(Args... args)
: tuple(args...){}
void do_something_to_each_vec() {
do_something_to_vec(tuple);
}
template<size_t I = 0, class ...P>
typename std::enable_if<I == sizeof...(P)>::type
do_something_to_vec(std::tuple<P...> &) {}
template<size_t I = 0, class ...P>
typename std::enable_if<I < sizeof...(P)>::type
do_something_to_vec(std::tuple<P...> & parts) {
auto & part = std::get<I>(tuple);
// Doing something...
std::cout << "vector[" << I << "][0] = " << part[0] << std::endl;
do_something_to_vec<I + 1>(parts);
}
};
#endif // EOF
A test program, built with GCC 4.7.2 and clang 3.2:
#include "tuple_of_vectors.h"
using namespace std;
int main()
{
TupleOfVectors<int,int,int,int> vecs(vector<int>(1,1),
vector<int>(2,2),
vector<int>(3,3),
vector<int>(4,4));
vecs.do_something_to_each_vec();
return 0;
}
The same style of recursion can be used in a generic "for_each"
function template without auxiliary indices apparatus:
#ifndef FOR_EACH_IN_TUPLE_H
#define FOR_EACH_IN_TUPLE_H
#include <type_traits>
#include <tuple>
#include <cstddef>
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> &, Func) {}
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> & tpl, Func func)
{
func(std::get<I>(tpl));
for_each_in_tuple<I + 1>(tpl,func);
}
#endif //EOF
And a test program for that:
#include "for_each_in_tuple.h"
#include <iostream>
struct functor
{
template<typename T>
void operator () (T&& t)
{
std::cout << t << std::endl;
}
};
int main()
{
auto tpl = std::make_tuple(1,2.0,"Three");
for_each_in_tuple(tpl,functor());
return 0;
}
I was testing with tuples and metaprograming and found the current thread.
I think my work can inspire someone else although I like the solution of #Andy.
Anyway, just get fun!
#include <tuple>
#include <type_traits>
#include <iostream>
#include <sstream>
#include <functional>
template<std::size_t I = 0, typename Tuple, typename Func>
typename std::enable_if< I != std::tuple_size<Tuple>::value, void >::type
for_each(const Tuple& tuple, Func&& func)
{
func(std::get<I>(tuple));
for_each<I + 1>(tuple, func);
}
template<std::size_t I = 0, typename Tuple, typename Func>
typename std::enable_if< I == std::tuple_size<Tuple>::value, void >::type
for_each(const Tuple& tuple, Func&& func)
{
// do nothing
}
struct print
{
template<typename T>
void operator () (T&& t)
{
std::cout << t << std::endl;
}
};
template<typename... Params>
void test(Params&& ... params)
{
int sz = sizeof...(params);
std::tuple<Params...> values(std::forward<Params>(params)...);
for_each(values, print() );
}
class MyClass
{
public:
MyClass(const std::string& text)
: m_text(text)
{
}
friend std::ostream& operator <<(std::ostream& stream, const MyClass& myClass)
{
stream << myClass.m_text;
return stream;
}
private:
std::string m_text;
};
int main()
{
test(1, "hello", 3.f, 4, MyClass("I don't care") );
}
Boost mp11 has this functionality:
#include <iostream>
#include <string>
#include <boost/mp11.hpp>
using namespace std;
using boost::mp11::tuple_for_each;
std::tuple t{string("abc"), 47 };
int main(){
tuple_for_each(t,[](const auto& x){
cout << x + x << endl;
});
}

In C++, is it possible to get the type of one element of a tuple when the element index is known at runtime?

typedef std::tuple< int, double > Tuple;
Tuple t;
int a = std::get<0>(t);
double b = std::get<1>(t);
for( size_t i = 0; i < std::tuple_size<Tuple>::value; i++ ) {
std::tuple_element<i,Tuple>::type v = std::get<i>(t);// will not compile because i must be known at compile time
}
I know it is possible to write code for get std::get working (see for example iterate over tuple ), is it possible to get std::tuple_element working too?
Some constraints (they can be relaxed):
no variadic templates, no Boost
C++ is a compile-time typed language. You cannot have a type that the C++ compiler cannot determine at compile-time.
You can use polymorphism of various forms to work around that. But at the end of the day, every variable must have a well-defined type. So while you can use Boost.Fusion algorithms to iterate over variables in a tuple, you cannot have a loop where each execution of the loop may use a different type than the last.
The only reason Boost.Fusion can get away with it is because it doesn't use a loop. It uses template recursion to "iterate" over each element and call your user-provided function.
If you want to do without boost, the answers to iterate over tuple already tell you everything you need to know. You have to write a compile-time for_each loop (untested).
template<class Tuple, class Func, size_t i>
void foreach(Tuple& t, Func fn) {
// i is defined at compile-time, so you can write:
std::tuple_element<i, Tuple> te = std::get<i>(t);
fn(te);
foreach<i-1>(t, fn);
}
template<class Tuple, class Func>
void foreach<0>(Tuple& t, Func fn) { // template specialization
fn(std::get<0>(t)); // no further recursion
}
and use it like that:
struct SomeFunctionObject {
void operator()( int i ) const {}
void operator()( double f ) const {}
};
foreach<std::tuple_size<Tuple>::value>(t, SomeFunctionObject());
However, if you want to iterate over members of a tuple, Boost.Fusion really is the way to go.
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/adapted/boost_tuple.hpp>
and in your code write:
boost::for_each(t, SomeFunctionObject());
This an example for boost::tuple. There is an adapter for boost::fusion to work with the std::tuple here: http://groups.google.com/group/boost-list/browse_thread/thread/77622e41af1366af/
No, this is not possible the way you describe it. Basically, you'd have to write your code for every possible runtime-value of i and then use some dispatching-logic (e.g. switch(i)) to run the correct code based on the actual runtime-value of i.
In practice, it might be possible to generate the code for the different values of i with templates, but I am not really sure how to do this, and whether it would be practical. What you are describing sounds like a flawed design.
Here is my tuple foreach/transformation function:
#include <cstddef>
#include <tuple>
#include <type_traits>
template<size_t N>
struct tuple_foreach_impl {
template<typename T, typename C>
static inline auto call(T&& t, C&& c)
-> decltype(::std::tuple_cat(
tuple_foreach_impl<N-1>::call(
::std::forward<T>(t), ::std::forward<C>(c)
),
::std::make_tuple(c(::std::get<N-1>(::std::forward<T>(t))))
))
{
return ::std::tuple_cat(
tuple_foreach_impl<N-1>::call(
::std::forward<T>(t), ::std::forward<C>(c)
),
::std::make_tuple(c(::std::get<N-1>(::std::forward<T>(t))))
);
}
};
template<>
struct tuple_foreach_impl<0> {
template<typename T, typename C>
static inline ::std::tuple<> call(T&&, C&&) { return ::std::tuple<>(); }
};
template<typename T, typename C>
auto tuple_foreach(T&& t, C&& c)
-> decltype(tuple_foreach_impl<
::std::tuple_size<typename ::std::decay<T>::type
>::value>::call(std::forward<T>(t), ::std::forward<C>(c)))
{
return tuple_foreach_impl<
::std::tuple_size<typename ::std::decay<T>::type>::value
>::call(::std::forward<T>(t), ::std::forward<C>(c));
}
The example usage uses the following utility to allow printing tuples to ostreams:
#include <cstddef>
#include <ostream>
#include <tuple>
#include <type_traits>
template<size_t N>
struct tuple_print_impl {
template<typename S, typename T>
static inline void print(S& s, T&& t) {
tuple_print_impl<N-1>::print(s, ::std::forward<T>(t));
if (N > 1) { s << ',' << ' '; }
s << ::std::get<N-1>(::std::forward<T>(t));
}
};
template<>
struct tuple_print_impl<0> {
template<typename S, typename T>
static inline void print(S&, T&&) {}
};
template<typename S, typename T>
void tuple_print(S& s, T&& t) {
s << '(';
tuple_print_impl<
::std::tuple_size<typename ::std::decay<T>::type>::value
>::print(s, ::std::forward<T>(t));
s << ')';
}
template<typename C, typename... T>
::std::basic_ostream<C>& operator<<(
::std::basic_ostream<C>& s, ::std::tuple<T...> const& t
) {
tuple_print(s, t);
return s;
}
And finally, here is the example usage:
#include <iostream>
using namespace std;
struct inc {
template<typename T>
T operator()(T const& val) { return val+1; }
};
int main() {
// will print out "(7, 4.2, z)"
cout << tuple_foreach(make_tuple(6, 3.2, 'y'), inc()) << endl;
return 0;
}
Note that the callable object is constructed so that it can hold state if needed. For example, you could use the following to find the last object in the tuple that can be dynamic casted to T:
template<typename T>
struct find_by_type {
find() : result(nullptr) {}
T* result;
template<typename U>
bool operator()(U& val) {
auto tmp = dynamic_cast<T*>(&val);
auto ret = tmp != nullptr;
if (ret) { result = tmp; }
return ret;
}
};
Note that one shortcoming of this is that it requires that the callable returns a value. However, it wouldn't be that hard to rewrite it to detect whether the return type is void for a give input type, and then skip that element of the resulting tuple. Even easier, you could just remove the return value aggregation stuff altogether and simply use the foreach call as a tuple modifier.
Edit:
I just realized that the tuple writter could trivially be written using the foreach function (I have had the tuple printing code for much longer than the foreach code).
template<typename T>
struct tuple_print {
print(T& s) : _first(true), _s(&s) {}
template<typename U>
bool operator()(U const& val) {
if (_first) { _first = false; } else { (*_s) << ',' << ' '; }
(*_s) << val;
return false;
}
private:
bool _first;
T* _s;
};
template<typename C, typename... T>
::std::basic_ostream<C> & operator<<(
::std::basic_ostream<C>& s, ::std::tuple<T...> const& t
) {
s << '(';
tuple_foreach(t, tuple_print< ::std::basic_ostream<C>>(s));
s << ')';
return s;
}