Iterate vector with 'grouped' elements? - c++

Consider the example of 4 colors packed in the same vector (this aspect of the design cannot be changed easily - eg. from a third party):
std::vector rgb_colors = {1,1,1,2,2,2,3,3,3,4,4,4};
The following can work:
for (size_t ci = 0; ci + 2 < rgb_colors.size(); ci+=3) {
auto& red_component = rgb_colors[ci];
auto& green_component = rgb_colors[ci+1];
auto& blue_component = rgb_colors[ci+2];
//...
}
In a vacuum this method is 'innocent' enough.
However, with modern c++/etc., I usually steer away from this method of coding, since it's more brittle/error-prone/redundant/etc. and now prefers range based for loops, iterators, etc. .
So what is a more expressive or elegant way to solve this ?
Update:
Added a note about that the 'data layout' cannot be easily changed.

As mentioned in the comments, create a struct representing a color or rather use one that probably comes with whatever library you are using for graphics, so that you can interact with it properly:
struct color {
int red, green, blue;
// Add members as you see fit
};
std::vector rgb_colors = {color{1,1,1}, color{2,2,2}, color{3,3,3}, color{4,4,4}};
// or
// std::vector<color> rgb_colors = {{1,1,1}, {2,2,2}, {3,3,3}, {4,4,4}};
for (auto& c : rgb_colors) {
auto& red_component = c.red;
auto& green_component = c.green;
auto& blue_component = c.blue;
//...
}
Or, focusing less on the concrete example, you can use a range/iterator adaptor like boost::adaptors::stride:
#include <boost/range/adaptor/strided.hpp>
//...
for (auto& c : boost::adaptors::stride(rgb_colors, 3)) {
auto& red_component = (&c)[0];
auto& green_component = (&c)[1];
auto& blue_component = (&c)[2];
//...
}
This still (as in your example) requires you to make sure that the length of the vector is divisible by 3 to avoid UB and that the elements are part of a continuous array (as std::vector provides) in the first place, so that the pointer arithmetic (&c)[i] has well-defined behavior.

As already noted in comments, the input data could be better defined to match its meaning, and that would avoid the issue best.
But if you're for some reason stuck with a sequence where N elements at a time mean something, and you'll need to work with it a lot, here's how I'd use boost::iterator_adaptor to create an iterator which grabs N elements at a time.
#include <cstddef>
#include <utility>
#include <iterator>
#include <tuple>
#include <boost/iterator/iterator_adaptor.hpp>
namespace N_elements_iterator_detail {
template <typename T, std::size_t N>
using ignore_N = T;
template <typename T, typename IndSeq>
struct repeat_type_helper;
template <typename T, std::size_t ...Inds>
struct repeat_type_helper<T, std::index_sequence<Inds...>>
{ using type = std::tuple<ignore_N<T, Inds>...>; };
template <typename T, std::size_t N>
using repeat_type = typename repeat_type_helper<T, std::make_index_sequence<N>>::type;
template <typename IndSeq>
struct deref_helper;
template <std::size_t ...Inds>
struct deref_helper<std::index_sequence<Inds...>>
{
template <typename RetType, typename FwdIter>
static RetType deref(FwdIter iter) {
FwdIter iter_array[] =
{ (static_cast<void>(Inds), iter++) ... };
return RetType( *iter_array[Inds]... );
}
};
}
template <typename FwdIter,
typename std::iterator_traits<FwdIter>::difference_type N,
std::enable_if_t<(N>0)>* = nullptr>
class N_elements_iterator :
public boost::iterator_adaptor<
N_elements_iterator<FwdIter, N>, // CRTP Derived type
FwdIter, // Implementation base iterator
N_elements_iterator_detail::repeat_type<
typename std::iterator_traits<FwdIter>::value_type, N>,
boost::use_default, // Iterator category
N_elements_iterator_detail::repeat_type<
typename std::iterator_traits<FwdIter>::reference, N>>
{
public:
using N_elements_iterator::iterator_adaptor::iterator_adaptor;
private:
friend class boost::iterator_core_access;
typename N_elements_iterator::reference dereference() const {
return N_elements_iterator_detail::deref_helper<
std::make_index_sequence<N>>
::template deref<typename N_elements_iterator::reference>(
this->base());
}
void advance(typename N_elements_iterator::difference_type dist) {
std::advance(this->base_reference(), N * dist);
}
void increment() { advance(1); }
void decrement() { advance(-1); }
// N must be the same, but we can subtract for example
// N_elements_iterator<C::iterator, N> and N_elements_iterator<C::const_iterator, N>
template <typename OtherIter>
auto distance_to(const N_elements_iterator<OtherIter, N>& other) const {
return N * std::distance(this->base(), other.base());
}
};
template <auto N, typename FwdIter>
N_elements_iterator<FwdIter, N> make_N_elements_iterator(FwdIter iter)
{ return N_elements_iterator<FwdIter, N>{ iter }; }
// -----
#include <vector>
#include <iostream>
int main() {
std::vector rgb_colors = {1,1,1,2,2,2,3,3,3,4,4,4};
for (auto c = make_N_elements_iterator<3>(rgb_colors.cbegin());
c != make_N_elements_iterator<3>(rgb_colors.cend());
++c) {
auto&& [red, green, blue] = *c;
std::cout << "red " << red
<< ", green " << green
<< ", blue " << blue
<< '\n';
}
}
See this program on coliru.

