3

I'm working on an application using Qt version 4.8 in C++.

I need to use a C function which requires a pointer to a free function:

void handler(int dummy)

I need to change the displayed values in my GUI depending on what happens inside the function.

So I've been reading on how to convert a member function to a free function. I've found out that I can do it only if my member function is static.

But I can't make it static - I won't be able to change any widgets if I make it static.

So, how can I deal with the problem? How can I pass a non-static member function as a pointer to a free function. If it's not possible, how to make a workaround?

I'll be grateful for any suggestions different that use another library.

user2738748
  • 1,106
  • 2
  • 19
  • 36

2 Answers2

2

Since the C function takes an argument, you can use the argument as an index into an array of functors:

class MyWidget : public QWidget {
  using vector_t = QVector<std::function<void()>>;
  static vector_t m_functions;
public:
  using function_type = void(*)(int);
  using size_type = vector_t::size_type;

  static function_type getHandler() {
    return +[](int index){ MyWidget::m_functions[index]; };
  }
  template <typename F> static size_type getIndexFor(F && fun) {
    m_functions.append(std::forward<F>(fun));
    return m_functions.size() - 1;
  }
  //...
  size_type getIndexForSetTitle();
};

The getHandler returns the address of the handler function. The getIndexFor returns the argument to be passed to the handler to run a given functor. The functor can encapsulate calls to the widget's methods. Here, we'll call setWindowTitle on the widget:

MyWidget::size_type MyWidget::getIndexForSetTitle() {
  static size_type index = getIndexFor([this]{ setWindowTitle("Foo"); });
  return index;
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Nice! But are you sure that you can control the value of the `dummy` parameter ? It might be set by the C library ...? Also, are you sure that his compiler will understand this construct (he's on Qt 4.8, for the compiler he didn't say) ? – Ilya Feb 25 '16 at 19:42
  • @Ilya The `dummy` parameter is an idiom. The API would be unusable otherwise. Also, Qt 4.8 doesn't prevent you from using a modern compiler. The above works just fine under Qt 4.8 and MSVC 2012, for example. – Kuba hasn't forgotten Monica Feb 25 '16 at 19:54
  • "Qt 4.8 doesn't prevent you from using a modern compiler" it also doesn't prevent you from being stuck to an old toolchain (some embedded stuff for instance). – Ilya Feb 25 '16 at 19:56
  • @Ilya Sure. That's why we need to hear from the OP :) – Kuba hasn't forgotten Monica Feb 25 '16 at 22:03
  • @KubaOber, thank you. This is a very nice solution. I'm using g++ and the C++11 standard. Unfortunately, I can check the version of my compiler now. But doesn't your piece of code indicate that I can use the function *getIndexFor* only once for each function that I want to get index of? – user2738748 Feb 26 '16 at 08:54
  • @user2738748 That's correct, and that's because `F` can be a closure and every time you call `getIndexFor`, the closure is a different object. You need to store the index yourself. If you want to limit what `F` can be, or to specialize `getIndexFor` for non-closures, you can do that of course! – Kuba hasn't forgotten Monica Feb 26 '16 at 14:23
1

Yes, C++ does not allow member functions to be passed as a function pointer to C libraries.

In this cases a common approach is using a helper function that acts like a bridge between the C library and our class instance. This helper function is a free function that takes a pointer to the current instance of the class and in its body invoke the corresponding member method of the class instance.

void handler_helper(int data){
    MyClass *obj = (MyClass *) data;
    obj->handler(); // This is the corresponding member method
}

And where you invoke a function from that C library, you should pass handler_helper as the function pointer and (int) this as its argument.

frogatto
  • 28,539
  • 11
  • 83
  • 129
  • Presuming 4 bytes for pointers is quite dangerous. I'd say `(int)this` is probably UB. – Ilya Feb 25 '16 at 19:37
  • Use `intptr_t`, an integer type large enough to hold a pointer. – Oktalist Feb 25 '16 at 20:26
  • I do know, casting a pointer to `int` isn't a proper way to pass it to a function. In question post the prototype of that function take an `int`, so in this case I have to pass the pointer as an `int`. However in most C libraries I've seen, always a variable of type free `void *` is accepted as the argument to the function being passed. – frogatto Feb 25 '16 at 20:32
  • @Oktalist, how can I use intptr_t if I have to use int as the parameter. – user2738748 Feb 26 '16 at 08:59