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-2019 Tiago de Paula Peixoto <tiago@skewed.de>
4
5
6
7
8
9
10
11
12
13
14
15
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
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// 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
64
65
66
67
68

struct stop_iteration: public std::exception {};

// 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
// function will be called with those types, and stop_iteration will be thrown.
template <class Action, std::size_t N>
struct all_any_cast
69
{
70
    all_any_cast(Action a, std::array<any*, N>& args)
71
        : _a(a), _args(args) {}
72
73

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

80
81
    struct fail_cast {};

82
83
84
85
86
87
88
    template <class T>
    T& try_any_cast(boost::any& a) const
    {
        try
        {
            return any_cast<T&>(a);
        }
89
        catch (bad_any_cast&)
90
        {
91
92
93
94
95
96
97
98
            try
            {
                return any_cast<std::reference_wrapper<T>>(a);
            }
            catch (bad_any_cast&)
            {
                throw fail_cast();
            }
99
        }
100
101
    }

102
    template <std::size_t... Idx, class... Ts>
103
    [[gnu::always_inline]]
104
    void dispatch(std::index_sequence<Idx...>, Ts*...) const
105
    {
106
        try
107
        {
108
109
            static_assert(sizeof...(Idx) == N,
                          "all_any_cast: wrong number of arguments");
110
            _a(try_any_cast<Ts>(*_args[Idx])...);
111
            throw stop_iteration();
112
        }
113
        catch (fail_cast) {}
114
115
116
    }

    Action _a;
117
    std::array<any*, N>& _args;
118
119
};

120
121
122
// recursion-free variadic version of for_each
template <class...>
struct for_each_variadic;
123

124
125
template <class F, class... Ts>
struct for_each_variadic<F,std::tuple<Ts...>>
126
{
127
    void operator()(F f)
128
    {
129
        auto call = [&](auto&& arg){f(std::forward<decltype(arg)>(arg)); return 0;};
130
        (void) std::initializer_list<int> {call(typename std::add_pointer<Ts>::type())...};
131
    }
132
133
};

134
135
136
// convert mpl sequence to std::tuple
template <class T, class R>
struct to_tuple_imp;
137

138
139
template <class... Ts, class X>
struct to_tuple_imp<std::tuple<Ts...>, X>
140
{
141
    typedef std::tuple<Ts..., X> type;
142
};
143

144
145
template <class Seq>
struct to_tuple
146
{
147
148
149
150
151
152
153
154
    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 {};
155

156
157
template <class Action, class... Ts>
struct inner_loop<Action, std::tuple<Ts...>>
158
{
159
    inner_loop(Action a): _a(a) {}
160

161
    template <class T>
162
    [[gnu::always_inline]]
163
164
165
    void operator()(T*) const
    { _a(typename std::add_pointer<Ts>::type()...,
         typename std::add_pointer<T>::type()); }  // innermost loop
166
167
    Action _a;
};
168

169
170
template <class Action, class... Ts, class TR1, class... TRS>
struct inner_loop<Action, std::tuple<Ts...>, TR1, TRS...>
171
172
{
    inner_loop(Action a): _a(a) {}
173

174
    template <class T>
175
    [[gnu::always_inline]]
176
    void operator()(T*) const
177
    {
178
179
180
        typedef inner_loop<Action, std::tuple<Ts..., T>, TRS...> inner_loop_t;
        typedef typename to_tuple<TR1>::type tr_tuple;
        for_each_variadic<inner_loop_t, tr_tuple>()(inner_loop_t(_a));
181
182
183
    }
    Action _a;
};
184

185
// final function
186

187
template <class TR1, class... TRS, class Action, class... Args>
188
bool nested_for_each(Action a, Args&&... args)
189
{
190
    std::array<any*, sizeof...(args)> as{{&args...}};
191
192
193
194
195
196
197
198
199
200
201
202
203
    auto b = all_any_cast<Action, sizeof...(args)>(a, as);
    try
    {
        typedef decltype(b) action_t;
        typedef typename to_tuple<TR1>::type tr_tuple;
        typedef inner_loop<action_t, std::tuple<>, TRS...> inner_loop_t;
        for_each_variadic<inner_loop_t, tr_tuple>()(inner_loop_t(b));
        return false;
    }
    catch (stop_iteration&)
    {
        return true;
    }
204
}
205

206
207
208
209
210
211
212
213
214
215
216
217
218
template <class TR1, class... TRS, class Action>
void nested_for_each(Action a)
{
    try
    {
        typedef typename to_tuple<TR1>::type tr_tuple;
        typedef inner_loop<Action, std::tuple<>, TRS...> inner_loop_t;
        for_each_variadic<inner_loop_t, tr_tuple>()(inner_loop_t(a));
    }
    catch (stop_iteration&) {}
}


219
220
221
222
} // mpl namespace
} // boost namespace

#endif //NESTED_FOR_LOOP_HH