#include <QtCore/QCoreApplication>
#include <boost/bind.hpp>
#include <boost/function.hpp>
class button
{
public:
boost::function<void()> onClick;
boost::function<void(int ,double )> onClick2;
};
class player
{
public:
void play(int i,double o){}
void stop(){}
};
button playButton, stopButton;
player thePlayer;
void connect()
{
//error C2298: 'return' : illegal operation on pointer to member function expression
playButton.onClick2 = boost::bind(&player::play, &thePlayer);
stopButton.onClick = boost::bind(&player::stop, &thePlayer);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
connect();
return a.exec();
}
boost::bind(&player::play, &thePlayer)
You need to use placeholders for the two arguments:
boost::bind(&player::play, &thePlayer, _1, _2)
The placeholders allow you to say "I'm only binding certain arguments; other arguments will be provided later."
And if you want create portable code - specify namespace of placeholders directly:
boost::bind( &player::play, &thePlayer, ::_1, ::_2 ); // Placeholders of boost::bind are placed in global namespace.
Related
I'm working on a simple wrapper for a IPC lib we are using.
I want to convert the events from this lib to calls on Qt slots.
Right now i have something like this:
void Caller::registerCallback(int id, QObject* reciever, const char* member)
{
_callbackMap[id] = std::make_pair(reciever, QString(member));
}
bool Caller::call(const SomeData data)
{
auto reciever = _callbackMap.value(data.id);
return QMetaObject::invokeMethod(reciever.first, reciever.second.toLocal8Bit(), Qt::QueuedConnection,
QGenericReturnArgument(),
Q_ARG(SomeData, data));
}
void Receiver::someCallback(SomeData data)
{
qDebug() << data.str;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Caller caller;
Receiver reciever;
caller.registerCallback(1, &reciever, "someCallback");
caller.call(SomeData({ "Hi", 1 }));
return a.exec();
}
struct SomeData {
QString str;
int id;
}; Q_DECLARE_METATYPE(SomeData);
This works quite well. But I don't like to register the callbacks as strings. I would prefer a compile time checking with a syntax like this:
caller.registerCallback(1, &reciever, &Reciever::someCallback);
I am aware of this implementation.
The slots I want to register always have exactly one argument and no return value.
I already found this request what could solve my problem but unfortunately this was never implemented.
Also this question doesn't help me as I'm not able to patch the moc we are using.
So is this really not possible with all the meta magic Qt is using?
EDIT:
I found a solution that works also when the Caller dose not know about the Receiver (what is actually what I need):
//Caller.h
class Caller : public QObject
{
Q_OBJECT
public:
Caller(QObject *parent = nullptr);
~Caller();
//void registerCallback(int id, QObject* reciever, const char *member);
template < class R, typename Func >
void inline registerCallback(int id, R reciever, Func callback)
{
using std::placeholders::_1;
registerCallbackImpl(id, reciever, std::bind(callback, reciever, _1));
};
bool call(const SomeData);
private:
QMap<int, std::pair<QObject *, std::function<void(SomeData)>> > _callbackMap;
void registerCallbackImpl(int id, QObject* reciever, std::function<void(SomeData)> callback);
};
//Caller.cpp
void Caller::registerCallbackImpl(int id, QObject* reciever, std::function<void(SomeData)> callback)
{
_callbackMap[id] = std::make_pair(reciever, callback);
}
bool Caller::call(const SomeData data)
{
auto reciever = _callbackMap.value(data.id).first;
auto fn = _callbackMap.value(data.id).second;
QMetaObject::invokeMethod(reciever, [reciever, fn, data]() {
std::invoke(fn, data);
fn(data);
}, Qt::QueuedConnection);
return true;
}
//main.cpp
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Caller caller;
Receiver reciever;
using std::placeholders::_1;
caller.registerCallback(2, &reciever, &Receiver::someCallback);
caller.call(SomeData({ "Hi2", 2 }));
return a.exec();
}
This soulution relies upon std::invoke and lambda.
Variant 1: use std::invoke directly instead of QMetaObject::invoke
Variant 2: use std::invoke inside a lambda, which is passed to QMetaObject::invoke
Variant 3: use MACRO instead of std::invoke in variant 2.
If you use QMetaObject::invoke you've got an option to choose connection type - Direct or Queued. In variant 1 the call is invoked immediately like in direct connection.
receiver.h
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
#include <QDebug>
struct SomeData {
QString str;
int id;
};
//Q_DECLARE_METATYPE(SomeData);
class Receiver : public QObject
{
Q_OBJECT
public:
explicit Receiver(QObject *parent = nullptr) : QObject(parent) {}
void doSmth(SomeData data) {
qDebug() << data.str;
}
signals:
};
#endif // RECEIVER_H
caller.h
#ifndef CALLER_H
#define CALLER_H
#include <QObject>
#include <QMap>
#include <utility>
#include <map>
#include "receiver.h"
#define CALL_MEMBER_FN(object,ptrToMember) ((object)->*(ptrToMember))
typedef void (Receiver::*callback)(SomeData);
class Caller : public QObject
{
Q_OBJECT
public:
explicit Caller(QObject *parent = nullptr) : QObject(parent) { }
void registerCallback(int id, Receiver* receiver, callback c)
{
auto pair = std::make_pair(receiver, c);
_callbackMap.emplace(id, pair);
}
bool call(const SomeData data)
{
auto &receiver = _callbackMap.at(data.id);
return QMetaObject::invokeMethod(receiver.first, [data, receiver] () {
// method 1
std::invoke(receiver.second, receiver.first, data);
// method 2 (better not to use a MACRO)
CALL_MEMBER_FN(receiver.first, receiver.second)(data);
}, Qt::QueuedConnection);
}
bool call_invoke(const SomeData data)
{
auto &receiver = _callbackMap.at(data.id);
std::invoke(receiver.second, receiver.first, data);
return true;
}
signals:
private:
std::map<int,std::pair<Receiver*,callback>> _callbackMap;
};
#endif // CALLER_H
main.cpp
#include <QCoreApplication>
#include "receiver.h"
#include "caller.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Caller caller;
Receiver reciever;
caller.registerCallback(1, &reciever, &Receiver::doSmth);
caller.registerCallback(2, &reciever, &Receiver::doSmth);
caller.call(SomeData({ "Hi", 1 }));
caller.call_invoke(SomeData({ "Hi2", 2 }));
return a.exec();
}
An alternative approach might be to use a suitable std::function to capture the callback and then make use of QTimer::singleShot with a zero timeout to invoke the callback in the correct context.
struct SomeData {
QString str;
int id;
};
class Caller {
public:
using task = std::function<void(SomeData)>;
void registerCallback (int id, QObject *receiver, task t)
{
_callbackMap[id] = std::make_pair(receiver, t);
}
bool call (SomeData data)
{
auto receiver = _callbackMap.value(data.id);
QTimer::singleShot(0, receiver.first, [=](){ receiver.second(data); });
return true;
}
private:
QMap<int, std::pair<QObject *, task>> _callbackMap;
};
class Receiver: public QObject {
public:
void someCallback (SomeData data)
{
qDebug() << data.str;
}
};
Then use as...
Caller caller;
Receiver receiver;
caller.registerCallback(1, &receiver, [&](SomeData d){ receiver.someCallback(d); });
caller.call(SomeData({ "Hi", 1 }));
#include <iostream>
#include <stdio.h>
#include <stdint.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class FooBar{
public:
typedef void(FooBar::*OnDio)(void);
void OnDio0Irq( void ){
printf("dio0\n");
};
void OnDio1Irq( void ){
printf("dio1\n");
};
FooBar(){
OnDio dioArray[] = {&OnDio0Irq, &OnDio1Irq};
};
OnDio *dioArray[2];
private:
};
int main(int argc, char* argv[]){
typedef void(FooBar::*OnDio)(void);
void (FooBar::*foo)(void);
OnDio *myPtr;
FooBar *fb = new FooBar();
myPtr = *(&fb->dioArray[0]);
foo = (OnDio &)(myPtr[0]);
(foo)();//me need call fb->dioArray[0]()
(*myPtr)(); // ?
}
How can I call a function from an array?
In my code i have error:
[Error] must use '.' or '->' to call pointer-to-member function in 'foo
(...)', e.g. '(... ->* foo) (...)'
[Error] must use '.' or '->' to call pointer-to-member function in
'* myPtr (...)', e.g. '(... ->* * myPtr) (...)'
To call a pointer to member function (ptmf), you need an instance and the ptmf, together.
OnDio is already typedef'ed to be a pointer type, so you may not want OnDio pointers.
Also, you initialize a local temporary in the constructor, not the dioArray of "this" instance.
This answer is also helpful: C++: Array of member function pointers to different functions
Here is your code, corrected to call dio0 through a pointer to member function.
#include <iostream>
#include <stdio.h>
#include <stdint.h>
class FooBar {
public:
typedef void(FooBar::*OnDio)(void);
void OnDio0Irq(void) {
printf("dio0\n");
};
void OnDio1Irq(void) {
printf("dio1\n");
};
FooBar() {
// declaring a local OnDio array just masks the actual member and then it gets tossed
// need to initialize this instance, not some local temporary
dioArray[0] = &FooBar::OnDio0Irq;
dioArray[1] = &FooBar::OnDio1Irq;
};
OnDio dioArray[2];
private:
};
int main(int argc, char* argv[]) {
// need instance
FooBar fb;
// need pointer to member function
FooBar::OnDio func = fb.dioArray[0];
// call pointer to member function using instance
(fb.*func)();
}
#include <iostream>
#include <stdio.h>
#include <stdint.h>
class FooBar {
public:
typedef void(FooBar::*OnDio)(void);
OnDio dioArray[2];
void OnDio0Irq(void) {
printf("dio0\n");
};
void OnDio1Irq(void) {
printf("dio1\n");
};
FooBar() {
dioArray[0] = &FooBar::OnDio0Irq;
dioArray[1] = &FooBar::OnDio1Irq;
};
private:
};
int main(int argc, char* argv[]) {
FooBar* fb = new FooBar();
for (int i = 0; i < sizeof(fb->dioArray) / sizeof(fb->dioArray[0]); i++)
{
(fb->*fb->dioArray[i])();
}
}
I built an interface taking pointers to functions. Sometimes this calculation depends on state, which I want to encapsulate in a class and pass its method:
#include <iostream>
class Printer {
public:
static void print(int i) { // Want to get rid of the static
std::cout << i << "\n";
}
};
template<typename int_func>
void with_1(int_func func) {
func(1);
}
int main(int argc, char const *argv[]) {
Printer printer;
with_1(printer.print);
return 0;
}
I need non-static methods (and would even prefer overloading operator()). However removing the static results in error: a pointer to a bound function may only be used to call the function.
I could use a dummy like this:
Printer printer;
void dummy(int i) {
printer.print(i);
}
int main(int argc, char const *argv[]) {
with_1(dummy);
return 0;
}
But that does not look elegant to me. Can I write a template that accepts both, function pointers and non-static methods? Or is there even a better design pattern for my problem?
You can not simply pass non-static method like this, because they work on instance. A simply solution is to use lambda:
#include <iostream>
class Printer {
public:
static void print(int i) { // Want to get rid of the static
std::cout << i << "\n";
}
};
template<typename int_func>
void with_1(int_func func) {
func(1);
}
int main(int argc, char const *argv[]) {
Printer printer;
// Can use capture by reference because we are sure printer still
// exist during execution of with_1
with_1([&printer](int i){ printer.print(i); });
return 0;
}
example
Try this:
int main(int argc, char const *argv[]) {
Printer printer;
with_1( std::bind( &Printer::print, printer, std::placeholders::_1 ) );
return 0;
}
(You'll need to #include <functional>.)
I want to connect a callback function to a boost signal through a public function. I can pass a function pointer just fine, but if I try to use std::bind to pass a member function, it will not compile. Giving me error saying no viable conversion. What type should I use for the App::SetCallback function argument?
#include <functional>
#include <boost/signal.hpp>
using namespace std::placeholders; // for _1, _2, _3...
//plain simple call back function
void SimpleCallback(int value) {
//do nothing
}
//class contains a boost::signal, set callback through a public function
class App {
public:
App() : sig_()
{}
typedef boost::signal<void (int value)> SigType;
typedef std::function<void (int value)> CallbackFunType;
//connect signal to a callback function
void SetCallback(CallbackFunType callback) {
sig_.connect(callback);
}
//private: //comment this out for testing purpose.
SigType sig_; //this is the boost::signal
};
//class that has member callback function
class MyCallback {
public:
MyCallback():
val(0), app()
{}
void MemberCb(int value){
val = value;
}
void Connect() {
auto bind_fun = std::bind(&MyCallback::MemberCb, this, _1);
app.SetCallback(bind_fun); //this will not compile, no viable conversion
app.sig_.connect(bind_fun); //this is fine
app.SetCallback(SimpleCallback); //this is fine
}
private:
int val;
App app;
};
int main(int argc, char **argv) {
MyCallback my_cb;
my_cb.Connect();
return 1;
}
----------------UPDATE-----------------
Reading boost signal documentation more carefully, I learned that I can pass slot type. This solves my problem
#include <functional>
#include <boost/signal.hpp>
using namespace std::placeholders; // for _1, _2, _3...
//plain simple call back function
void SimpleCallback(int value) {
//do nothing
}
//class contains a boost::signal, set callback through a public function
class App {
public:
App() : sig_()
{}
typedef boost::signal<void (int value)> SigType;
typedef SigType::slot_type CallbackFunType;
//typedef std::function<void (int value)> CallbackFunType;
//connect signal to a callback function
void SetCallback(CallbackFunType callback) {
sig_.connect(callback);
}
//private: //comment this out for testing purpose.
SigType sig_; //this is the boost::signal
};
//class that has member callback function
class MyCallback {
public:
MyCallback():
val(0), app()
{}
void MemberCb(int value){
val = value;
}
void Connect() {
auto bind_fun = std::bind(&MyCallback::MemberCb, this, _1);
app.SetCallback(bind_fun); //using SigType::slot_type
app.sig_.connect(bind_fun);
app.SetCallback(SimpleCallback);
}
private:
int val;
App app;
};
int main(int argc, char **argv) {
MyCallback my_cb;
my_cb.Connect();
return 1;
}
Reading boost signal documentation more carefully, I learned that I can pass slot type. This solves my problem
#include <functional>
#include <boost/signal.hpp>
using namespace std::placeholders; // for _1, _2, _3...
//plain simple call back function
void SimpleCallback(int value) {
//do nothing
}
//class contains a boost::signal, set callback through a public function
class App {
public:
App() : sig_()
{}
typedef boost::signal<void (int value)> SigType;
typedef SigType::slot_type CallbackFunType;
//typedef std::function<void (int value)> CallbackFunType;
//connect signal to a callback function
void SetCallback(CallbackFunType callback) {
sig_.connect(callback);
}
//private: //comment this out for testing purpose.
SigType sig_; //this is the boost::signal
};
//class that has member callback function
class MyCallback {
public:
MyCallback():
val(0), app()
{}
void MemberCb(int value){
val = value;
}
void Connect() {
auto bind_fun = std::bind(&MyCallback::MemberCb, this, _1);
app.SetCallback(bind_fun); //using SigType::slot_type
app.sig_.connect(bind_fun);
app.SetCallback(SimpleCallback);
}
private:
int val;
App app;
};
int main(int argc, char **argv) {
MyCallback my_cb;
my_cb.Connect();
return 1;
}
I tried to connect my app to OpenViBE through VRPN server. My app works well until I try to add code to connect my app to VRPN server.
My code looks like this:
MainWindow.c code:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtUiTools/QUiLoader>
#include <QFile>
#include <QMessageBox>
#include <QFileDialog>
#include <iostream>
using namespace std;
#include "vrpn_Analog.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
currentImage = 0;
labelSize = ui->label_2->size();
createActions();
openFileDialog();
}
void MainWindow::checkChannels()
{
vrpn_Analog_Remote *vrpnAnalog = new vrpn_Analog_Remote("Mouse0#localhost");
vrpnAnalog->register_change_handler( 0, handle_analog );
}
void VRPN_CALLBACK MainWindow::handle_analog( void* userData, const vrpn_ANALOGCB a )
{
int nbChannels = a.num_channel;
cout << "Analog : ";
for( int i=0; i < a.num_channel; i++ )
{
cout << a.channel[i] << " ";
}
cout << endl;
}
MainWindow.h code:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFileInfoList>
#include "vrpn_Analog.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void resizeEvent(QResizeEvent *);
private slots:
void openFileDialog();
private:
void checkChannels();
void VRPN_CALLBACK handle_analog( void* userData, const vrpn_ANALOGCB a );
};
#endif // MAINWINDOW_H
With this code, when I try to run my app I get:
error: C3867: 'MainWindow::handle_analog': function call missing argument list; use '&MainWindow::handle_analog' to create a pointer to member
I try to edit code by error advice, but I get another error:
error: C2664: 'vrpn_Analog_Remote::register_change_handler' : cannot convert parameter 2 from 'void (__stdcall MainWindow::* )(void *,const vrpn_ANALOGCB)' to 'vrpn_ANALOGCHANGEHANDLER'
There is no context in which this conversion is possible
I search around, but I don't find any usable solution.
Methods checkChannels and handle_analog I "copy" from this code, where all works fine:
#include <QtCore/QCoreApplication>
#include <iostream>
#include "vrpn_Analog.h"
void VRPN_CALLBACK vrpn_analog_callback(void* user_data, vrpn_ANALOGCB analog)
{
for (int i = 0; i < analog.num_channel; i++)
{
if (analog.channel[i] > 0)
{
std::cout << "Analog Channel : " << i << " / Analog Value : " << analog.channel[i] << std::endl;
}
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
/* flag used to stop the program execution */
bool running = true;
/* VRPN Analog object */
vrpn_Analog_Remote* VRPNAnalog;
/* Binding of the VRPN Analog to a callback */
VRPNAnalog = new vrpn_Analog_Remote("openvibe_vrpn_analog#localhost");
VRPNAnalog->register_change_handler(NULL, vrpn_analog_callback);
/* The main loop of the program, each VRPN object must be called in order to process data */
while (running)
{
VRPNAnalog->mainloop();
}
return 0;
return a.exec();
}
Where I'm doing mistake? Thanks for all replies.
I had a similar error in Visual Studio: "function call missing argument list; use '&className::functionName' to create a pointer to member"..
I was just missing the parenthesis when calling the getter, so className.get_variable_a()
The error message tells you that the argument you provided does not match vrpn_ANALOGCHANGEHANDLER. You didn't show the definition of that. I checked online and it suggested
typedef void (*vrpn_ANALOGCHANGEHANDLER)(void *userdata, const vrpn_ANALOGCB info);
so I'm going with that.
Your code attempts to pass a pointer-to-member-function, which cannot be converted to a pointer-to-function. This is because a pointer-to-member-function can only be called on an object, so it wouldn't know what object to use.
If you look at the code you are "copying off", you will see that vrpn_analog_callback is a free function. However in your code it is a member function. You need to change your code so that the callback is a free function (or a static member function).
If your intent is that the callback should call the member function on the same MainWindow object that you are registering the handler on, then do this:
// In MainWindow's class definition, add this:
static void VRPN_CALLBACK cb_handle_analog( void* userData, const vrpn_ANALOGCB a )
{
static_cast<MainWindow *>(userData)->handle_analog(NULL, a);
}
// In checkChannels()
vrpnAnalog->register_change_handler( this, cb_handle_analog );
You cannot directly call a non-static class method using this callback. This is because the method is expecting to be called with the class this pointer.
If you don't need any data from your class, then just make the method static. If you do need data from the class, you can make a static "stub" that takes the class pointer in the userData parameter and then calls the original method. Something like:
Declaration:
static void VRPN_CALLBACK handle_analog_stub( void* userData, const vrpn_ANALOGCB a );
Definition
void VRPN_CALLBACK MainWindow::handle_analog_stub( void* userData, const vrpn_ANALOGCB a )
{
MainWindow *mainWindow = static_cast<MainWindow*>(userData);
mainWindow->handle_analog(NULL, a);
}
Then when you call the function use:
vrpnAnalog->register_change_handler( this, handle_analog_stub );
(Updated to static_cast to pointer, thanks rpavlik)