1

I want to use this python module: https://python-omxplayer-wrapper.readthedocs.io/en/latest/ on my c++ app.

It is a omxplayer python wrapper and has some callback events that I want to link to my c++ class function member.

I did it with success using a static function like this:

void foo(py::object player) { 
    std::cout << "pause event callback" << std::endl; 
}

py::object mod = py::import("omxplayer.player");

OMXPlayer::pyOMXPlayer = mod.attr("OMXPlayer")(_file, args, NULL, NULL, _dbus_name, _pause);

OMXPlayer::pyOMXPlayer.attr("pauseEvent") = py::make_function( &foo );

Where OMXPlayer is my c++ class.

I tried to use boost::bind and boost::function without success.

How can use OMXPlayer::onPause() function instead of static foo function?

Edit with example:

OMXPlayer.cpp

    #include "OMXPlayer.h"
    OMXPlayer::OMXPlayer(std::string _file, 
        std::vector<std::string> _args, 
        bool _pause, 
        std::string _dbus_name){
    
    
    try{
        Py_Initialize();
        py::object mod = py::import("omxplayer.player");
        
        
        py::list args;
        for(auto const& value: _args) {
            args.append(value);
        }
        
        

        OMXPlayer::pyOMXPlayer = mod.attr("OMXPlayer")(_file, args, NULL, NULL, _dbus_name, _pause);
        
        pyOMXPlayer.attr("pauseEvent"   ) = py::make_function(&OMXPlayer::onPause);
        
        OMXPlayer::active = false;

    }
    catch(py::error_already_set){
        PyErr_Print();
    }

}


void OMXPlayer::onPause(){
    std::cout << "onPause" << std::endl;
}

void OMXPlayer::pause(){
    try{
        OMXPlayer::pyOMXPlayer.attr("pause")();
    }
    catch(py::error_already_set){
        PyErr_Print();
    }
}

OMXPlayer.h

#include <boost/python.hpp>
#include <boost/function.hpp>

#include <vector>
#include <iostream>
#include <atomic>

namespace py = boost::python;

class OMXPlayer{
    public:
        OMXPlayer(std::string _file, 
            std::vector<std::string> _args, 
            bool _pause = false, 
            std::string _dbus_name = "");
        
        void pause();
        void onPause();
};

main.cpp function:

#include "OMXPlayer.h"

int main(){
    
    OMXPlayer player1("/root/Setteventi.mp4", std::vector<std::string> {"--loop"}, false, "org.mpris.MediaPlayer2.omxplayer1");

    player1.pause();


}

You can see the python Class from here: https://python-omxplayer-wrapper.readthedocs.io/en/latest/_modules/omxplayer/player/

When eventPause on python side is called it throw this:

Traceback (most recent call last):
  File "<decorator-gen-56>", line 2, in pause
  File "/usr/local/lib/python3.7/dist-packages/omxplayer/player.py", line 48, in wrapped
    return fn(self, *args, **kwargs)
  File "/usr/local/lib/python3.7/dist-packages/omxplayer/player.py", line 550, in pause
    self.pauseEvent(self)
Boost.Python.ArgumentError: Python argument types in
    None.None(OMXPlayer)
did not match C++ signature:
    None(OMXPlayer {lvalue})
Traceback (most recent call last):
  File "<decorator-gen-56>", line 2, in pause
  File "/usr/local/lib/python3.7/dist-packages/omxplayer/player.py", line 48, in wrapped
    return fn(self, *args, **kwargs)
  File "/usr/local/lib/python3.7/dist-packages/omxplayer/player.py", line 550, in pause
    self.pauseEvent(self)
Boost.Python.ArgumentError: Python argument types in
    None.None(OMXPlayer)
did not match C++ signature:
    None(OMXPlayer {lvalue})
Stefano
  • 59
  • 7
  • 1
    From the [boost docs](https://www.boost.org/doc/libs/1_58_0/libs/python/doc/v2/make_function.html): "If F is a pointer-to-member-function type, the target object of the function call (*this) will be taken from the first Python argument, and subsequent Python arguments will be used as the arguments to f." So, did you try to simply pass `&OMXPLayer::onPause`? What is the result? Show the complete example. – pptaszni Jul 24 '20 at 10:08
  • 1
    Yes I have already tried how you say, but it doesn't work because the function signature is different, as you can see from my edited example. – Stefano Jul 24 '20 at 14:23
  • Mmm true, because python calls it with `self` that is a different `OMXPlayer` class. And if you `boost::bind` or `std::bind` the `this` pointer, what is the error? – pptaszni Jul 24 '20 at 14:32
  • I can't or I don't know how. If I just try with: ```pyOMXPlayer.attr("pauseEvent" ) = py::make_function( boost::bind(&OMXPlayer::onPause, this) );``` It will not compile with error: ```/usr/include/boost/python/make_function.hpp:104:57: error: no matching function for call to ‘get_signature(boost::_bi::bind_t, boost::_bi::list1 > >&)’ f,default_call_policies(), detail::get_signature(f));``` make_function require a function and boost:bind return a object. – Stefano Jul 24 '20 at 14:49
  • Should be more or less: `py::make_function(boost::bind(&OMXPlayer::onPause, this));` Or maybe rather `py::make_function(boost::bind(&OMXPlayer::onPause, this, _1));` and the signature: `void onPause(py::object player)` because python calls it with one argument. Or with standard library: `py::make_function(std::bind(&OMXPlayer::onPause, this, std::placeholders::_1));` Sorry for not being more precise, today I don't have any good environment to try your code. – pptaszni Jul 24 '20 at 14:53
  • Related [topic](https://stackoverflow.com/questions/16845547/using-c11-lambda-as-accessor-function-in-boostpythons-add-property-get-sig). Maybe boost supports only function pointers, not any kind of Callable" If this is the case, you cannot do much. – pptaszni Jul 24 '20 at 15:00
  • I already tried your code, but the bind function return a ’function object’, but ’make_function’ will call ’get_signature’ that require a function instead. There is a way to convert a function object that is return from bind to a function? – Stefano Jul 24 '20 at 15:09
  • Unfortunately it is impossible. Only C++ lambdas that do not capture anything can decay to function pointer (but you need to capture `this`). [Here is a trick](https://stackoverflow.com/a/22299052/4165552) to trigger such conversion explicitly. `std::function` cannot decay to a pointer at all. – pptaszni Jul 24 '20 at 15:18

1 Answers1

1

Ok, with @pptaszni help I found how to do it.

The problem was that boost could not detarmine the function signature because boost::bind return a function object.

To avoid this problem, I have to specify the signature when call make_function.

Example:

pyOMXPlayer.attr("pauseEvent") = py::make_function( boost::bind(&OMXPlayer::onPause, this, _1), py::default_call_policies(), boost::mpl::vector<void, py::object>() );

The tricky part is the last argument of py::make_function

Thanks @pptaszni

Stefano
  • 59
  • 7