Related

How to return multi- dimensional-vector of given dimension and type?

Is it possible in modern c++ to write a function that, given a number n and a type, return a n-dimensional vector type.
1,float -> return std::vector<float>
2,float -> return std::vector<std::vector<float>>
and so on...
Here is a compile time approach using template specialization. We apply std::vector<> recursively until we reach the specialization for N == 0, where the final type is set to T:
#include <type_traits>
#include <vector>
template<std::size_t N, typename T>
struct recursive_vector {
using type = std::vector<typename recursive_vector<N - 1, T>::type>;
};
template<typename T>
struct recursive_vector<0, T> {
using type = T;
};
template<std::size_t N, typename T>
using vec = recursive_vector<N, T>::type;
so vec<3, float> becomes vector<vector<vector<float>>>:
int main() {
vec<3, float> v;
using std::vector;
static_assert(std::is_same_v<decltype(v), vector<vector<vector<float>>>>);
}
Try out the code, here.
Is it possible in modern C++ to write a function that given a number n and a type return a n-dimensional vector type?
Compile time
If n is known at compile time, the job easy, which is already given in the other answer. Alternatively, you could also do:
#include <vector>
template<typename T, std::size_t N>
struct vec : public vec<std::vector<T>, N - 1u> {};
template<typename T> struct vec<T, 0u> { using type = T; };
template<typename T, std::size_t N> using vec_t = typename vec<T, N>::type;
Now vec_t<float, 5u> means std::vector<std::vector<std::vector<std::vector<std::vector<float>>>>>
(See a live demo in godbolt.org)
Run time
When n is run time depended, one possible solution is to wrap the possible multidimensional vectors types to std::variant (Since c++17).
Following is an example code.
#include <vector>
#include <variant> // std::variant
// type aliases for the multi-dim vectors
template<typename T> using Vec1D = std::vector<T>;
template<typename T> using Vec2D = Vec1D<Vec1D<T>>;
template<typename T> using Vec3D = Vec1D<Vec2D<T>>;
// ... so on (hope that you wouldn't need more than 4 or 5!)
template<typename T>
using VecVariants = std::variant<Vec1D<T>, Vec2D<T>, Vec3D<T>/*, so on ...*/>;
template<typename T> auto getVector_of(const std::size_t n)
{
if (n == 1) return VecVariants<T>{Vec1D<T>{}};
if (n == 2) return VecVariants<T>{Vec2D<T>{}};
if (n == 3) return VecVariants<T>{Vec3D<T>{}};
// ... so on
return VecVariants<T>{Vec3D<T>{}}; // some default return!
}
template<typename T> void doSomething(const VecVariants<T>& vec)
{
std::visit([](auto&& arg) {
using Type = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<Type, Vec1D<T>>)
std::cout << "... do something with Vec1D\n";
else if constexpr (std::is_same_v<Type, Vec2D<T>>)
std::cout << "... do something with Vec2D\n";
else if constexpr (std::is_same_v<Type, Vec3D<T>>)
std::cout << "... do something with Vec3D\n";
// .... so on
else
std::cout << "... default!\n";
}, vec);
}
Now you can pass run time n
int main()
{
std::size_t n{ 5 };
while (n)
doSomething(getVector_of<float>(n--)); // n must be 0u < n <= MAX_n you defined!
}
(See a live demo in godbolt.org)

An unordered_map that returns pairs of different types c++

