Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Library JetsonGPIO - add_event_detect #25

Closed
dserranozrgz opened this issue Nov 5, 2021 · 6 comments
Closed

Library JetsonGPIO - add_event_detect #25

dserranozrgz opened this issue Nov 5, 2021 · 6 comments

Comments

@dserranozrgz
Copy link

Hi all!

i'm using "add_event_detect" to detect a button click signal to snap photos. But i can't retrieve the context of my class.

A few lines of my code:
//---------------------------------------------------------------------------
void Snap(int iPin, void* ptr)
{
MyGPIO* pGpio = (MyGPIO*)ptr;
};

                          //---------------------------------------------------------------------------
                          MyGPIO::MyGPIO() {};
                          
                          //---------------------------------------------------------------------------
                          MyGPIO::~MyGPIO() {};
                          
                          //---------------------------------------------------------------------------
                          bool MyGPIO::LoadGPIO()
                          {
                              int iPin = 18;
                              GPIO::setup(iPin, GPIO::IN);
                              GPIO::add_event_detect(iPin, GPIO::Edge::RISING, Snap, 50);
                          }

¿Any idea?
thanks!!

@pjueon
Copy link
Owner

pjueon commented Nov 7, 2021

If JetsonGPIO could use lambda objects or custom callable objects as callback,
the problem would be very easy and clean.

But unfortunately current version of JetsonGPIO can only use function pointers as callback,
so I think global object or singleton pattern is the only option for now.

for example, something like this

static MyGPIO* pGpio = nullptr;

static void Snap(int iPin)
{
    if(pGpio == nullptr)
        return;
    //...
};

//---------------------------------------------------------------------------
bool MyGPIO::LoadGPIO()
{
    pGpio = this;
    int iPin = 18;
    GPIO::setup(iPin, GPIO::IN);
    GPIO::add_event_detect(iPin, GPIO::Edge::RISING, Snap, 50);
}

In the future, I want to make JetsonGPIO supports std::function as callback type,
so that we can use all kinds of callable types (ex> lambda, function pointer, std::bind, user-defined callable class),
but there is a technical issue.

I don't know how to implement remove_event_callback function with std::function,
because you cannot compare 2 std::function objects for equality in general.
(https://stackoverflow.com/questions/20833453/comparing-stdfunctions-for-equality)

If somebody has any idea for this issue, please let me know.

@pjueon
Copy link
Owner

pjueon commented Nov 10, 2021

Ok, I came up with an idea to support custom type callback!
And I've just updated the master branch. Pull the master branch and re-build the library first.

Now any object that satisfies the following requirements can be used as callback functions.

  • Callable (argument type: int, return type: void)
  • Copy-constructible
  • Equality-comparable with same type (ex> func0 == func1)

So you don't need to use ugly global variables for this!
All you need to do is just define your custom callback class.

For example:

// define callback object
class MyCallback
{
public:
   explicit MyCallback(MyGPIO& myGPIO) : myGPIO(myGPIO) {}
   MyCallback(const MyCallback&) = default; // Copy-constructible

   void operator()(int channel) // Callable
   {
       // do something with myGPIO
   }

   bool operator==(const MyCallback& other) const // Equality-comparable
   {
       // equality compare example:
       return myGPIO == other.myGPIO;
   }

private:
   MyGPIO& myGPIO;
};

//------
bool MyGPIO::LoadGPIO()
{
   // create callback object
   MyCallback my_callback(*this);
   int iPin = 18;
   GPIO::setup(iPin, GPIO::IN);
   GPIO::add_event_detect(iPin, GPIO::Edge::RISING, my_callback, 50);
}

Or you can also make MyGPIO class into callback itself if you want.

@pjueon pjueon closed this as completed Nov 14, 2021
@yassiezar
Copy link

I'm having some trouble implementing the custom type callback class you posted. Here's my code:

#include <functional>

class GpioCallback {
 public:
  using CallbackType = std::string;
  using Callback = std::function<void(const CallbackType&)>;

  explicit GpioCallback() {}
  GpioCallback(const GpioCallback&) = default;

  void operator()(const CallbackType& channel) { callback_(channel); }
  bool operator==(const GpioCallback& other) const { 
    /* placeholder for now */
    return true; 
  }

  void setCallback(Callback callback) { callback_ = callback; }

 private:
  Callback callback_;
};

I'm not passing in the GPIO controller object for now to avoid a circular include issue I've been having and using placeholders instead. However, when I change CallbackType from std::string to e.g. int, I get a compile error:

error: no matching function for call to ‘std::function<void(const std::__cxx11::basic_string<char>&)>::function(GpioCallback&)’
         comparer([](const func_t& A, const func_t& B) { return comparer_impl<std::decay_t<T>>(A, B); })

Is this the expected behaviour? Am I doing something silly? I've tried it on the latest release and master branches

@pjueon
Copy link
Owner

pjueon commented Feb 28, 2022

@yassiezar

However, when I change CallbackType from std::string to e.g. int, I get a compile error:

In the earlier version of the library(at the moment I posted this example), the channel type in the callback was int.
But in the latest version, it changed to const std::string& from int (related to #39, #40).
So the callback object MUST be callable with const std::string& argument now, not int.

So in your code, the GpioCallback::CallbackType must be std::string. If you want to use callbacks with int type channel,
you can change your code like this:

class GpioCallback {
  using CallbackType = int;
  using Callback = std::function<void(const CallbackType&)>;
  
  /*...*/
  
  // the argument type of the operator() MUST be const std::string& 
  void operator()(const std::string& channel) 
  { 
     // cast the channel to int by std::stoi
    callback_(std::stoi(channel)); 
  }
  
  /*...*/
};

The support for callbacks with int argument might be added in the future, not sure.

@yassiezar
Copy link

Thanks for the quick response. Your suggestions worked and we can use callbacks with different signatures, but still using std::string for the channel names. Using intfor the channel isn't really that crucial, I just wanted to use different signatures for the callbacks themselves

@pjueon
Copy link
Owner

pjueon commented Mar 23, 2022

@yassiezar
You mean you want to use your callback without those wrapper?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants