8

I had a function submitAsync which accepted a templated std::function as a parameter:

template <typename Ret, typename... Args>                                                                                                                                                     
Future<Ret> submitAsync(const function<Ret (Args...)> &func, Args&&... args);

However, implicit template argument deduction wasn't working when passing a lambda (similar to the issue here, so I had to make a more generic function that accepted the function as a template parameter, to then pass it to the original function:

template <typename Func, typename... Args>
auto submitAsync(Func &&func, Args&&... args) -> // Line 82, where the strange error occurs
    Future<
        typename enable_if<
            is_convertible<
                Func, function<decltype(func(args...)) (Args...) >
            >::value ,  decltype(func(args...))
        >::type
    > {
    typedef decltype(func(args...)) ReturnType;
    return submitAsync<ReturnType, Args...>(function<ReturnType (Args...)>(func), forward<Args>(args)...);
}

This compiles fine with Clang, but with GCC returns the following error:

src/Scheduler.hpp: In substitution of ‘template<class Func, class ... Args> Future<typename std::enable_if<std::is_convertible<Func, std::function<decltype (func(MCServer::Scheduler::startThread::args ...))(Args ...)> >::value, decltype (func(args ...))>::type> MCServer::Scheduler::submitAsync(Func&&, Args&& ...) [with Func = int; Args = {}]’:
src/Scheduler.hpp:91:109:   required from ‘Future<typename std::enable_if<std::is_convertible<Func, std::function<decltype (func(MCServer::Scheduler::startThread::args ...))(Args ...)> >::value, decltype (func(args ...))>::type> MCServer::Scheduler::submitAsync(Func&&, Args&& ...) [with Func = MCServer::MinecraftServer::init()::<lambda()>&; Args = {}; typename std::enable_if<std::is_convertible<Func, std::function<decltype (func(MCServer::Scheduler::startThread::args ...))(Args ...)> >::value, decltype (func(args ...))>::type = int]’
src/MinecraftServer.cpp:237:37:   required from here
src/Scheduler.hpp:82:10: error: expansion pattern ‘#‘nontype_argument_pack’ not supported by dump_expr#<expression error>’ contains no argument packs

This shows firstly that the lines

return submitAsync<ReturnType, Args...>(function<ReturnType (Args...)>(func), forward<Args>(args)...); 

which should be calling submitAsync(const function<Ret (Args...)> &, Args&&...), is actually trying to call submitAsync(Func &&func, Args&&... args), which of course doesn't work as the type of func being passed is int. The last part of the error I also do not understand, expansion pattern ‘#‘nontype_argument_pack’ not supported by dump_expr#<expression error>’ contains no argument packs which could perhaps be a compiler bug (line 82 is the main part of the function's signature, where I put a comment to mark it)?

Strangely enough, when I remove the explicit template parameters in the call to submitAsync, replacing this line:

return submitAsync<ReturnType, Args...>(function<ReturnType (Args...)>(func), forward<Args>(args)...);

with this:

return submitAsync(function<ReturnType (Args...)>(func), forward<Args>(args)...);

GCC compiles it correctly. So, why is GCC calling the wrong function when the template arguments are specified, even though it works fine when allowed to deduce the arguments? And can anyone tell me what the strange error on line 82 is?

Edit: Forgot to mention, I'm using GCC 4.7.2

EDIT 2: solution and explanation here

Community
  • 1
  • 1
Daniel Mulcahy
  • 133
  • 1
  • 7
  • 2
    That strange error looks like a compiler issue. Which GCC version are you using? – Dave S Oct 15 '12 at 17:44
  • Oh yeah, forgot to mention that, I'm using GCC 4.7.2 – Daniel Mulcahy Oct 15 '12 at 17:46
  • 1
    *'nontype_argument_pack' not supported* seems to indicate that the compiler does not have support for that particular feature yet... – David Rodríguez - dribeas Oct 15 '12 at 18:09
  • I've just tested it properly and as I've edited the question to state, both Clang and GCC weren't compiling it as I thought they were. Thanks for the help anyway. – Daniel Mulcahy Oct 15 '12 at 18:32
  • 2
    Your `is_convertible` is kinda unnecessary, since *anything* (except integers) is convertible to a `std::function`, the standard itself placed no further restriction on the constructors. Now, libc++ will actually SFINAE out the ctor if `func` was not callable with the provided signature (`decltype(func(args...))(Args...)`), but that is not mandated behaviour. – Xeo Oct 15 '12 at 18:49
  • On another note, you don't need to choose another name if you simply don't specify the template parameters, since then overload resolution will detect the one that takes a `std::function` and decide that it's a better match (it is more specialized). – Xeo Oct 15 '12 at 18:52
  • Hmm... it seems that the overload resolution picks the right function to call if it takes an std::function by value, but if it's passed by const reference it calls the wrong function, i.e. itself. – Daniel Mulcahy Oct 15 '12 at 20:02
  • C++11 support is still ''experimental'' in gcc 4.7 - so, some things won't work and are a bug in most cases. – Thomas Berger Oct 15 '12 at 20:39
  • 1
    The reason it picks itself is that `Func&&` will be deduced as `std::function&&` and that will be a better match for rvalues than a `const&`. – Xeo Oct 15 '12 at 22:27
  • I'm voting to close this as 'too localized'. Though that's not quite right. What I would suggest you do is simply answer the question yourself and accept that answer. That's what you did anyway. – Omnifarious Oct 17 '12 at 19:42

1 Answers1

1

Upon further testing, I've realised that both Clang and GCC without explicit template parameters wasn't actually working as I wished. In both cases, the function was just calling itself as the first function that fit the parameters. It was caused by the compiler deciding the submitAsync that takes the function as a template parameter was a better match than the one taking a const std::function&. It works fine now after changing that submitAsync to take the function by reference:

template <typename Ret, typename... Args>
Future<Ret> Scheduler::submitAsync(function<Ret (Args...)> func, Args&&... args)
BenMorel
  • 34,448
  • 50
  • 182
  • 322
Daniel Mulcahy
  • 133
  • 1
  • 7