66

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?

nwp
  • 9,623
  • 5
  • 38
  • 68
  • 9
    [Use a `tuple`](http://stackoverflow.com/q/1198260/1171191) – BoBTFish Dec 16 '15 at 14:10
  • @LogicStuff I can't really make all classes inherit from something with a `virtual void show()`, it should also work with `.size()` and std-containers. – nwp Dec 16 '15 at 14:12
  • 2
    Is the list a run time or compile time one? you can always expand multiple calls to the same function [like here](http://coliru.stacked-crooked.com/a/7543103223c64c86) – Piotr Skotnicki Dec 16 '15 at 14:24
  • 1
    The optimal solution depends on whether that list of things you want to iterate over is fixed or variable. – Richard Hodges Dec 16 '15 at 14:24
  • It is a fixed list. Using the link from @BoBTFish I got [this](http://ideone.com/pRuKxx) working to my satisfaction. Maybe write an answer? – nwp Dec 16 '15 at 14:27
  • 1
    @nwp it is acceptable to answer your own question once you figure it out, and it sounds like your solution is rather different from Richard Hodge's. – BoBTFish Dec 16 '15 at 14:33
  • 2
    From the example, it looks like Window, Toolbar and Widget should be derived from Showable. The answers with variadic templates, while fancy, are the horror from a maintenance point of view. Maybe not that example in isolation, but a program that has 10s or 1000s of such constructs, ..., I would be out. – Sebastian Mach Dec 17 '15 at 14:00
  • FYI: quoted this question in another question of my own. Enjoy: http://stackoverflow.com/questions/34360588/treat-non-polymorphic-objects-in-a-polymorphic-way-with-no-performance-overhead/34360589 – Richard Hodges Dec 18 '15 at 17:09

8 Answers8

64

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

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • 7
    @RichardHodges You don't get more modern than that – Nikos Athanasiou Dec 16 '15 at 15:28
  • How the heck does that work? Is the lambda equivalent to a functor containing a template function? – user253751 Dec 17 '15 at 05:28
  • @immibis that's exactly what it is – Nikos Athanasiou Dec 17 '15 at 06:14
  • 1
    @NikosAthanasiou Nevertheless, it is not "modern", it is "future". Modern C++ usually refers to a currently accepted standard, which is of now C++14. – Mikhail Dec 23 '15 at 08:26
  • @Mikhail Folds are accepted into the language (the fact that the next Standard is not yet released does not change that, there will be no more committee discussions on whether to incorporate folds or not) and implementations already exist that have folds (clang and g++6 - plus you can access clang in VS15). And this not even a case like `#pragma once` which is not Standard c++ but all major compilers have it (folds are not an extension) – Nikos Athanasiou Dec 23 '15 at 08:36
40

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);
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • 3
    Funny how you consider `boost::fusion` oldskool, yet you use a C-style cast. Double standards? – Maxim Egorushkin Dec 16 '15 at 14:56
  • 5
    @MaximEgorushkin :) this is one of the few times when a c-style cast is appropriate, but I can modify it to not use one if necessary. The casts are there to suppress compiler warnings in case your functor returns a value (which is then unused) – Richard Hodges Dec 16 '15 at 14:57
  • There are two camps: people who use C-style casts and people who do not. I like the second camp because it eliminated a class of errors resulting from bad judgement when a C-style cast is appropriate. – Maxim Egorushkin Dec 16 '15 at 15:48
  • 2
    @MaximEgorushkin I of course agree with you. There is no place for c-style casts in almost any code. the static_cast version would look like this: `static_cast(expand { 0, (static_cast(objects.show()), 0)... });` Not sure whether that improves clarity or reduces it. What do you think? – Richard Hodges Dec 16 '15 at 15:58
  • 1
    I always write `static_cast` to suppress unused variable/value warnings, but some of my colleagues do not like it. But I transcend such aversions :))) – Maxim Egorushkin Dec 16 '15 at 16:03
  • 3
    You need the `(void)` cast in the braced-init-list to suppress overloaded commas anyway. – T.C. Dec 16 '15 at 17:17
  • 3
    I wonder why you: 1. Use trailing-return-type exactly once, and that for `main` of all possibilities. 2. Don't take advantage of the implicit `return 0;` in main. – Deduplicator Dec 16 '15 at 18:56
  • 2
    @Deduplicator in all honesty it's utter laziness. I have a self-compiling template file called skeleton.cpp that I copy to a new file for trying out these little demos. Then I push the file into a git repo called "play" just in case someone asks me a question a few weeks later when I've completely forgotten what I was saying. – Richard Hodges Dec 16 '15 at 19:03
21

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.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • That's comforting to know. on my installation of apple clang 7.0 with -O3, the compiler has inlined everything into a series of calls to cout::operator<<. i.e. absolutely zero overhead. If boost does that too it's a testament to the fantastic guys who maintain the library. – Richard Hodges Dec 16 '15 at 15:23
  • @RichardHodges I agree. Easy to use, portable and as fast as non-portable solutions :))) – Maxim Egorushkin Dec 16 '15 at 15:24
  • Which answer is non-portable? – ildjarn Jan 01 '16 at 05:17
  • @ildjarn This answer works for C++98 and on. Provided the lambda is replaced with a callable object, – Maxim Egorushkin Jan 01 '16 at 07:35
  • Ah, between C++ standard versions then; I thought you meant someone's answer was compiler or platform-specific, and I wasn't seeing it. – ildjarn Jan 01 '16 at 23:59
