CppCon 2015: Vittorio Romeo “`for_each_argument` explained and expanded"

By: CppCon

21   4   2486

Uploaded on 10/14/2015

http://www.Cppcon.org

Presentation Slides, PDFs, Source Code and other presenter materials are available at: https://github.com/cppcon/cppcon2015

During January 2015, Sean Parent posted a very interesting short piece of code on Twitter. The code iteratively iterates at compile-time over any number of function arguments, forwarding them one by one to a callable object.

How does this code work? What are the possible use cases? Can we make it even more generic and useful?

My talk answers all of the questions above, using independently compiled chronologically sequential code segments that show the audience the analysis and improvement process of `for_each_argument`.

Vittorio Romeo is an Italian 20 year old Computer Science student at "Università degli Studi di Messina".

He began programming at a very young age and soon became a C++ enthusiast. While following the evolution of the C++ standard and embracing the newest features, he worked on several open-source projects, including modern general-purpose libraries and free cross-platform indie games.

Vittorio is an active member of the C++ community: he participated as a speaker at CppCon 2014, as a Student/Volunteer at C++Now 2015, as a speaker at Italian C++ Meeting 2015 and as a speaker at his local city's Linux Day 2013 and 2014 events.

He currently maintains a YouTube channel featuring well-received modern C++11 and C++14 tutorials.

When he's not programming, Vittorio enjoys weightlifting and fitness-related activities, competitive/challenging computer gaming (CS:GO, Quake, LoL, Touhou, ...), and good sci-fi movies/tv-series (Firefly, Fringe, Futurama, ...).

Videos Filmed & Edited by Bash Films: http://www.BashFilms.com

Comments (7):

By anonymous    2017-09-20

C++17 solution - fold expression:

template <typename... Ts>
auto anyCondition(Ts... xs)
{
    return (xs || ...);
}

wandbox example


C++11 solution - for_each_argument:

template <typename TF, typename... Ts>
void for_each_argument(TF&& f, Ts&&... xs)
{
    (void)(int[]){(f(std::forward<Ts>(xs)), 0)...};
}

template <typename... Ts>
auto anyCondition(Ts... xs)
{
    bool acc = false;
    for_each_argument([&acc](bool x){ acc = acc || x; }, xs...);
    return acc;
}   

wandbox example

I gave a talk about this snippet at CppCon 2015:
CppCon 2015: Vittorio Romeo “for_each_argument explained and expanded"

Original Thread

By anonymous    2017-09-20

There's no need for polymorphism in your current situation. You could simply use a variadic template + higher-order function to iterate over the vectors. Here's a C++17 solution using a fold expression:

template <typename F, typename... Vectors>
void for_all_vectors(F&& f, Vectors&&... vs)
{
    (std::for_each(std::forward<Vectors>(vs).begin(), 
                   std::forward<Vectors>(vs).end(), 
                   f), ...);
}

Usage:

int main()
{
    std::vector<A> my_a;
    std::vector<B> my_b;
    std::vector<C> my_c;

    for_all_vectors([](const auto& x){ something(x); }, my_a, my_b, my_c);
}

live example on wandbox


In C++11/14 you can replace the fold expression with for_each_argument:

template <typename TF, typename... Ts>
void for_each_argument(TF&& f, Ts&&... xs)
{
    return (void)std::initializer_list<int>{
        (f(std::forward<Ts>(xs)), 0)...};
}

template <typename F, typename... Vectors>
void for_all_vectors(F&& f, Vectors&&... vs)
{
    for_each_argument([&f](auto&& v)
    { 
        std::for_each(v.begin(), v.end(), f);
    }, std::forward<Vectors>(vs)...);
}

live example on wandbox

I explain the idea behind this snippet and expand upon it in this CppCon 2015 talk: "for_each_argument explained and expanded".

Original Thread

By anonymous    2017-09-23

@geza: yes, you can use the [`for_each_argument` trick with `initializer_list`](https://www.youtube.com/watch?v=2l83JlqkzBk), or use variadic template recursion.

Original Thread

By anonymous    2018-01-01

You could use std::apply to unpack the tuple more easily:

template <typename F, typename Tuple>
constexpr bool tuple_all_of(F&& fn, const Tuple& t)
{
    return std::apply([&fn](const auto&... xs)
    {
        return (fn(xs) && ...);
    }, t);
}

The fold expression can be replaced with for_each_argument:

template <typename F, typename Tuple>
constexpr bool tuple_all_of(F&& fn, const Tuple& t)
{
    bool result = true;
    std::apply([&](const auto&... xs)
    {
        for_each_argument([&](const auto& x)
        {
            if(fn(x)) result = false;
        }, xs...);
    }, t);
    return result;
}

template <class F, class... Ts>
void for_each_argument(F f, Ts&&... a)
{
    (void)std::initializer_list<int>{(f(std::forward<Ts>(a)), 0)...};
}

Original Thread

By anonymous    2018-01-01

You don't need to use the preprocessor to store an arbitrary list of types and generate code for them. We can use variadic templates and compile-time strings. You can isolate preprocessor usage to the generation of pairs of names and types.

Firstly, let's define a wrapper for a compile-time sequence of characters. Note that the use of the _cs literal is non-Standard, but available in every major compiler and likely to be part of C++20:

template <char... Cs>
using ct_str = std::integer_sequence<char, Cs...>;

template <typename T, T... Cs>
constexpr ct_str<Cs...> operator""_cs() { return {}; }

We can then define an empty type that stores a pair of a name and a type:

template <typename Name, typename T>
struct named_type
{
    using name = Name;
    using type = T;
};

And a macro to conveniently instantiate it:

#define NAMED_TYPE(type) \
    named_type<decltype(#type ## _cs), type>

You can now use an empty variadic template class to store your types:

template <typename... Ts>
struct named_type_list { };

using my_types = named_type_list<
    NAMED_TYPE(int),
    NAMED_TYPE(long),
    NAMED_TYPE(float),
    NAMED_TYPE(double)
>;

Now, let's see how our main should look:

int main()
{
    const std::string input{"float"};
    handle(my_types{}, input, [](auto t)
    {
        print(typename decltype(t)::name{});
    });
}

The above will print out "float". We can implement handle by unpacking the list of named_type types and using a fold expression to find the matching type name:

template <typename... Ts, typename F>
void handle(named_type_list<Ts...>, const std::string& input, F&& f)
{
    ( (same(input, typename Ts::name{}) && (f(Ts{}), true) ) || ...);
}

Checking for equality between std::string and ct_str is annoying, but doable:

template <std::size_t... Is, char... Cs>
bool same_impl(const std::string& s, 
               std::integer_sequence<char, Cs...>, 
               std::index_sequence<Is...>)
{
    return ((s[Is] == Cs) && ...);
}

template <char... Cs>
bool same(const std::string& s, std::integer_sequence<char, Cs...> seq)
{
    return s.size() >= sizeof...(Cs) 
        && same_impl(s, seq, std::make_index_sequence<sizeof...(Cs)>{});
}

final result live on wandbox.org


Note that this answer uses C++17 fold expressions. You can replace them in C++14 with one of the following techniques:

  • Recursive variadic template function, where the base case returns the default accumulation value, and the recursive case performs an operation between the tail and the head.

  • C++11 pack expansion tricks such as for_each_argument.


The dispatching does short-circuit:

( (same(input, typename Ts::name{}) && (f(Ts{}), true) ) || ...);

This fold expression will stop at the first invocation of f thanks to the , true expression and the || operator.

empirical proof on wandbox.org

Original Thread

Submit Your Video

If you have some great dev videos to share, please fill out this form.