I am trying to implement an std::unordered_map that returns pairs of either double, int or std::string. The keys for the map are std::strings. Below is what I have tried so far:
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
#include <utility>
#include <vector>
// A base class for boundary class
class Boundbase {
public:
Boundbase(){};
virtual ~Boundbase(){};
};
// A different map of boundaries for each different data type
template <class dType>
class Boundary : public Boundbase {
std::pair<dType, dType> bpair;
public:
//Constructor
Boundary(const std::string &lbound,
const std::string &ubound) {
setbound(lbound, ubound);
};
//A method to set boundary pair
void setbound(const std::string &lbound,
const std::string &ubound);
// A method to get boundary pair
std::pair<dType, dType> getbound() {return bpair;}
};
// Class to hold the different boundaries
class Boundaries {
std::unordered_map<std::string, Boundbase*> bounds;
public:
//Constructor
Boundaries() {};
// A method to set boundary map
void setboundmap(std::unordered_map<std::string,
std::vector<std::string>> xtb);
// A template to get boundaries.
std::unordered_map<std::string, Boundbase*> getbounds()
{return bounds;}
};
// A method to set covariate boundary
template <class dType> void
Boundary<dType>::setbound(const std::string &lbound,
const std::string &ubound) {
dType val;
std::istringstream isa(lbound);
while(isa >> val) {
bpair.first = val;
}
std::istringstream isb(ubound);
while(isb >> val) {
bpair.second = val;
}
}
// A method to set boundary map
void Boundaries::setboundmap(std::unordered_map<std::string,
std::vector<std::string>> xtb) {
for(auto s : xtb) {
char type = s.second[1][0];
switch(type) {
case 'd': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<double>(
s.second[2], s.second[3]);
bounds.insert(opair);
}
break;
case 'i': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<int>(
s.second[2], s.second[3]);
bounds.insert(opair);
break;
}
case 'c': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<std::string>(
s.second[2], s.second[2]);
bounds.insert(opair);
break;
}
}
}
}
This compiles ok using g++. When I try to run it though ( as follows):
int main() {
Data D;
Boundaries B;
std::ifstream iss("tphinit.txt");
D.read_lines(iss);
auto dbounds = D.get_xtypebound();
B.setboundmap(dbounds);
auto tbounds = B.getbounds();
auto sbound = tbounds["X1"];
std::cout << sbound->bpair.first << ","
<< sbound->bpair.second << std::endl;
}
I get 'class Boundbase' has no member named 'bpair' which is true because I am pointing to the base class and not the derived class. As far as I can tell, trying to get the derived member bpair requires that I use the visitor pattern. Now, it is clear that I am noob so when I had a look at different ways of doing this on SO I was a little in over my head (no reflection on the authors, just on my inexperience).
So my main question is: Is this the best and simplest way to go about this? I would like to avoid boost::variant if at all possible (mainly for the sake of purity: this cannot be that difficult). A sub-question is whether I have to use the visitor pattern or is there a better/simpler way to get the member pbair?
I will have to perform this lookup many times so I am hoping to make it as fast as possible but using the stl for the sake of simplicity.
Make your values std variants over the 3 types.
Failing that, boost variant.
Std and boost variant really are what you want. You'll end up implementing some subset of its implementation.
Failing that, find a tutorial on how to implement ones of them, or use std any. Failing that, dynamic casts around an otherwise useless wrapper type with a virtual dtor stored in a unique ptr, or do manual RTTI with try get methods.
This just gets increasingly ugly and/or inefficient however.
Boost variant, and std variant from it, was implemented for a reason, and that reason was solving the exact problem you are describing in an efficient manner.
#include <tuple>
#include <utility>
#include <string>
template<class...Ts>
struct destroy_helper {
std::tuple<Ts*...> data;
destroy_helper( std::tuple<Ts*...> d ):data(d){}
template<class T>
static void destroy(T* t){ t->~T(); }
template<std::size_t I>
void operator()(std::integral_constant<std::size_t, I>)const {
destroy( std::get<I>( data ) );
}
};
struct construct_helper {
template<class T, class...Args>
void operator()(T* target, Args&&...args)const {
::new( (void*)target ) T(std::forward<Args>(args)...);
}
};
template<std::size_t...Is>
struct indexes {};
template<std::size_t N, std::size_t...Is>
struct make_indexes:make_indexes<N-1, N-1, Is...> {};
template<std::size_t...Is>
struct make_indexes<0, Is...>{
using type=indexes<Is...>;
};
template<std::size_t N>
using make_indexes_t = typename make_indexes<N>::type;
template<class F>
void magic_switch( std::size_t i, indexes<>, F&& f ) {}
template<std::size_t I0, std::size_t...Is, class F>
void magic_switch( std::size_t i, indexes<I0,Is...>, F&& f )
{
if (i==I0) {
f( std::integral_constant<std::size_t, I0>{} );
return;
}
magic_switch( i, indexes<Is...>{}, std::forward<F>(f) );
}
template<class T0>
constexpr T0 max_of( T0 t0 ) {
return t0;
}
template<class T0, class T1, class...Ts>
constexpr T0 max_of( T0 t0, T1 t1, Ts... ts ) {
return (t1 > t0)?max_of(t1, ts...):max_of(t0, ts...);
}
template<class...Ts>
struct Variant{
using Data=typename std::aligned_storage< max_of(sizeof(Ts)...), max_of(alignof(Ts)...)>::type;
std::size_t m_index=-1;
Data m_data;
template<std::size_t I>
using alternative_t=typename std::tuple_element<I, std::tuple<Ts...>>::type;
using pointers=std::tuple<Ts*...>;
using cpointers=std::tuple<Ts const*...>;
template<class T> T& get(){ return *reinterpret_cast<T*>(&m_data); }
template<class T> T const& get() const { return *reinterpret_cast<T*>(&m_data); }
template<std::size_t I>
alternative_t<I>& get(){ return std::get<I>(get_pointers()); }
template<std::size_t I>
alternative_t<I> const& get()const{ return std::get<I>(get_pointers()); }
pointers get_pointers(){
return pointers( (Ts*)&m_data... );
}
cpointers get_pointers()const{
return cpointers( (Ts const*)&m_data... );
}
std::size_t alternative()const{return m_index;}
void destroy() {
if (m_index == -1)
return;
magic_switch(m_index, make_indexes_t<sizeof...(Ts)>{}, destroy_helper<Ts...>(get_pointers()));
}
template<std::size_t I, class...Args>
void emplace(Args&&...args) {
destroy();
construct_helper{}( std::get<I>(get_pointers()), std::forward<Args>(args)... );
m_index = I;
}
Variant()=default;
Variant(Variant const&)=delete;//todo
Variant&operator=(Variant const&)=delete;//todo
Variant(Variant &&)=delete;//todo
Variant&operator=(Variant &&)=delete;//todo
~Variant(){destroy();}
};
int main() {
Variant<int, double, std::string> bob;
bob.emplace<0>( 7 );
bob.emplace<1>( 3.14 );
bob.emplace<2>( "hello world" );
}
here is a really simple variant interface.
The hard part is turning a runtime index into which of the compile time indexes you want to use. I call that the magic switch problem.
You might also want to implement apply visitor.
...
Or...
template<class T>
struct Derived;
struct Base {
virtual ~Base() {}
template<class T>
friend T* get(Base* base) {
Derived<T>* self = dynamic_cast<T*>(base);
return self?&self.t:nullptr;
}
template<class T>
friend T const* get(Base const* base) {
Derived<T> const* self = dynamic_cast<T const*>(base);
return self?&self.t:nullptr;
}
};
template<class T>
struct Derived:Base {
Derived(T in):t(std::move(in)){}
T t;
};
std::unordered_map<std::string, std::unique_ptr<Base>> map;
map["hello"] = std::unique_ptr<Base>( new Derived<int>(-1) );
map["world"] = std::unique_ptr<Base>( new Derived<double>(3.14) );
int* phello = get<int>(map["hello"]);
if (phello) std::cout << *hello << "\n";
double* pworld = get<double>(map["world"]);
if (pworld) std::cout << *world << "\n";
which is a seriously bargain-basement std::any.