15

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();
    });
}
Community
  • 1
  • 1
nwp
  • 9,623
  • 5
  • 38
  • 68
  • 4
    you are copying `w3, w4, w5, t1` when calling `make_tuple`. it seems like too much of an overhead to copy instances just to print them. [Demo](http://coliru.stacked-crooked.com/a/cf0a4518d8f87de6) – Lorah Attkins Dec 16 '15 at 18:20
  • 1
    @LorahAttkins You are right. Fortunately it also works with `std::tie`, so the copies are avoidable. Fixed. – nwp Dec 16 '15 at 20:16
11

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

GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52
  • 3
    That comes with a runtime cost (maybe the calls get devirtualized), requires modification of all classes and it is not feasible to create a common base class for every function. Additionally it doesn't work for buildins, member variables and std-containers with `.size()`. But generally you are right, this is the traditional solution. – nwp Dec 16 '15 at 16:13
  • For runtime dispatch, why not just `std::bind` the member function calls into an array of `std::function`? No virtual inheritance on the widget required. – Richard Hodges Dec 16 '15 at 16:25
  • @nwp: It comes at a tiny, tiny, tiny, microscopic runtime cost that mostly vanishes within loops or when the show-function is non-trivial. Your solution comes at a paytime cost. In a business, it's in many cases the more expensive solution, both at programming-time as well as maintenance-time. All solutions have their advantages and disadvantages. Does tie() still work once the customer wishes to have flexible user interfaces, for example as seen million times in typical dashboards? – Sebastian Mach Dec 17 '15 at 14:06
  • I'm up-voting this regardless of how OP find run-time overhead annoying, because this also make maintenance of software overall much better, it also relate the three classes which are not arbitrary classes that has nothing to do with each others except for having a `show` function, which is good for a well engineered system. Also it ensure input follows the interface forcing it to have the required function preventing potential issue where it should never. – Khaled.K Dec 23 '15 at 09:30
  • @KhaledAKhunaifer : It's also intrusive – is it not silly to change an entire class hierarchy just so someone can have a more "conventional" for-loop somewhere? – ildjarn Jan 01 '16 at 05:20
8

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(); });
Brian Rodriguez
  • 4,250
  • 1
  • 16
  • 37
  • @Richard There may be a `std::tie` equivalent in the library, but I don't have the time to find it right now. If I find one I'll update. – Brian Rodriguez Dec 16 '15 at 15:50
  • May be it _is the best and most flexible_ but this usage looks too verbose. – Maxim Egorushkin Dec 16 '15 at 15:53
  • @Maxim There you are :) – Brian Rodriguez Dec 16 '15 at 17:15
  • Now, how is this better than using `boost::fusion`? – Maxim Egorushkin Dec 16 '15 at 17:25
  • @Maxim Hana is a header-only library for C++ metaprogramming suited for computations on both types and values. The functionality it provides is a superset of what is provided by the well established Boost.MPL and Boost.Fusion libraries. By leveraging C++11/14 implementation techniques and idioms, Hana boasts faster compilation times and runtime performance on par or better than previous metaprogramming libraries, while noticeably increasing the level of expressiveness in the process. – Brian Rodriguez Dec 16 '15 at 17:33
  • I fail to see the difference in this particular example. And the library does not seem to be available in current boost-1.59. – Maxim Egorushkin Dec 16 '15 at 17:39
  • @Maxim The fact that hana is more flexible and compiles faster is good enough of a reason to prefer it over boost.fusion – Brian Rodriguez Dec 16 '15 at 17:39
  • @Maxin take a look at the link I've provided in my answer. – Brian Rodriguez Dec 16 '15 at 17:41
  • Do I understand it correctly that Hana is not part of Boost, despite its name, location and namespace? – Ruslan Dec 16 '15 at 19:07
  • @Rusian It'll join the official boost in (I believe) the next release. Until then, you'll have to download and include the headers manually (at least it's a header-only library!) – Brian Rodriguez Dec 16 '15 at 19:44
  • 3
    @Ruslan it's on GitHub in the boostorg account. I downloaded and installed it I to my local boost directory last night. Very easy if you're familiar with cmake. It's a very nice library. Now I'm trying to think of a real problem to solve with it :) – Richard Hodges Dec 17 '15 at 09:14
  • @Ruslan Hana will be in Version 1.61.0 and is currently available in the [beta](http://www.boost.org/users/history/version_1_61_0.html)! – Brian Rodriguez Apr 11 '16 at 14:05
4

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;
}
Mikhail
  • 20,685
  • 7
  • 70
  • 146
0

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.

Akira
  • 4,385
  • 3
  • 24
  • 46