I was hoping to create a variadic template function which sits in front of the QtConcurrent::run functions that does some stuff and then passes the parameters on.
QtConcurrent::run is massively overloaded - check out qtconcurrentrun.h
Is it possible to create a variadic template function that I can call which will pass through to QtConcurrent::run ? This is what I have thus far:
template <typename returnT, typename... Args>
static auto Run(Args&&... args) -> QFuture<returnT>
{
// Do Stuff
// Now call through to start the task
QFuture<returnT> future = QtConcurrent::run(std::forward<Args>(args)...);
QFutureWatcher<void>* futureWatcher = new QFutureWatcher<void>(); //A QFutureWatcher<void> is special, see QFutureWatcher QT docs.
futureWatcher->setFuture(future);
QObject::connect(futureWatcher, &QFutureWatcher<void>::finished, [=]() {
// Do stuff
futureWatcher->deleteLater();
});
return future;
}
I'm struggling to work out how to deduce the return type, so I've got the returnT as a separate template param. This doesn't compile (VS2012 Nov CTP) when called with:
Tasking::TaskManager::Run<void>([&]() { while (stopTask == false); });
With the top couple error messages being:
1> error C2065: '<lambda_86e0f4508387a4d4f1dd8316ce3048ac>' : undeclared identifier
1> Implementation\TaskingTests\TaskManagerTests.cpp(31) : see reference to function template instantiation 'QFuture<void> Tasking::TaskManager::Run<void,TaskManagerTests::WaitsForTaskTest::<lambda_86e0f4508387a4d4f1dd8316ce3048ac>>(TaskManagerTests::WaitsForTaskTest::<lambda_86e0f4508387a4d4f1dd8316ce3048ac> &&)' being compiled
1>C:\tkbt\Launch2.0.0\ICDE\IceLibrary\Implementation\Tasking/TaskManager.hpp(108): error C2974: 'std::forward' : invalid template argument for '_Ty', type expected
1> C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\type_traits(1780) : see declaration of 'std::forward'
1> C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\type_traits(1774) : see declaration of 'std::forward'
1>C:\tkbt\Launch2.0.0\ICDE\IceLibrary\Implementation\Tasking/TaskManager.hpp(108): error C2780: 'QFuture<T> QtConcurrent::run(const Class *,T (__cdecl Class::* )(Param1,Param2,Param3,Param4,Param5) const,const Arg1 &,const Arg2 &,const Arg3 &,const Arg4 &,const Arg5 &)' : expects 7 arguments - 0 provided
1> c:\qt\qt5.0.2\5.0.2\msvc2012_64\include\qtconcurrent\qtconcurrentrun.h(333) : see declaration of 'QtConcurrent::run'
Any help much appreciated.
I guess that TaskManager.hpp(108) is the line where you call QtConcurrent::run.
What you are experiencing seems to be this MSVC bug. In short, variadic templates cannot forward lambdas in MSVC. You probably will have to use an oldscool functor in this case or provide nonvariadic overloads to support lambdas, maybe for the first few arguments. If I had to guess I'd think QtConcurrent::run's first argument has to be a function and the other arguments are its parameters, meaning you never get to call Run without arguments. You could rewrite your function template to have one fixed "normal" parameter for the function and the parameter pack for the function arguments.
For the return type deduction you might want to use decltype. Together that would look like this:
template <class F, class... Args>
static auto Run(F&& f, Args&&... args)
-> decltype(QtConcurrent::run(std::forward<F>(f), std::forward<Args>(args)...))
{
auto future = QtConcurrent::run(std::forward<F>(f), std::forward<Args>(args)...);
//I suppose this can not be a smart pointer?
auto futureWatcher = new QFutureWatcher<void>();
futureWatcher->setFuture(future);
QObject::connect(futureWatcher, &QFutureWatcher<void>::finished, [=]() {
// Do stuff
futureWatcher->deleteLater();
});
return future;
}
This way, the lambda would be passed to the normal template parameter F, wich should be ok for forwarding, i.e. the bug should not happen this way.
Update: If QtConcurrent::run does not give you the correct return type right away, you could go by using decltype on the function and its arguments:
static auto Run(F&& f, Args&&... args)
-> QtFuture<decltype(f(std::forward<Args>(args)...))>
Maybe you'll need to add some std::remove_reference and std::remove_const to the decltype to get the right future type.
Related
This question already has answers here:
Passing arguments to std::async by reference fails
(3 answers)
Closed 2 years ago.
I have this code.
int TA11::AsyncRunP(Unit *unit,Function func)
{
return 0;
}
int TA11::AsyncRunR(Unit& unit, Function func)
{
return 0;
}
void TA11::RunFunc(Unit& unit, Function func)
{
assert(!unit.fut_.valid());
unit.fut_ = std::async(std::launch::async, &TA11::AsyncRunR, this, unit, func);
unit.fut_ = std::async(std::launch::async, &TA11::AsyncRunP, this, &unit, func);
}
VS2019 c++17 mode. (Function is a class enum)
the first std::async wont compile, second one is fine.
1>C:\work\pdp\mysim\mysim\Ta11Cassette.cpp(115,19): error C2672:
'std::async': no matching overloaded function found
1>C:\work\pdp\mysim\mysim\Ta11Cassette.cpp(115,79): error C2893:
Failed to specialize function template
'std::future<_Invoke_traits<void,decay<_Ty>::type,decay<_ArgTypes>::type...>::type> std::async(std::launch,_Fty &&,_ArgTypes &&...)' 1>C:\Program Files
(x86)\Microsoft Visual
Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include\future(1481):
message : see declaration of 'std::async'
1>C:\work\pdp\mysim\mysim\Ta11Cassette.cpp(115,79): message : With the
following template arguments:
1>C:\work\pdp\mysim\mysim\Ta11Cassette.cpp(115,79): message :
'_Fty=int (__thiscall TA11::* )(TA11::Unit &,TA11::Function)'
1>C:\work\pdp\mysim\mysim\Ta11Cassette.cpp(115,79): message :
'_ArgTypes={TA11 *, TA11::Unit &, TA11::Function &}'
std::async passes the arguments to the callable by value (doesn't make perfect forwarding), hence you got the error because your callable only accepts reference.
You can use std::ref() to pass your var by reference.
I try to dispatch between two functions using if constexpr. The dispatcher function should accept for example std::size_t and an arbitrary class type.
It works if I just call it with a scalar type, but if I try to pass a class type it triggers a compile error which is not really helpful to me (please see below).
Please have a look at my current approach:
template <auto Other>
constexpr auto mul() const {
if constexpr (std::is_scalar_v<decltype(Other)>)
return mul_with_scalar<Other>();
else
return mul_with_matrix<Other>();
}
template <size_t Scalar>
constexpr auto mul_with_scalar() const {
return apply([](size_t v, auto) { return v * Scalar; },
std::make_index_sequence<Size>{});
}
template <class Other>
constexpr auto mul_with_matrix() const {
return size_t{0}; // implement me
}
note: candidate: template<auto Other> constexpr auto matrix<Rows, Cols, Vals>::mul() const [with auto Other = Other; long unsigned int Rows = 3; long unsigned int Cols = 3; long unsigned int ...Vals = {}]
constexpr auto mul() const {
^~~
./constexpresian/matrix.hpp:81:18: note: template argument deduction/substitution failed:
I wan't that the function mul can handle non-type and type parameters.
That is not possible in C++. A template parameter can be a type or a value (or a template), but it cannot be both. template<auto Name> makes Name a value template parameter, the type of whose value will be deduced at the time the value is passed.
But since it is a compile-time value, you can wrap its value in a type. For integer types, std::integer_constant will work. For future C++ revisions that allow other kinds of value parameters, you'll have to use a more generic wrapper.
There is one question I have, you can refer to my comment below your post that pertains to the term Size.
I tried your code above as is even with Size the way it is to see the compiler errors and this is coming from Visual Studio 2017 v4.6.0105 c++17 on Win 7 x64 Home Premium under x86 Debug on an Intel Quad Core Extreme.
1>------ Build started: Project: StackOverflow, Configuration: Debug Win32 ------
1>Learner.cpp
1>c:\users\skilz80\documents\visual studio 2017\projects\stackoverflow\stackoverflow\learner.h(75): error C3533: a parameter cannot have a type that contains 'auto'
1>c:\users\skilz80\documents\visual studio 2017\projects\stackoverflow\stackoverflow\learner.h(75): error C2270: 'mul': modifiers not allowed on nonmember functions
1>c:\users\skilz80\documents\visual studio 2017\projects\stackoverflow\stackoverflow\learner.h(83): error C2270: 'mul_with_scalar': modifiers not allowed on nonmember functions
1>c:\users\skilz80\documents\visual studio 2017\projects\stackoverflow\stackoverflow\learner.h(89): error C2270: 'mul_with_matrix': modifiers not allowed on nonmember functions
1>Done building project "StackOverflow.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Maybe this interpretation of these compiler errors might help you.
Here are some links that might help concerning auto and template parameters:
StackOverflow : Advantages of auto in template parameters in C++17
open-std.org : Declaring non-type template arguments with auto
www.bfilipek.com : C++17 in details: Templates
I want to pass the standardized C++ binary functions to a template function, but somehow I didn't get it to work.
The following is my attempt to do it:
template<template <typename> typename Pred,typename T, typename Iterator>
void iota_stepa(Iterator begin, Iterator end, T startofSequence_, T threadStep)
{
int currMaxThreads = startofSequence_;
bool first = true;
generate(begin, end, Pred<T>(currMaxThreads, threadStep) );
}
and testing it with:
vector<int> tempVect_(10, 0);
iota_stepa<std::plus>(begin(tempVect_),end(tempVect_),1,thread::hardware_concurrency());
gives me unfortunately the errors:
Severity Code Description Project File Line Suppression State
Error C2440 '<function-style-cast>': cannot convert from 'initializer list' to 'std::plus<int>'
Error C2672 'generate': no matching overloaded function found FractalCarpet
Error C2780 'void std::generate(_FwdIt,_FwdIt,_Fn0)': expects 3 arguments - 2 provided FractalCarpet
The console output looks like the following:
1> c:\users\mtunca\documents\esd\sps\fractalcarpet\main.cpp(55): note: see reference to function template instantiation 'void iota_stepa<std::plus,int,std::_Vector_iterator<std::_Vector_val<std::_Simple_types<float>>>>(Iterator,Iterator,T,T)' being compiled
1> with
1> [
1> Iterator=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<float>>>,
1> T=int
1> ]
1>c:\users\mtunca\documents\esd\sps\fractalcarpet\main.cpp(34): error C2672: 'generate': no matching overloaded function found
1>c:\users\mtunca\documents\esd\sps\fractalcarpet\main.cpp(34): error C2780: 'void std::generate(_FwdIt,_FwdIt,_Fn0)': expects 3 arguments - 2 provided
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\algorithm(1532): note: see declaration of 'std::generate'
Could someone help me, how to solve this problem?
std::generate needs a generator, something that can be called like gen().
You could create one with a lambda, perhaps like this:
template<template <typename> class Pred, typename T, typename Iterator>
void iota_stepa(Iterator begin, Iterator end, T startofSequence_, T threadStep)
{
bool first = true;
T current;
auto gen = [&]() -> T
{
if(first) {
current = startofSequence_;
first = false;
} else {
current = Pred<T>() ( current, threadStep );
}
return current;
};
generate(begin, end, gen );
}
Pred<T>(currMaxThreads, threadStep) );
Pred<T> is a type. You need to construct an actual callable object:
Pred<T>()(currMaxThreads, threadStep) );
This however cannot be the last argument to std::generate. The latter requires a callable object with no arguments, presumably holding a state (otherwise a call to std::fill woud suffice). It is unclear how an arbitrary binary function could be adapted to fill this role.
Assume we have the following function definitions
int function_wrapper_dummy( lua_State* ) { }
template < typename F, F* f >
int function_wrapper( lua_State* L ) { }
And the following function
template < typename F >
void register_function( F f )
{
int (*lf) (lua_State *L) = function_wrapper_dummy; // line 1
int (*lf) (lua_State *L) = function_wrapper< F, f >; // line 2
}
Compiling the above code works for line 1, but doesn't compile for line 2.
Quite humorously, VS2012 tells us:
error C2440: 'initializing' : cannot convert from 'int (__cdecl *)(lua_State *)'
to 'int (__cdecl *)(lua_State *)'
While Clang 3.3 emits:
error: address of overloaded function
'function_wrapper' does not match required type 'int (lua_State *)'
...int (*lf) (lua_State *) = function_wrapper< F, f >;
I use templated function adresses in less complex situations, so I see that it can be done. I checked that lua_State is the same lua_State, I've checked the calling conventions, and I've checked for name shadowing. I've also spent 2 hours trying to google the solution, to no avail (no, this is not a member function problem, only freestanding functions involved).
What am I doing wrong?
Any template argument must be determined at compile time. In register_function, the value of function pointer f is not known until the function is invoked at runtime.
This code compiled fine, hadly i got one error "function_wrapper_dummy" must return a value. I don't think there is an error,with this code.
I've got this sample code:
struct A
{
int foo() { return 27; }
};
template<typename T>
struct Gobstopper
{
};
template<>
struct Gobstopper<int(void)>
{
Gobstopper(int, int) { } // To differentiate from general Gobstopper template
};
template<typename ClassType, typename Signature>
void DeduceMethodSignature(Signature ClassType::* method, ClassType& instance)
{
// If Signature is int(), Gobstopper<> should resolve to the specialized one.
// But it only does on x64!
Gobstopper<Signature>(1, 2);
}
int main(int argc, char** argv)
{
A a;
DeduceMethodSignature(&A::foo, a);
return 0;
}
This compiles fine with g++. It also compiles fine with VC10, but only when building for the 64-bit platform. When I build for the 32-bit platform, I get this compile error:
error C2661: 'Gobstopper<T>::Gobstopper' : no overloaded function takes 2 arguments
1> with
1> [
1> T=int (void)
1> ]
1> c:\...\test.cpp(26) : see reference to function template instantiation 'void DeduceMethodSignature<A,int(void)>(Signature (__thiscall A::* ),ClassType &)' being compiled
1> with
1> [
1> Signature=int (void),
1> ClassType=A
1> ]
The error indicates that the non-specialized version of Gobstopper is being used, which must mean the Signature is something other that int (void). But the error also clearly says that Signature is int (void). So where does the error come from? And how can I fix it?
The only thing I can think of that might change from 32-bit to 64-bit and not show up in the signature displayed in the error message is the calling convention; apparently, there is a unified calling convention for VC x64, whereas for x86 each calling convention is distinct. But even if that's the problem, I have no idea how to fix it.
Edit: I should mention that I tried this with regular (non-member) function pointers, and that worked fine.
You are quite correct.
The type of Signature with a Win32 target is int __thiscall(void) while on x64 it is int __cdecl(void). Note that on either target the type of non-member functions commonly called int(void) is indeed int __cdecl(void) so, by coincidence one of the constructed types actually (not really correctly!) matches.
In general it is not advisable to mix the different types of function pointers by template magic, so the Gobstopper specialization should look at something like int (ClassType::*)() instead.