Iterating over different types

C++C++11C++14

C++ Problem Overview


Given the following code:

struct Window{
    void show();
    //stuff
}w1, w2, w3;

struct Widget{
    void show();
    //stuff
}w4, w5, w6;

struct Toolbar{
    void show();
    //stuff
}t1, t2, t3;

I want to show a bunch of items:

for (auto &obj : {w3, w4, w5, t1})
    obj.show();

However this does not compile since the std::initializer_list<T> in the for-loop cannot deduce T and in fact there is not really a T that would fit. I don't want to create a type erasure type because of the amount of code required and the unnecessary runtime overhead. How do I correctly write my loop so that the type of obj is deduced for every item in the conceptual list separately?

C++ Solutions


Solution 1 - C++

In C++17 or better you'd use fold expressions, to "walk through" your heterogenous arguments applying the member function:

auto Printer = [](auto&&... args) {
	(args.show(), ...);
};

Printer(w1, w2, w3, w4, w5, w6, t1, t2, t3);

Demo

You can read more on this in my blog

Solution 2 - C++

boost::fusion is awesome but oldskool - it caters for the deficiencies in c++03.

c++11's variadic template expansion to the rescue!

#include <iostream>

struct Window{
    void show() {
        std::cout << "Window\n";
    }
    //stuff
}w1, w2, w3;

struct Widget{
    void show() {
        std::cout << "Widget\n";
    }
    //stuff
}w4, w5, w6;

struct Toolbar{
    void show()
    {
        std::cout << "Toolbar\n";
    }
    //stuff
}t1, t2, t3;


template<class...Objects>
void call_show(Objects&&...objects)
{
    using expand = int[];
    (void) expand { 0, ((void)objects.show(), 0)... };
}

auto main() -> int
{
    call_show(w3, w4, w5, t1);
	return 0;
}

expected output:

Window
Widget
Widget
Toolbar

another, more generic way (requires c++14):

// note that i have avoided a function names that look like
// one in the standard library.

template<class Functor, class...Objects>
void for_all(Functor&& f, Objects&&... objects)
{
    using expand = int[];
    (void) expand { 0, (f(std::forward<Objects>(objects)), 0)... };

}

called like so:

for_all([](auto& thing) { thing.show(); }, w3, w4, w5, t1);

Solution 3 - C++

Another option is to use boost::tuple or std::tuple and boost::fusion::for_each algorithm:

#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/adapted/boost_tuple.hpp>

boost::fusion::for_each(
	boost::tie(w1, w2, w3, w4, w5, w6, t1, t2, t3), // by reference, not a copy
	[](auto&& t) { t.show(); } 
	);

Just out of curiosity, compared the generated assembly output of Richard Hodges's method with the above. With gcc-4.9.2 -Wall -Wextra -std=gnu++14 -O3 -march=native the produced assembly code is identical.

Solution 4 - C++

Based on https://stackoverflow.com/a/6894436/3484570 this works without creating an extra function, boost or inheritance.

Header:

#include <tuple>
#include <utility> 
 
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  for_each(const std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
  { }
 
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  for_each(const std::tuple<Tp...>& t, FuncT f)
  {
	f(std::get<I>(t));
	for_each<I + 1, FuncT, Tp...>(t, f);
  }
 
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...> &&, FuncT) // Unused arguments are given no names.
  { }
 
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...>&& t, FuncT f)
  {
	f(std::get<I>(t));
	for_each<I + 1, FuncT, Tp...>(std::move(t), f);
  }

.cpp:

struct Window{
	void show(){}
	//stuff
}w1, w2, w3;

struct Widget{
	void show(){}
	//stuff
}w4, w5, w6;

struct Toolbar{
	void show(){}
	//stuff
}t1, t2, t3;

int main() {
	for_each(std::tie(w3, w4, w5, t1), [](auto &obj){
		obj.show();
	});
}

Solution 5 - C++

Window, Widget and Toolbar share common interface, so you can create abstract class and make other classes inherit from it:

struct Showable {
    virtual void show() = 0; // abstract method
};

struct Window: Showable{
    void show();
    //stuff
}w1, w2, w3;

struct Widget: Showable{
    void show();
    //stuff
}w4, w5, w6;

struct Toolbar: Showable{
    void show();
    //stuff
}t1, t2, t3;

Then, you can create array of pointers to Showable, and iterate over it:

int main() {
    Showable *items[] = {&w3, &w4, &w5, &t1};
    for (auto &obj : items)
        obj->show();
}

See it working online

Solution 6 - C++

I recommend Boost.Hana, which IMHO is the best and most flexible template meta-programming library available.

#include <boost/hana/ext/std/tuple.hpp>
#include <boost/hana.hpp>

namespace hana = boost::hana;
hana::for_each(std::tie(w3, w4, w5, t1), [](auto& obj) { obj.show(); });

Solution 7 - C++

I think boost::variant is worth mentioning. All the more it has chances to become std::variant in C++17.

int main()
{
  std::vector<boost::variant<Window*, Widget*, Toolbar*>> items = { &w1, &w4, &t1 };

  for (const auto& item : items)
  {
    boost::apply_visitor([](auto* v) { v->show(); }, item);
  }
  return 0;
}

Solution 8 - C++

A late answer but here is general solution with C++14 which works like the boost::fusion::for_each but doesn't require Boost:

#include <tuple>

namespace detail {
template<typename Tuple, typename Function, std::size_t... Is>
void tuple_for_each_impl(Tuple&& tup, Function&& fn, std::index_sequence<Is...>) {
    using dummy = int[];
    static_cast<void>(dummy {
        0, (static_cast<void>(fn(std::get<Is>(std::forward<Tuple>(tup)))), 0)...
    });
}
}

template<typename Function, typename... Args>
void tuple_for_each(std::tuple<Args...>&& tup, Function&& fn) {
    detail::tuple_for_each_impl(std::forward<std::tuple<Args...>>(tup),
            std::forward<Function>(fn), std::index_sequence_for<Args...>{});
}

int main() {
    tuple_for_each(std::tie(w1, w2, w3, w4, w5, w6, t1, t2, t3), [](auto&& arg) {
        arg.show();
    });
}

If you want to achieve more or less the same thing without the std::tuple, you can create a single-function variant of the above code:

#include <utility>

template<typename Function, typename... Args>
void va_for_each(Function&& fn, Args&&... args) {
    using dummy = int[];
    static_cast<void>(dummy {
    	0, (static_cast<void>(fn(std::forward<Args>(args))), 0)...
    });
}

int main() {
    auto action = [](auto&& arg) { arg.show(); };
    va_for_each(action, w1, w2, w3, w4, w5, w6, t1, t2, t3);
}

The drawback of the second example is that it requires to specify the processing function first, therefore doesn't have the same look like the well known std::for_each. Anyway with my compiler (GCC 5.4.0) using -O2 optimization level, they produce the same assembly output.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnwpView Question on Stackoverflow
Solution 1 - C++Nikos AthanasiouView Answer on Stackoverflow
Solution 2 - C++Richard HodgesView Answer on Stackoverflow
Solution 3 - C++Maxim EgorushkinView Answer on Stackoverflow
Solution 4 - C++nwpView Answer on Stackoverflow
Solution 5 - C++GingerPlusPlusView Answer on Stackoverflow
Solution 6 - C++Brian RodriguezView Answer on Stackoverflow
Solution 7 - C++MikhailView Answer on Stackoverflow
Solution 8 - C++AkiraView Answer on Stackoverflow