9

I'm trying to use a class function (interrupt service routine),

void (ClassName::*fp)(void)=ClassName::FunctionName;

and attaching it to an Arduino interrupt pin using the function with the following type inputs but that doesn't work.

void attachInterrupt(int, void (*)(void),int);

How can I make this happen? The interrupt service routine (ISR) needs to access privat object data, so I can't make a function outside of the class.

My compiler error:

ClassName.cpp : : In constructor 'ClassName::ClassName()':
ClassName.cpp : *)()'
ClassName.cpp : *)()' to 'void (*)()' for argument '2' to 'void attachInterrupt(uint8_t, void (*)(), int)'

Note: I am looking for a solution inside the class and will accept the answer that shows me a solution or shows me it's not possible.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Flying Swissman
  • 818
  • 2
  • 14
  • 39

3 Answers3

8

If the function is not static, you cannot pass it in input to a function that accepts a non-member function pointer.

Consider that a non-static member function has an implicit pointer to ClassName as its first parameter, which points to the object on which the member function is being invoked.

struct X
{
    static void foo() { } // Does not have an implicit "this" pointer argument
    void bar() { } // Has an implicit "this" pointer argument
};

int main()
{
    void (*f)() = &X::foo; // OK: foo is static
    void (*g)() = &X::bar; // ERROR! bar is non-static
}

Here, not even std::bind() will work, because the result is not convertible to a function pointer. Lambdas are convertible to function pointers, but only if they are non-capturing (and a lambda here would need to capture the object to invoke the member function on).

Therefore, the only (ugly) workaround is to have a global adapter function which invokes the member function on an object which is available through a global pointer variable. The global pointer variable is set prior to calling the function:

struct X
{
    void bar() { }
};

void function_taking_a_function_pointer(void (*f)())
{
    // Do something...
    f();
}

X* pX = nullptr;
void bar_adapter()
{
    pX->bar();
}

int main()
{
    X x; // Some object I want to invoke the member function bar() on...

    pX = &x; // Set the global pointer and invoke the function...
    function_taking_a_function_pointer(bar_adapter);
}

If you want, you can make this slightly more flexible by turning bar_adapter into a function template, and passing the pointer-to-member-function as a template argument:

template<typename T, void (T::*mf)()>
void adapter()
{
    (pX->*mf)();
}

Here is how you would use it:

#include <iostream>

struct X
{
    void foo() { std::cout << "X::foo()" << std::endl; }
    void bar() { std::cout << "X::bar()" << std::endl; }
};

void function_taking_a_function_pointer(void (*f)())
{
    // Do something...
    f();
}

X* pX = nullptr;

template<typename T, void (T::*mf)()>
void adapter()
{
    (pX->*mf)();
}

int main()
{
    X x; // Some object I want to invoke the member function bar() on...

    pX = &x; // Set the global pointer and invoke the function(s)...

    function_taking_a_function_pointer(adapter<X, &X::foo>);
    function_taking_a_function_pointer(adapter<X, &X::bar>);
}

Finally, here is a live example.

Chenna V
  • 10,185
  • 11
  • 77
  • 104
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
2

Each class member function has an implicit first parameter that is the this pointer, so your method in fact is not with void paramter list - it takes one parameter-- the instance it is invoked on.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
  • Ok thanks this makes sense but doesn't solve my problem. Is the only thing I can do to make the object data public and make a ISR outside of the object? or is there a work around? – Flying Swissman Mar 24 '13 at 14:39
  • 1
    @FlyingSwissman: I am proposing a workaround in my answer. Unfortunately, there is not much that can be done when a function accepts a raw function pointer. – Andy Prowl Mar 24 '13 at 14:51
0

You can use boost::function<> or boost::bind<> to point to a class member function:

# include <boost/function.hpp>
# include <boost/bind.hpp>
class FunctionClass {
    private:
       double a_;
    public:
       FunctionClass (const double & a): a_(a ){}
       double multWithA (const double & x) const { return a_*x;}
       double operator ()(const double & x) const { return a_*x;}
};

FunctionClass myClass (2.0);
double x = 12.0;
boost :: function <double (FunctionClass *, double)> funcPtr , funcPtr1;
funcPtr =& FunctionClass :: multWithA;
funcPtr1 =& FunctionClass :: operator ();

std :: cout << myClass . multWithA (x) << std :: endl;
std :: cout << funcPtr (& myClass ,x) << std :: endl;
std :: cout << funcPtr1 (& myClass ,x) << std :: endl;

// Bind the function with the class instance
boost :: function <double (double)> funcPtrNew ;
funcPtrNew = boost :: bind (funcPtr ,& myClass ,_1);
std :: cout << funcPtrNew (x) << std :: endl;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
4pie0
  • 29,204
  • 9
  • 82
  • 118
  • 2
    He can't, he has to pass a `void (*)(void)` function pointer. (I'm not a big fan of the arduino library.) – ipc Mar 24 '13 at 14:36
  • 1
    Because he has to call [void attachInterrupt(int, void (*)(void),int)](http://arduino.cc/en/Reference/AttachInterrupt). – ipc Mar 24 '13 at 14:46