4

Its seems that Microsoft is slow to support Fold Expressions and I'm struggling to find a way to emulate it. I have the following cutdown example. This fails to compile on Visual Studio 2017 because of no support for Fold Expressions. Any idea how to emulate it please?

template<typename... Args>
void get(weak_ptr<sqlite::database> awdb_, Args&&... args) {
    (*awdb_.lock() << ... << args);
}
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982

1 Answers1

7

Before fold expressions, there was the expander trick:

template <class... Args>
void get(weak_ptr<sqlite::database> awdb_, Args&&... args) {
    if (auto db = awdb_.lock()) {
        using expander = int[];
        (void)expander{0,
            (void(*db << std::forward<Args>(args)), 0)...
        };
    }
}

As T.C. points out, this isn't necessarily strictly equivalent if << does something other than returning *this back, so we could write:

template <class F, class Arg>
decltype(auto) fold(F&& , Arg&& arg) {
    return std::forward<Arg>(arg);
}

template <class F, class A0, class A1, class... Args>
decltype(auto) fold(F&& f, A0&& a0, A1&& a1, Args&&... args) {
    return fold(f, f(a0, a1), std::forward<Args>(args)...);
}

template <class... Args>
void get(weak_ptr<sqlite::database> awdb_, Args&&... args) {
    if (auto db = awdb_.lock()) {
        auto lshift = [](auto&& a, auto&& b) -> decltype(auto) { return a << std::forward<decltype(b)>(b); };
        fold(lshift, *db, std::forward<Args>(args)...);
    }
}    
Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
  • This assumes that the `<<` just returns the LHS rather than something more complicated, though, so it's not strictly equivalent. – T.C. Feb 13 '17 at 20:58
  • 1
    @T.C. Good point. Though it's awkward to work around. I feel like there should be an easier way than I what I just did. – Barry Feb 13 '17 at 21:15
  • @T.C. Amusingly, we have standard function operators for `+`, `-`, `*`, `/`, `%`, unary `-`, `==`, `!=`, `>`, `<`, `>=`, `<=`, `&&`, `||`, `!`, `&`, `|`, `^`, and `~`... but not `<<`. – Barry Feb 13 '17 at 21:21
  • @Barry .. My first thought was great, but it does not quite do what I need when there is more than 1 arg. What it needs to translate into is `db << arg1 << arg2 << arg3` which doesn't seem to be the case. –  Feb 13 '17 at 21:43
  • @mtissington The second approach would do that more directly. – Barry Feb 13 '17 at 21:51
  • @Barry One thing I omitted to mention was that I need to forward the result, something like `db << arg1 << arg2 << arg3 >> results` and I'm confused where I would put the `>> results` in the second example. –  Feb 13 '17 at 22:42
  • @mtissington Well, where do you think? There aren't that many places where you could put it. – Barry Feb 13 '17 at 22:47
  • @Barry Well, I've tried a couple of places, but none compile ... after `return a << std::forward(b) >> results` –  Feb 13 '17 at 22:53
  • @mtissington You put it in `get`, not `fold`. – T.C. Feb 13 '17 at 23:59