Constructing an iterator_range with variadic templates and runtime indices

I have a collection of equally-sized vectors and want to provide an interface for the user to obtain an iterator range over a subset of these vectors.
The following example shows the problematic line inside getRange: its idea is to receive a bunch of types (specifying the types of vectors) and equally many indices (specifying the locations of the vectors). The code compiles, but the problem is that i++ never gets executed as intended, i.e., the call is always with just i (which equals 0). This will also lead to runtime errors via boost::get if the user tries to get distinct types.
This is probably a well-known issue. What's a neat solution to it?
#include <vector>
#include <boost/variant.hpp>
#include <boost/range/combine.hpp>
template <typename... T>
struct VectorHolder
{
template<typename X>
using Iterator = typename std::vector<X>::const_iterator;
std::vector<boost::variant<std::vector<T>...> > vecs_;
template <typename X>
auto begin(int idx) const {
return boost::get<std::vector<X> >(vecs_.at(idx)).cbegin();
}
template <typename X>
auto end(int idx) const {
return boost::get<std::vector<X> >(vecs_.at(idx)).cend();
}
};
template <typename... T, typename VectorHolder>
auto getRange(const VectorHolder& vh, const std::vector<int>& idx)
{
assert(sizeof...(T) == idx.size());
// Fetch a boost::iterator_range over the specified indices
std::size_t i = 0;
std::size_t j = 0;
// PROBLEM: i and j not incremented as intended
return boost::combine(
boost::iterator_range<VectorHolder::Iterator<T>>(
vh.begin<T>(idx[i++]), vh.end<T>(idx[j++]))...);
}
int main()
{
VectorHolder<bool, int, double> vh;
vh.vecs_.push_back(std::vector<int>(5, 5));
vh.vecs_.push_back(std::vector<bool>(5));
vh.vecs_.push_back(std::vector<double>(5, 2.2));
vh.vecs_.push_back(std::vector<int>(5, 1));
const std::vector<int> idx = { 0, 3 };
for (auto t : getRange<int, int>(vh, idx))
{
std::cout << t.get<0>() << " " << t.get<1>() << "\n";
}
}
std::index_sequence helps:
template <typename... Ts, typename VectorHolder, std::size_t ... Is>
auto getRange(const VectorHolder& vh, const std::vector<int>& idx, std::index_sequence<Is...>)
{
assert(sizeof...(Ts) == idx.size());
return boost::combine(
boost::iterator_range<typename VectorHolder::template Iterator<Ts>>(
vh.template begin<Ts>(idx[Is]), vh.template end<Ts>(idx[Is]))...);
}
template <typename... Ts, typename VectorHolder>
auto getRange(const VectorHolder& vh, const std::vector<int>& idx)
{
return getRange<Ts...>(vh, idx, std::index_sequence_for<Ts...>());
}
Demo

