1

I'm trying to call a function for each value in a std::tuple, of course there is no way to iterate a tuple and so I've resorted to using the template techniques discussed in iterate over tuple

However, I'm using Visual Studio 2013 and it does not support expression SFINAE, so that code won't work. I've tried to partially specialize the templates based on a constant numbers (e.g 5, 4, 3, 2, 1, 0), but haven't had any success. I'm certainly no template expert, and so I was hoping somebody could help me out. My expression SFINAE code is below.

#include <iostream>
#include <tuple>

using namespace std;

struct ArgPush {
    void push(bool x) {}
    void push(int x) {}
    void push(double x) {}
    void push(const char* x) {}
    void push(const std::string& x) {}

    template<std::size_t I = 0, typename... Tp>
    inline typename std::enable_if<I == sizeof...(Tp), void>::type
        push_tuple(const std::tuple<Tp...>& t)
    { }

    template<std::size_t I = 0, typename... Tp>
    inline typename std::enable_if<I < sizeof...(Tp), void>::type
        push_tuple(const std::tuple<Tp...>& t)
    {
        push(std::get<I>(t));
        push_tuple<I + 1, Tp...>(t);
    }
};

int main() {
    ArgPush().push_tuple(std::make_tuple(1,2,3,4));
    ArgPush().push_tuple(std::make_tuple("hello", "msvc makes me sad", 4, true));
    return 0;
}
Community
  • 1
  • 1
user1520427
  • 1,345
  • 1
  • 15
  • 27

2 Answers2

3

MSVC doesn't like the equality comparisons being made within the enable_if. So move these out from there to a helper template. Then your code compiles on VS2013.

template<std::size_t I, typename... Tp>
struct comparator
{
    static const bool value = (I < sizeof...(Tp));
};

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<!comparator<I, Tp...>::value>::type
    push_tuple(const std::tuple<Tp...>& t)
{ }

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<comparator<I, Tp...>::value>::type
    push_tuple(const std::tuple<Tp...>& t)
{
    push(std::get<I>(t));
    push_tuple<I + 1, Tp...>(t);
}
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 1
    I've never used VS2013, but from what I read here, it seems rubbish. How come anybody want's to use it if there is clang for free? – Walter Jun 07 '14 at 16:49
  • 2
    @Walter Put another way, you're asking why do people want to use a compiler that is best suited for targeting an OS with over 90% market share, does that answer your question? :-) clang doesn't work on Windows (and don't link me to their Windows builds, it's unusable for anything other than toy programs). MinGW is an option, and I use that myself for personal projects, but most people are forced to go with the compiler their employer chooses. – Praetorian Jun 07 '14 at 19:13
  • 1
    @Walter Also, even though I hate how lacking it is, I will also admit that if you stay within the confines it imposes on you, VS has a much better user experience than screwing around with gdb and such. – Praetorian Jun 07 '14 at 19:14
  • 1
    @Walter I've never got clang to work correctly on Windows, and the rest is most or less as Praetorian says; it works well most of the time. Also, thanks Praetorian - works great. I'll certainly be adding this to my MSVC toolbox! – user1520427 Jun 08 '14 at 00:07
2

You can partial specialization instead of SFINAE:

template <std::size_t N>
struct helper
{
    template <typename T>
    static void push(T&& v)
    {
        // do something
    }
    template <typename ...Types>
    static void push_tuple(const std::tuple<Types...>& t)
    {
        push(std::get<sizeof...(Types) - N>(t));
        helper<N - 1>::push_tuple(t);
    }
};

template <>
struct helper<0>
{
    template <typename ...Types>
    static void push_tuple(const std::tuple<Types...>&)
    {
        // nothing (end of iteration)
    }
};

template <typename ...Types>
void push_tuple(const std::tuple<Types...>& t)
{
    helper<sizeof...(Types)>::push_tuple(t);
}
nosid
  • 48,932
  • 13
  • 112
  • 139