I'm trying to use QSet for storing a list of function pointers. See this code for more details. The problem is that this code does not compile by gcc/mingw. MSVC compiles it normally. What am I doing wrong?
typedef intptr_t (*UikHook)(intptr_t);
...
typedef struct
{
QSet<UikHook>* qsetSubscribers;
//QMutex* qmutexHook;
} THookableEvent;
...
THookableEvent* p = qmapHooks_.value(name);
if (p->qsetSubscribers == 0)
p->qsetSubscribers = new QSet<UikHook>();
p->qsetSubscribers->insert(hookProc);
error: no matching function for call to ‘qHash(long int (* const&)(long int))’
Perhaps, I should to declare operator== and function qHash() for type UikHook as it said in the docs, but I dont know how to do it because when I'm declaring opertator==, I get the following error:
inline bool operator==(const UikHook &e1, const UikHook &e2)
error: ‘bool operator==(intptr_t (* const&)(intptr_t), intptr_t (* const&)(intptr_t))’
must have an argument of class or enumerated type
P.S. I'm using Qt 5.8, gcc 6.2, msvc2015
Update: Solved by replacing QSet to QVector.
You can't define an operator== for built-in types, such as pointers, integers or floating-point numbers. They already have it. You just need to provide a qHash.
Using function pointers is not really the best practice in C++. In early C++ versions, functors were used instead. Since C++11, there are real function objects. You can convert a function pointer to an object via std::function() (Reference).
If the compilers you listed are the ones you need to support, I strongly suggest using std::function instead of function pointers.
Try (warning, brain compile):
template <typename R, typename ...A>
inline uint qHash(R (*)(A...) const f) noexcept
{
return std::hash<R (*)(A...)>()(f);
}
Qt is in a fix here, since it can't use the STL and hashing function pointers is somewhat compiler specific.
Related
I have some utility code that I've been using for years to safely call the ctype family of functions, it looks like this:
template<int (&F)(int)>
int safe_ctype(unsigned char c) {
return F(c);
}
And is used like this:
int r = safe_ctype<std::isspace>(ch);
The idea being that it handles the need to cast the input int to an unsigned value for you in order to prevent undefined behavior. The specifics of this function is somewhat irrelivant though. Here's my question:
Now that in C++17 and later, noexcept is part of the type system, this is a compile error! Because all of the ctype functions are now noexcept.
EDIT: The above sentence is incorrect. the ctype family of functions are not noexcept. I was however getting a compiler error in gcc < 11.2. https://godbolt.org/z/cTq94q5xE
The code works as expected (despite being technically not allowed due to these functions not being addressable) with the latest versions of all 3 major compilers.
I can of course change my function to look like this:
template<int (&F)(int) noexcept>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
But now it doesn't work when compiled as C++11 or C++14. So I end up having to do something like this:
#if __cplusplus >= 201703L
template<int (&F)(int) noexcept>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
#else
template<int (&F)(int)>
int safe_ctype(unsigned char c) {
return F(c);
}
#endif
Which is getting increasingly complex for such a simple task. So is there a way to make the function pointer:
valid for C++11 - C++20
Accept both noexcept and non-noexcept when in C++17+
?
I tried doing something like this:
template<class F>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
In the hopes that it would accept "anything", but sadly, no go.
Thoughts?
Now that in C++17 and later, noexcept is part of the type system, this is a compile error! Because all of the ctype functions are now noexcept.
It is not a compile error. Pointers to noexcept functions are implicitly convertible to pointers to potentially throwing functions, and thus the template accepting a pointer to potentially throwing functions works with both potentially throwing and noexcept functions. Only caveat is that the noexceptedness information is lost and might not be used for optimisation purposes.
Hence, the original solution satisfies both points 1. and 2.
Another problem pointed out in the comments is that the standard library functions (std::isspace) that you intend to use are not designated "addressable". Hence the behaviour of the program is unspecified (possibly ill-formed) due to forming a pointer to them.
To wrap such callable, you could use a lambda instead of a function pointer. But that makes the template itself obsolete since you can change the argument type of the lambda directly:
auto safe_isspace = [](unsigned char c){ return std::isspace(c); };
int r = safe_isspace(ch);
Though we no longer need to pass this into a template, so the same can be achieved with a plain function:
int // or bool?
safe_isspace(unsigned char c) noexcept // ...
Since this involves a bit of identical boilerplate for multiple functions, this is a good candidate for meta-programming.
Because all of the ctype functions are now noexcept.
This is untrue. C++17 did not add noexcept to any C-library functions accessed through the C++ c* headers. You can see here that all of the C++ function declarations do not contain noexcept. And a standard library implementation is not allowed to make non-noexcept functions noexcept.
Secondly, even if it were noexcept, a noexcept function pointer can be converted into a throwing function pointer (but not the other way around). So your code compiles.
But most importantly, C++20 makes it clear that you are not allowed to get function pointers for any C++ standard library function unless it is specifically stated to be "addressable". And there are very few addressable functions in the C++ standard library.
So in C++20, your code will yield UB. You're just going to have to write wrappers for the cctype functions if you want your code to work across all language versions.
#include <iostream>
#include <deque>
using namespace std;
main()
{
typedef void (deque<int>::*func_ptr)(int);
func_ptr fptr = &deque<int>::push_back;
}
Im trying to get pointer to this function but I get a compilation error
error: cannot convert ‘void (std::deque<int>::*)(const value_type&) {aka void (std::deque<int>::*)(const int&)}’ to ‘func_ptr {aka void (std::deque<int>::*)(int)}’ in initialization
func_ptr fptr = &deque<int>::push_back;
I want to do this so that I can get pointer to different member functions on the basis of different conditions.
I referred this link.
As said in the accepted answer, the problem is that the type signature is different -- for a std::deque<T>, push_back only has an overload that accepts T const&, and not T directly. typedef void (deque<int>::*func_ptr)(const int &) is a perfectly concise and cromulent way to write this.
I wanted to address a C++11 way to do this -- type aliases. One may first wonder "why not use auto?" This is not possible because the function being accessed is an overloaded function, and auto does not know which of the overloads to access. Because the original question embeds the knowledge of the function type being void(int), the statement &deque<int>::push_back selects the correct overload. The problem with the simpler statement is that it bakes in knowledge of the container and the type being contained. If you want to change to a std::vector, or from int to short, you'll have to make all new types. With type aliases, we can templatize everything to avoid embedding type knowledge:
using func_ptr = void(Cont::*)(typename Cont::const_reference);
We can do something simple, as before:
func_ptr<deque<int>> fptr = &deque<int>::push_back;
...or we can refer to a container somewhere:
vector<short> container;
...looking up its type at compile time, and storing a pointer to its push_back function, all without caring what the container is:
using container_type = decltype(container);
func_ptr<container_type> fptr2 = &container_type::push_back;
push_back() takes a const T & parameter, as the error message states:
cannot convert ‘void (std::deque::*)(const value_type&) ...
Change your type alias to:
typedef void (deque<int>::*func_ptr)(const int &);
There is a bigger problem with what you are trying to do. Implementations are allowed to add default parameters to any member functions, so the actual signature for push_back() might be different. It can even add additional functions with the same name (but a different signature).
I advise against taking pointers to container members function. You are usually better of without it.
Consider the following code:
template <typename T>
class B
{
};
template <typename T>
B<T> f(T& t)
{
return B<T>();
}
class A
{
class C {};
C c;
public:
A() {}
decltype(f(c)) get_c() const { return f(c); }
};
int main()
{
A a;
a.get_c();
}
When I try to compile this, I get the error:
test.cpp: In member function 'B<A::C> A::get_c() const':
test.cpp:31:46: error: conversion from 'B<const A::C>' to non-scalar type 'B<A::C>' requested
It seems that in the decltype, the compiler doesn't know that this is a const member function and therefore c is of type const C, and as a result incorrectly deduces the type of f(c) to be B<C> rather than B<const C> which is what it really is.
Am I doing something incorrectly, or is this a compiler bug? I use gcc 4.6, but 4.4 and 4.5 exhibit the same behaviour.
The compiler operates correctly according to the current C++0x WP. See this issue report, which is currently being worked on.
Possibly the final C++0x Standard won't change the meaning of your decltype application in the return type before the function name. You would need to move it to after the parameter list using -> decltype(f(c)), which hopefully will do The Right thing in final C++0x.
No, decltype is not supposed to take into account whether the function is const or not, because it can't. The same could have been written differently:
typedef decltype(f(c)) return_type;
return_type get_c() const { return f(c); }
Correction: decltype(f(c)) shouldn't even compile, because c is not static.
f needs to take an rvalue reference, not an lvalue reference.
I don't think you're allowed to use decltype on anything you wouldn't normally be able to call. I haven't been able to find anything in the standard that would allow you to access c, even within a decltype expression, outside of anywhere you could use c. Since you don't have a this pointer at the point you're trying to do your thing, I don't think you can do what you're trying to do. Doing so doesn't work in MSVC 2010 at least, and it has nothing to do with const.
I considered using declval to get one but you can't access A&&.c because A is an incomplete type at that point. I can't see anyway to do what you're trying to do other than something like so:
decltype(f(declval<C const>())) get_c() const { ... }
Is it posible to use the type of a prefiously declared function as a function pointer without using a typedef?
function declaration:
int myfunc(float);
use the function declaration by some syntax as function pointer
myfunc* ptrWithSameTypeAsMyFunc = 0;
Not as per the 2003 standard. Yes, with the upcoming C++0x standard and MSVC 2010 and g++ 4.5:
decltype(myfunc)* ptrWithSameTypeAsMyFunc = 0;
Yes, it is possible to declare a function pointer without a typedef, but no it is not possible to use the name of a function to do that.
The typedef is usually used because the syntax for declaring a function pointer is a bit baroque. However, the typedef is not required. You can write:
int (*ptr)(float);
to declare ptr as a function pointer to a function taking float and returning int -- no typedef is involved. But again, there is no syntax that will allow you to use the name myfunc to do this.
Is it posible to use the type of a prefiously declared function as a function pointer without using a typedef?
I'm going to cheat a bit
template<typename T>
void f(T *f) {
T* ptrWithSameTypeAsMyFunc = 0;
}
f(&myfunc);
Of course, this is not completely without pitfalls: It uses the function, so it must be defined, whereas such things as decltype do not use the function and do not require the function to be defined.
No, not at the present time. C++0x will change the meaning of auto, and add a new keyword decltype that lets you do things like this. If you're using gcc/g++, you might also look into using its typeof operator, which is quite similar (has a subtle difference when dealing with references).
No, not without C++0x decltype:
int myfunc(float)
{
return 0;
}
int main ()
{
decltype (myfunc) * ptr = myfunc;
}
gcc has typeof as an extension for C (don't know about C++) ( http://gcc.gnu.org/onlinedocs/gcc/Typeof.html ).
int myfunc(float);
int main(void) {
typeof(myfunc) *ptrWithSameTypeAsMyFunc;
ptrWithSameTypeAsMyFunc = NULL;
return 0;
}
int (*ptrWithSameTypeAsMyFunc)(float) = 0;
See here for more info on the basics.
I have a simple class like this:
class A
{
public:
void f(const int& n)
{
std::cout<<"A::f()" << n <<"\n";
}
};
and I am trying to use it like this:
std::vector<A> vec;
A a;
vec.push_back(a);
std::for_each(vec.begin(), vec.end(), std::bind2nd(std::mem_fun_ref(&A::f), 9));
But when I compile the code I get the following error somewhere inside functional header file:
error C2529: '_Right' : reference to
reference is illegal
If I remove the reference in the parameter f() it compiles fine. How do I resolve this? I don't want to remove the reference as in my real code the copying of the object is quite costly. Also, I am not using boost.
You can't do that easily, sorry. Just consider it one of those cases not covered by std::bind1st and std::bind2nd (kinda like 3-argument functions etc). Boost would help - boost::bind supports references transparently, and there's also boost::ref.
If your implementation supports TR1 - latest g++ versions and VC++2008 SP1 both do - then you can use std::tr1::bind, which is for the most part same as boost::bind, but standardized.
I dont believe you can bind parameters to a method that takes references. (not in the STL, I think the boost versions may let you do it but I am not sure)
You will need to roll your own.
struct CallF
{
CallF(int const& data): m_data(data) {}
void operator()(A& val) const
{
val.f(m_data);
}
int const& m_data;
};
Use like this:
std::for_each(vec.begin(), vec.end(), CallF(9));
I've been bitten by the same problem. If you look into the C++ standard, you'll see that it's actually a "library defect". A conforming C++ implementation simply can't deal with reference parameters. mem_fun_ref returns an object of a class that has nested typedefs (
argument_type, first_argument_type, second_argument_type
) where references are not stripped away. bind1st and bind2nd are specified to have an operator() wich takes references as parameters. In case argument_type is a reference already this will fail to compile.
One solution might be to replace memfunref with your own template magic and strip away references for the nested argument_type typedefs.
Actually, the compilers error message tells the whole story:
error C2529: '_Right' : reference to reference is illegal
std:: binders take their arguments as references - you cant pass a reference to a reference.
No way.