Search code examples
c++boostboost-preprocessor

How to use boost preprocessors to run a sequence of functions?


For example I have a sequence of functions f1, f2 and so on with the same two argument type. I want to using macro

RUN((f1)(f2)(f3), a, b)

to run the sequence of functions with the results

f1(a, b), f2(a, b), f3(a, b)

I think boost preprocessors can help. I tried

#define RUN_DETAIL(pR, pData, pF) pF(a, b);
#define RUN(pFs, a, b) BOOST_PP_SEQ_FOR_EACH(RUN_DETAIL, BOOST_PP_EMPTY, pFs)

But failed. How to do it?

Found an answer as below

#define RUN_DETAIL(pR, pData, pF) pF pData;
#define RUN(pFs, ...) BOOST_PP_SEQ_FOR_EACH(RUN_DETAIL, (__VA_ARGS__), pFs)

This technique works also for calling a sequence of macros.


Solution

  • You don't need to be using macros here. See it Live On Coliru:

    #include <boost/fusion/adapted/std_tuple.hpp>
    #include <boost/fusion/algorithm.hpp>
    #include <boost/phoenix.hpp>
    
    template <typename... F>
    struct sequence_application
    {
        explicit sequence_application(F... fs) : fs(fs...) { }
    
        template <typename... Args>
            void operator()(Args const&... args) const {
                namespace phx = boost::phoenix;
                using namespace phx::arg_names;
    
                boost::fusion::for_each(fs, phx::bind(arg1, phx::cref(args)...));
            }
        private:
            std::tuple<F...> fs;
    };
    
    template <typename... F>
    sequence_application<F...> apply_all(F&&... fs) {
        return sequence_application<F...>(std::forward<F>(fs)...);
    }
    

    Let's demonstrate this:

    #include <iostream>
    #include <string>
    
    void foo(const char* v) { std::cout << __FUNCTION__ << ": " << v << "\n"; }
    void bar(std::string v) { std::cout << __FUNCTION__ << ": " << v << "\n"; }
    
    struct poly_functor {
        template <typename... T>
            void operator()(T&...) const { std::cout << __PRETTY_FUNCTION__ << "\n"; }
    };
    

    You can of course do the direct invocation as in the question:

    poly_functor pf;
    apply_all(&foo, &bar, pf)("fixed invocation is boring");
    

    But, that's rather boring indeed. How about, we keep the compound functor around, and pass it to another algorithm?

    auto combined = apply_all(&foo, &bar, pf);
    
    boost::for_each(
            std::vector<const char*> {"hello", "world", "from", "various"},
            combined);
    

    Now, try that with your macro approach. Macros are not first class language citizens in C++.

    Finally, let's showcase that it works with variadics argument lists:

    struct /*anonymous*/ { int x, y; } point;
    
    // the variadic case
    apply_all(pf)("bye", 3.14, point);
    

    The full demo prints:

    foo: fixed invocation is boring
    bar: fixed invocation is boring
    void poly_functor::operator()(T &...) const [T = <char const[27]>]
    foo: hello
    bar: hello
    void poly_functor::operator()(T &...) const [T = <const char *const>]
    foo: world
    bar: world
    void poly_functor::operator()(T &...) const [T = <const char *const>]
    foo: from
    bar: from
    void poly_functor::operator()(T &...) const [T = <const char *const>]
    foo: various
    bar: various
    void poly_functor::operator()(T &...) const [T = <const char *const>]
    void poly_functor::operator()(T &...) const [T = <char const[4], const double, const <anonymous struct at test.cpp:54:5>>]