Template function taking a std::vector or std::array

I have a function that currently accepts 2 vectors that can contain any plain old data ...
template <class T>
void addData(const vector<T>& yData, vector<T> xData)
{ .. }
Question:
Would it be possible to modify it to take two std::array or two std::vector, or even a combination thereof, given that these containers take a different number of template arguments?
Sure, it's just a matter of creating a suitable type trait. The example just uses a function f() with one argument but it is trivial to extend to take any number of arguments.
#include <array>
#include <vector>
#include <deque>
#include <utility>
#include <cstddef>
template <typename T>
struct is_array_or_vector {
enum { value = false };
};
template <typename T, typename A>
struct is_array_or_vector<std::vector<T, A>> {
enum { value = true };
};
template <typename T, std::size_t N>
struct is_array_or_vector<std::array<T, N>> {
enum { value = true };
};
template <typename T>
typename std::enable_if<is_array_or_vector<T>::value>::type
f(T const&)
{
}
int main()
{
f(std::vector<int>()); // OK
f(std::array<int, 17>()); // OK
f(std::deque<int>()); // ERROR
}
Why not just use this, which works with any container using random-access iterators, including plain old arrays. If you can use iteration instead of indexing, you can do away with the random-access requirement as well.
template <typename Cnt1, typename Cnt2>
void addData(const Cnt1& yData, Cnt2 xData) // is pass-by-value intended?
{
using std::begin;
using std::end;
typedef decltype(*begin(yData)) T;
const auto sizeY = end(yData) - begin(yData);
const auto sizeX = end(xData) - begin(xData);
// ...
}
C++03 version (doesn't support plain old arrays):
template <typename Cnt1, typename Cnt2>
void addData(const Cnt1& yData, Cnt2 xData) // is pass-by-value intended?
{
typedef Cnt1::value_type T;
const size_t sizeY = yData.end() - yData.begin();
const size_t sizeX = xData.end() - xData.begin();
// ...
}
An alternative solution:
#include <iostream>
#include <vector>
#include <array>
using std::vector;
using std::array;
template <typename Container>
struct container_helper; // undefined
template <typename T>
struct container_helper<vector<T>>
{
explicit container_helper(vector<T>& data)
: _data(data)
{}
T* get_data()
{ return &_data[0]; }
size_t get_size()
{ return _data.size(); }
private:
vector<T>& _data;
};
template <typename T, size_t N>
struct container_helper<array<T,N>>
{
explicit container_helper(array<T,N>& data)
: _data(data)
{}
T* get_data()
{ return &_data[0]; }
size_t get_size()
{ return N; }
private:
array<T,N>& _data;
};
template <typename Container1, typename Container2>
void add_data(Container1& c1, Container2& c2)
{
container_helper<Container1> c1_helper(c1);
container_helper<Container2> c2_helper(c2);
/* do whatever you want with the containers */
std::cout << "c1 size " << c1_helper.get_size() << std::endl;
std::cout << "c2 size " << c2_helper.get_size() << std::endl;
}
int main()
{
vector<int > v_ints(3);
array<int, 2> a_ints;
add_data(v_ints, a_ints);
}

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;
}