Search code examples
c++pointersfunction-pointersvolatilepointer-to-member

volatile pointer to function showing compile error when using without typedef; need help w/ "void (* volatile userFunc)(void)"


I'm writing an Arduino library using C++ classes. Inside the class, I have a private member variable which is a pointer to a function.

The problem is that I need the pointer to be volatile since the pointer to the function will be set outside an ISR, and can be changed during program execution, but the function will be called inside an ISR. Hence, I think I need a volatile pointer to a nonvolatile function, is that right?

Anyway, what I'm doing is creating a mechanism for allowing a custom user function which will be called periodically by my library.

Here's the basics:

This just shows the essential parts for you to see.

.h file

class myLib
{
  public:
  void attachOverflowInterrupt(void (*myFunc)());

  private:
  volatile void (*userOverflowFunction)(void);
}  

.cpp file

void myLib::attachOverflowInterrupt(void (*myFunc)())
{
  //ensure atomic access
  uint8_t SREGbak = SREG; //back up interrupt state
  noInterrupts(); //interrupts off

  userOverflowFunction = myFunc; //attach function //<--the part that matters to my problem here

  SREG = SREGbak; //restore interrupt state 
}

//inside the interrupt, their function is called essentially like this:
this->userOverflowFunction();

Arduino .ino file (only essential parts shown)

void doSomething()
{
}

myLib1.attachOverflowInterrupt(doSomething);

THIS CODE DOES NOT compile. I get this error:

error: invalid conversion from 'void (*)()' to 'volatile void (*)()' [-fpermissive] userOverflowFunction = myFunc; //attach function

If I do this in the class instead, however, it compiles:

class myLib
{
  public:
  void attachOverflowInterrupt(void (*myFunc)());
  //volatile void (*userOverflowFunction)(void); //compile error
  void (* volatile userOverflowFunction)(void); //<---compiles
} 

OR, if I do this instead, it compiles:

typedef void (*voidFuncPtr)(void);

class myLib
{
  public:
  void attachOverflowInterrupt(void (*myFunc)());
  //volatile void (*userOverflowFunction)(void); //compile error
  //void (* volatile userOverflowFunction)(void); //compiles
  volatile voidFuncPtr userOverflowFunction; //<---compiles
} 

So, what is the difference between these three things that makes the first one fail but the latter 2 compile?

volatile void (*userOverflowFunction)(void); //compile error
void (* volatile userOverflowFunction)(void); //compiles
volatile voidFuncPtr userOverflowFunction; //compiles

Background reading I have done:

  1. http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword
  2. Why is a point-to-volatile pointer, like "volatile int * p", useful?

Additional question:

The 1st background link above states:
"Volatile pointers to non-volatile data are very rare (I think I've used them once), but I'd better go ahead and give you the syntax:"

int * volatile p;

So, when would you use this above technique? In my case?


Solution

  • The problem

    The statement volatile void (*userOverflowFunction)(void); says that you have a pointer to a function returning a type volatile void.

    While a volatile void is rather conceptual (it's easier to understand what a volatile int is), it is yet a valid return type, and it doesn't match the return type of doSomething() .

    The solution

    You said that you wanted the pointer to be volatile.

    Translating this in C++ gives: void (* volatile userOverflowFunction)(void); and here you have your first finding.

    THe second finding you describe corresponds to defining a function pointer type, and then says it's a volatile pointer of that type. Both are equivalent.

    Edit: additional remarks

    The approach that you use is perfectly logic: your function is not volatile (as Kerrek already pointed out in the comments), but your interupt handler needs to get the value of the pointer which might have been changed in a volatile manner.

    You may also be interested in reading about std::signal. As your function pointer is not a volatile std::sig_atomic_t, you should consider making it atomic, using your second approach: std::atomic<voidFuncPtr> userOverflowFunction;