Normally, to test a if a pointer points to function, use std::is_function is enough.
However, it cannot work with lambda. Since lambda is an object with operator().
Now I have to use both is_function and is_object to check if one works like function, as below:
std::is_function<decltype(f)>::value || std::is_object<decltype(f)>::value
So I'm wondering if there is a better way to test if one is lambda or not?
EDIT:
Related code:
template<typename Func>
void deferJob(Func f, int ms=2000)
{
if(! std::is_function<decltype(f)>::value
&& ! std::is_object<decltype(f)>::value){
qDebug()<<"Not function!";
return;
}
QTimer* t = new QTimer;
t->setSingleShot(true);
QObject::connect(t, &QTimer::timeout,
[&f, t](){
qDebug()<<"deferJob";
f();
t->deleteLater();
});
t->start(ms);
}
EDIT2:
Similar question: C++ metafunction to determine whether a type is callable
So here are some thoughts that may or may not be helpful.
To create a type_trait that works for functors, lambdas and traditional functions, I think I would look into seeing if the template argument is convertible into a std::function<void()>. I think that would cover most bases in a clear way.
As we've mentioned in the comments, you can't test a template argument like the way you are doing. The f() later in the function will cause a compile error, and so you'll never have the opportunity to see the runtime error.
You can try to do something with std::enable_if. You'd need to create template specializations so that SFINAE can function to choose the correct implementation. This would use that type_trait that I mentioned in bullet 1.
If you did this, you could make the implementation of the other template to be a static_assert to create a "better" error message.
That being said, the compiler error messages aren't that bad in the first place. (At least in clang and gcc. I haven't looked as msvc).
This doesn't get you a great error message, but it does get you a different one:
#include <cassert>
#include <functional>
#include <type_traits>
template <typename Func>
typename std::enable_if<std::is_convertible<Func, std::function<void()>>::value>::type
deferJob(Func f, int ms=2000) {
}
void normal_function() {}
int main() {
deferJob([]() {}); // works
deferJob(&normal_function); // works
deferJob(3); // compile time error
}
In Clang, I get an error that looks like:
foo.cc:15:2: error: no matching function for call to 'deferJob'
deferJob(3); // compile time error
^~~~~~~~
foo.cc:6:25: note: candidate template ignored: disabled by 'enable_if' [with Func = int]
typename std::enable_if<std::is_convertible<Func, std::function<void()>>::value>::type
In GCC, I get an error that looks like:
foo.cc: In function ‘int main()’:
foo.cc:15:12: error: no matching function for call to ‘deferJob(int)’
deferJob(3); // compile time error
^
foo.cc:15:12: note: candidate is:
foo.cc:7:1: note: template<class Func> typename std::enable_if<std::is_convertible<Func, std::function<void()> >::value>::type deferJob(Func, int)
deferJob(Func f, int ms=2000) {
^
foo.cc:7:1: note: template argument deduction/substitution failed:
foo.cc: In substitution of ‘template<class Func> typename std::enable_if<std::is_convertible<Func, std::function<void()> >::value>::type deferJob(Func, int) [with Func = int]’:
foo.cc:15:12: required from here
foo.cc:7:1: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
We could go one step further (although doing it this way makes it hard to extend further) and add an additional function:
template <typename Func>
typename std::enable_if<not std::is_convertible<Func, std::function<void()>>::value>::type
deferJob(Func f, int ms=2000) {
static_assert(false, "You should pass a function");
}
This causes clang to report (at compile time):
foo.cc: In function ‘typename std::enable_if<(! std::is_convertible<Func, std::function<void()> >::value)>::type deferJob(Func, int)’:
foo.cc:14:2: error: static assertion failed: You should pass a function
static_assert(false, "You should pass a function");
But sadly, it doesn't give a stack trace, so I would find this far less helpful than any of the earlier messages.
And finally, we could also replace that static assert with your runtime message:
template <typename Func>
typename std::enable_if<not std::is_convertible<Func, std::function<void()>>::value>::type
deferJob(Func f, int ms=2000) {
qDebug() << "Not function!";
}
Related
What follows is reduced from a system that stores pointers to methods along with their parameter's types. The user just provides type::method and the template machinery does the rest. When the method is overloaded the user must provide the signature of the desired method.
This was working very well until we tried it with some boost::asio stuff. The following code demonstrates the problem:
#include <boost/asio.hpp>
using namespace boost::asio::ip;
using namespace boost::system;
template <typename TT, typename MFP, MFP> struct OpM;
template <typename TR, typename TT, typename ... Ts, TR (TT::*f)(Ts...)>
struct OpM<TT, TR (TT::*)(Ts...), f> {};
using sig = error_code (tcp::socket::*)(const tcp::endpoint&, error_code&);
struct RM {
template <class C, typename R, typename ... Ps>
RM(R (C::*)(Ps...)) {
typedef OpM<C, R (C::*)(Ps...), &tcp::socket::connect> OP;
}
} MRegisterer(static_cast<sig>(&tcp::socket::connect));
g++ 8.3 fails to compile with the message:
g++ -std=c++17 -c connect.cpp
connect.cpp: In instantiation of 'RM::RM(R (C::*)(Ps ...)) [with C = boost::asio::basic_stream_socket<boost::asio::ip::tcp>; R = boost::system::error_code; Ps = {const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&}]':
connect.cpp:19:40: required from here
connect.cpp:17:46: error: conversion from 'boost::system::error_code (boost::asio::basic_socket<boost::asio::ip::tcp>::*)(const endpoint_type&, boost::system::error_code&)' {aka 'boost::system::error_code (boost::asio::basic_socket<boost::asio::ip::tcp>::*)(const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&)'} to 'boost::system::error_code (boost::asio::basic_stream_socket<boost::asio::ip::tcp>::*)(const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&)' in a converted constant expression
typedef OpM<C, R (C::*)(Ps...), &tcp::socket::connect> OP;
^~
connect.cpp:17:46: error: could not convert template argument '& boost::asio::basic_socket<boost::asio::ip::tcp>::connect' from '<unresolved overloaded function type>' to 'boost::system::error_code (boost::asio::basic_stream_socket<boost::asio::ip::tcp>::*)(const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&)'
It is weird that the error message refers to a conversion error from ... boost::asio::basic_socket ... to ... boost::asio::basic_stream_socket ... (and something similar for the endpoint parameter).
I'm providing the full type of the method, in RM seems to work fine but when the method is passed to OpM apparently the compiler gets confused.
What is wrong?
For completeness' sake, this is the output of clang++ 8.0:
~/bin/clang++ -std=c++17 -c connect.cpp
connect.cpp:17:37: error: conversion from '<overloaded function type>' to
'boost::system::error_code
(boost::asio::basic_stream_socket<boost::asio::ip::tcp>::*)(const
boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> &,
boost::system::error_code &)' is not allowed in a converted constant
expression
typedef OpM<C, R (C::*)(Ps...), &tcp::socket::connect> OP;
^~~~~~~~~~~~~~~~~~~~~
connect.cpp:19:3: note: in instantiation of function template specialization
'RM::RM<boost::asio::basic_stream_socket<boost::asio::ip::tcp>,
boost::system::error_code, const
boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> &,
boost::system::error_code &>' requested here
} MRegisterer(static_cast<sig>(&tcp::socket::connect));
^
1 error generated.
Here's a short reproduction of the same issue without Boost.Asio or even overloaded functions:
struct B {
void foo();
};
struct D : B { };
template <typename T, T> struct X { };
using T = X<void (D::*)(), &D::foo>; // error
The problem is, the type of &D::foo, despite being spelled D::, is actually void (B::*)(). And that type is not implicitly convertible to void (D::*)().
The nice thing for you is that since you're using C++17, you don't actually have to go through this explicit typing rigamarole, you can just write:
template <auto F> struct X { };
using T = X<&D::foo>; // fine
Or rework the whole thing to use pointers to function instead, and turn your pointer-to-member function into a function taking a D* (which you can do with a lambda or write out a function template and use an explicit specialization of it).
This minimal compileable sample seems like a pretty standard setup of SFINAE:
#include <type_traits>
struct AType{};
// Helper type-trait templates for AType
template<typename T> struct isAType{ static const bool value = false; };
template<> struct isAType<AType>{ static const bool value = true; };
template<typename T>
void function( typename std::enable_if<isAType<T>::value, T>::type& t
) {}
int main()
{
AType a1;
// This line, using automatic type deduction, fails to compile:
// function( a1 );
// If you manually specify template parameter, it compiles:
function<AType>( a1 );
}
The error message I get, when function( a1 ); is uncommented, is the following:
main.cpp: In function ‘int main()’:
main.cpp:17:16: error: no matching function for call to ‘function(AType&)’
function( a1 );
^
main.cpp:10:6: note: candidate: template<class T> void function(typename
std::enable_if<isAType<T>::value, T>::type&)
void function( typename std::enable_if<isAType<T>::value, T>::type& t )
{}
^
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:17:16: note: couldn't deduce template parameter ‘T’
function( a1 );
I've seen some posts indicating that "T" is in a nondeduced context. "Nondeduced context" is a new concept to me but enough ink has been spilled elsewhere that I can figure it out. My question here, I guess, is whether my declaration of function can be tweaked in such a way that automatic type deduction will succeed. Is there a canonical way to implement SFINAE with type traits such that automatic type deduction succeeds?
Not all C++ compilers support it, but if yours does this is the cleanest way to do this:
template<bool b>
using sfinae = typename std::enable_if< b, bool >::type;
template<class T,
sfinae<isAType<T>::value> =true
>
void function( T& t )
{
}
in c++14 I wouldn't bother with the sfinae alias, but getting rid of the typename makes it worth it in c++11.
Note that the =true portion is required, but if it was =false it would mean the same thing.
What is going on here is we define a non-type template parameter whose type only exists if the test passes. We then give it a default value.
I find this technique reads the most like the incoming c++17 I mean c++20 I mean c++23 Concepts feature of C++.
I have a program like this:
template<typename ...Args>
using Function = void(*)(Args *...);
template<typename ...Args>
void DoThing(Function<Args...> func) { }
void IntFunction(int *i) { }
int main(int argc, char *argv[]) {
DoThing(IntFunction);
}
When I run the program I get this error
$ clang++ -std=c++14 template.cpp
template.cpp:12:3: error: no matching function for call to 'DoThing'
DoThing(IntFunction);
^~~~~~~
template.cpp:7:6: note: candidate template ignored: substitution failure [with Args = int]
void DoThing(Function<Args...> func) { }
^
1 error generated.
But if I compile it using g++ I don't get any errors.
It appears that clang is having trouble deducing the variadic template parameters when used in a type alias. If I replace the variadic parameters with standard ones then I don't get the error anymore.
Which compiler is giving me the proper result? And why would I not be allowed to do this?
Can be reduced to
template <typename... T>
using funptr = void(*)(T...);
template <typename... T>
void f(funptr<T...>) {}
template void f(void(*)());
This is valid code; if we substitute funptr<T...> by the corresponding pack expansion, Clang suddenly doesn't complain anymore.
Reported as #25250.
ideone example
I need to forward some predefined arguments plus some user-passed arguments to a member function.
#define FWD(xs) ::std::forward<decltype(xs)>(xs)
template<class T, class... Ts, class... TArgs>
void forwarder(void(T::*fptr)(Ts...), TArgs&&... xs)
{
T instance;
(instance.*fptr)(FWD(xs)..., 0);
// ^
// example predefined argument
}
forwarder(&example::f0, 10, 'a');
forwarder(&example::f1, 10, "hello", 5);
This works properly for non-template member functions.
The member function pointer passed to forwarder can, however, point to template functions as well. Unfortunately, the compiler is not able to deduce the type of T in that case:
struct example
{
void f0(int, int) { }
template<class T>
void f1(T&&, int) { }
};
// Compiles
forwarder(&example::f0, 10);
// Does not compile
forwarder(&example::f1, 10);
Errors:
prog.cpp:30:28: error: no matching function for call to 'forwarder(<unresolved overloaded function type>, int)'
forwarder(&example::f1, 10);
^
prog.cpp:20:6: note: candidate: template<class T, class ... Ts, class ... TArgs> void forwarder(void (T::*)(Ts ...), TArgs&& ...)
void forwarder(void(T::*fptr)(Ts...), TArgs&&... xs)
^
prog.cpp:20:6: note: template argument deduction/substitution failed:
prog.cpp:30:28: note: couldn't deduce template parameter 'T'
forwarder(&example::f1, 10);
Is there any way I can help the compiler deduce the correct types without changing the interface of forwarder?
If not, what's the best way of solving this issue without making the user syntax too convoluted?
EDIT: It would also be acceptable to pass the member function pointer as a template parameter, maybe through a wrapper. The target member function will always be known at compile-time. Pseudocode:
forwarder<WRAP<example::f0>>(10, 'a');
// Where WRAP can be a macro or a type alias.
ideone example
I compiled your code in gcc 4.9 by providing template arguments to the member function pointer;
like this
int main(){
// Compiles
forwarder(&example::f0, 10);
//Does not compile
forwarder(&example::f1, 10);
//Does compile, instantiate template with int or what ever you need
forwarder(&example::f1<int>,10)
}
I believe what is going on is that you need to instantiate the template member function.
does that change your interface too much?
I think any answer will revolve around instantiating that member template somehow.
I have a variadic template function f. This compiles fine (using g++ -std=c++11 and possibly using c++0x):
#include <tuple>
template<int ...>
struct seq { };
template <typename ...T, int ...S>
void f(std::tuple<T...> arg, seq<S...> s) {
// ... do stuff
}
int main() {
f<int>(std::tuple<int>(10), seq<0>());
return 0;
}
The compiler automatically fills in the int ...S that works.
However, I can't seem to manually provide the integer arguments:
int main() {
f<int, 0>(std::tuple<int>(10), seq<0>());
return 0;
}
Output:
/tmp/t.cpp: In function ‘int main()’: /tmp/t.cpp:12:42: error: no
matching function for call to ‘f(std::tuple, seq<0>)’
/tmp/t.cpp:12:42: note: candidate is: /tmp/t.cpp:7:6: note:
template void f(std::tuple<_TElements ...>,
seq) /tmp/t.cpp:7:6: note: template argument
deduction/substitution failed:
I believe I've read that technically there should only be one variadic template parameter pack provided to a template function (and in the first case, it is completely determined by context), so that explains it(?).
For debugging, is there a way in GCC to output the expansion used for ...S to stderr or stdout? It would be very useful for debugging things like this when they don't compile at first.
I know no way to specify two template argument packs manually. Since a template pack may contain arbitrarily many arguments, there is no way for the compiler to know when you meant the first one to stop and the second one to start. Automatic or partially automatic deduction seems to work, though, but I am not sure if this is just some generousity of g++...
I am not sure what you are actually trying to do, but I bet that you do not need both template packs at the same time. You might introduce one layer of indirection, e.g.,
template <typename ... Tuple_Elements>
void do_something_with_single_value(std::tuple<Tuple_Elements...> arg, int s) {
// ... do stuff
}
template <typename Tuple_Type, int ...S>
void f(Tuple_Type arg, seq<S...> s) {
// ... do stuff which needs all S at the same time
// ... call do_something_with_single_value in compile time loop to access tuple elements
}
Perhaps your signature is a hint that your function has too many responsibilities. Try to create smaller functions with clear responsibilities.
There is a way to output the arguments deduced for T and S, but only if the compiler can determine a match. For this, you would need to provoke an error at compile time:
template <typename ...T, int ...S>
void f(std::tuple<T...> arg, seq<S...> s) {
static_assert(std::tuple_size<std::tuple<T...>>::value < 0, "Provoked error message");
// ... do stuff
}
This would generate the following output in your working example:
stack.cpp: In function ‘void f(std::tuple<_Elements ...>, seq<S ...>) [with T = {int}, int ...S = {0}]’:
stack.cpp:15:34: instantiated from here
stack.cpp:10:2: error: static assertion failed: "Provoked error message"
For getting stderr for types and values in situations like this I, personally, find it best to force a compile error for the situation you're interested in.
So in this situation we want to know exactly what the type and integer packs for the first example where it compiles correctly. If we cause a compile fail before instantiation of the function then the compile error will refer to the function without the info we want:
template <class ... T, int ... S>
void f (std::tuple<T...> arg, seq<S...> s)
{
foo
}
In function 'void f(std::tuple<_Elements ...>, seq<S ...>)':
error: 'foo' was not declared in this scope
demo
So now let's fail compilation during instantiation. This means that we'll have to write something that could make sense for some types of T..., but not for our instantiation. I usually write a function like force_fail to make this easy to repeat:
template <class T, class ...>
int force_compile_fail ()
{
return T::force_compile_fail;
}
template <class ... T, int ... S>
void f (std::tuple<T...> arg, seq<S...> s)
{
force_compile_fail<T...>();
}
In instantiation of 'int force_compile_fail() [with T = int;
= {}]':
required from 'void f(std::tuple<_Elements ...>, seq<S ...>) [with T = {int}; int ...S = {0}]'
required from here
error: 'force_compile_fail' is not a member of 'int'
return T::force_compile_fail;
demo