mpl_nested_loop.hh 6.05 KB
Newer Older
1
2
// graph-tool -- a general graph modification and manipulation thingy
//
Tiago Peixoto's avatar
Tiago Peixoto committed
3
// Copyright (C) 2006-2020 Tiago de Paula Peixoto <tiago@skewed.de>
4
//
5
6
7
8
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option) any
// later version.
9
//
10
11
12
13
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
// details.
14
//
15
// You should have received a copy of the GNU Lesser General Public License
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#ifndef NESTED_FOR_LOOP_HH
#define NESTED_FOR_LOOP_HH

#include <boost/mpl/for_each.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/empty.hpp>
#include <boost/any.hpp>

namespace boost
{
namespace mpl
{
// The following is a implementation of a nested for_each loop, which runs a
// given Action functor for each combination of its arguments, given by the type
// ranges, as such:
//
//     struct foo
//     {
//         template<class T1, class T2, class T3>
//         void operator()(T1, T2, T3) const
//         {
//             ...
//         }
//     };
//
//     ...
//
//     typedef mpl::vector<int,float,long> r1;
//     typedef mpl::vector<string,double> r2;
//     typedef mpl::vector<size_t,char> r3;
//
49
50
51
//     any x = float(2);
//     any y = string("foo");
//     any z = size_t(42);
52
//
53
54
55
56
//     bool found = nested_for_each<r1,r2,r3>(foo(), x, y, z);
//
// The code above will run iterate through all combinations of foo::operator(T1,
// T2, T3) and call the one that corresponds to the actual types stored in x, y,
57
58
// and z. If the types are not found during iteration, we have found == true,
// otherwise found == false. This provides a more general compile-time to
59
60
// run-time bridge than the simpler mpl::for_each().

61
62
63

// this is a functor wrapper that will perform an any_cast<> in each in an array
// of arguments according to the called types. If the cast is successful, the
64
// function will be called with those types, and true will be returned.
65

66
67
template <class Action, std::size_t N>
struct all_any_cast
68
{
69
    all_any_cast(Action a, std::array<any*, N>& args)
70
        : _a(a), _args(args) {}
71
72

    template <class... Ts>
73
    [[gnu::always_inline]]
74
    bool operator()(Ts*... vs) const
75
    {
76
        return dispatch(std::make_index_sequence<sizeof...(Ts)>(), vs...);
77
78
79
    }

    template <class T>
80
    T* try_any_cast(boost::any& a) const
81
    {
82
83
        T* t = any_cast<T>(&a);
        if (t != nullptr)
84
85
            return t;

86
87
        std::reference_wrapper<T>* tr = any_cast<std::reference_wrapper<T>>(&a);
        if (tr != nullptr)
88
89
90
            return &(tr->get());

        return nullptr;
91
92
    }

93
    template <std::size_t... Idx, class... Ts>
94
    [[gnu::always_inline]]
95
    bool dispatch(std::index_sequence<Idx...>, Ts*...) const
96
    {
97
98
99
100
101
        static_assert(sizeof...(Idx) == N,
                      "all_any_cast: wrong number of arguments");

        std::tuple<std::add_pointer_t<Ts>...> args;
        if (((std::get<Idx>(args) = try_any_cast<Ts>(*_args[Idx])) && ...))
102
        {
103
104
105
            // successful set of casts. Dereference and call action.
            std::apply([this](auto*... arg){ _a(*arg...); }, args);
            return true;
106
        }
107
108

        return false;
109
110
111
    }

    Action _a;
112
    std::array<any*, N>& _args;
113
114
};

115
116
117
// recursion-free variadic version of for_each
template <class...>
struct for_each_variadic;
118

119
120
template <class F, class... Ts>
struct for_each_variadic<F,std::tuple<Ts...>>
121
{
122
    bool operator()(F f)
123
    {
124
125
        auto call = [&](auto&& arg) -> bool {return f(std::forward<decltype(arg)>(arg));};
        return (call(typename std::add_pointer<Ts>::type()) || ...);
126
    }
127
128
};

129
130
131
// convert mpl sequence to std::tuple
template <class T, class R>
struct to_tuple_imp;
132

133
134
template <class... Ts, class X>
struct to_tuple_imp<std::tuple<Ts...>, X>
135
{
136
    typedef std::tuple<Ts..., X> type;
137
};
138

139
140
template <class Seq>
struct to_tuple
141
{
142
143
144
145
146
147
148
149
    typedef typename mpl::fold<Seq, std::tuple<>,
                               to_tuple_imp<mpl::_1, mpl::_2>>::type type;
};

// nested type loops via variadic templates

template <class...>
struct inner_loop {};
150

151
152
template <class Action, class... Ts>
struct inner_loop<Action, std::tuple<Ts...>>
153
{
154
    inner_loop(Action a): _a(a) {}
155

156
    template <class T>
157
    [[gnu::always_inline]]
158
159
160
    bool operator()(T*) const
    { return _a(typename std::add_pointer<Ts>::type()...,
                typename std::add_pointer<T>::type()); }  // innermost loop
161
162
    Action _a;
};
163

164
165
template <class Action, class... Ts, class TR1, class... TRS>
struct inner_loop<Action, std::tuple<Ts...>, TR1, TRS...>
166
167
{
    inner_loop(Action a): _a(a) {}
168

169
    template <class T>
170
    [[gnu::always_inline]]
171
    bool operator()(T*) const
172
    {
173
174
        typedef inner_loop<Action, std::tuple<Ts..., T>, TRS...> inner_loop_t;
        typedef typename to_tuple<TR1>::type tr_tuple;
175
        return for_each_variadic<inner_loop_t, tr_tuple>()(inner_loop_t(_a));
176
177
178
    }
    Action _a;
};
179

180
// final function
181

182
template <class TR1, class... TRS, class Action, class... Args>
183
bool nested_for_each(Action a, Args&&... args)
184
{
185
    std::array<any*, sizeof...(args)> as{{&args...}};
186
    auto b = all_any_cast<Action, sizeof...(args)>(a, as);
187
188
189
190
    typedef decltype(b) action_t;
    typedef typename to_tuple<TR1>::type tr_tuple;
    typedef inner_loop<action_t, std::tuple<>, TRS...> inner_loop_t;
    return for_each_variadic<inner_loop_t, tr_tuple>()(inner_loop_t(b));
191
}
192

193
194
195
template <class TR1, class... TRS, class Action>
void nested_for_each(Action a)
{
196
197
198
    typedef typename to_tuple<TR1>::type tr_tuple;

    // wrap action into a bool-returning function
199
    auto ab = [=](auto*... args) -> bool { a(args...); return false; };
200
201
202

    typedef inner_loop<decltype(ab), std::tuple<>, TRS...> inner_loop_t;
    for_each_variadic<inner_loop_t, tr_tuple>()(inner_loop_t(ab));
203
204
205
}


206
207
208
209
} // mpl namespace
} // boost namespace

#endif //NESTED_FOR_LOOP_HH