volatile указатель на функцию, показывающую ошибку компиляции при использовании без typedef; нужна помощь с void (* volatile userFunc)(void)

Я пишу библиотеку Arduino, используя классы С++. Внутри класса у меня есть частная переменная-член, которая является указателем на функцию.

Проблема в том, что мне нужно, чтобы указатель был изменчивым, поскольку указатель на функцию будет установлен вне ISR и может быть изменен во время выполнения программы, но функция будет вызываться внутри< /em> ISR. Следовательно, я думаю, мне нужен изменчивый указатель на энергонезависимую функцию, верно?

В любом случае, то, что я делаю, это создание механизма, позволяющего пользовательской функции пользователя, которая будет периодически вызываться моей библиотекой.

Вот основы:

Это просто показывает основные части для вас.

.h файл

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

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

.cpp-файл

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 (показаны только основные части)

void doSomething()
{
}

myLib1.attachOverflowInterrupt(doSomething);

ЭТОТ КОД НЕ скомпилируется. Я получаю эту ошибку:

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

Однако, если я сделаю это в классе, он скомпилируется:

class myLib
{
  public:
  void attachOverflowInterrupt(void (*myFunc)());
  //volatile void (*userOverflowFunction)(void); //compile error
  void (* volatile userOverflowFunction)(void); //<---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
} 

Итак, в чем разница между этими тремя вещами, из-за которой первая терпит неудачу, а последние 2 компилируются?

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

Предварительное чтение, которое я сделал:

  1. http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword
  2. Почему указатель, такой как volatile int * p, полезен?

Дополнительный вопрос:

Первая справочная ссылка выше гласит:
"Изменчивые указатели на энергонезависимые данные очень редки (думаю, я использовал их однажды), но я лучше дам вам синтаксис:"

int * volatile p;

Итак, когда бы вы использовали эту вышеописанную технику? В моем случае?


person Gabriel Staples    schedule 08.08.2015    source источник
comment
Не существует такой вещи, как энергонезависимая функция. За исключением нестатических функций-членов класса, функции не могут быть квалифицированы. (И даже для NSMF квалификация применяется к неявному аргументу экземпляра, а не к самой функции.)   -  person Kerrek SB    schedule 09.08.2015
comment
Ошибки компилятора выводятся, а не выбрасываются. И есть большая разница между неработающим и не компилируемым.   -  person user207421    schedule 09.08.2015
comment
@KerrekSB, дополнительный вопрос: мой источник barrgroup выше показывает, как сделать изменчивый указатель на энергонезависимые данные, например: int * volatile p;. Это то, что я делаю с функцией в моем первом примере, который компилируется. Имеет смысл (хотя в моем случае функция не может быть ни энергозависимой, ни энергозависимой. Barrgroup также показывает, как сделать энергозависимый указатель на энергозависимую переменную, например: int volatile * volatile p;. Поскольку функции не могут быть volatile, правильно ли будет сказать, что НЕТ ЭКВИВАЛЕНТА УКАЗАНИЯ НА ФУНКЦИЮ для последнего из двух только что упомянутых примеров Barrgroup?   -  person Gabriel Staples    schedule 09.08.2015
comment
Функции — это не данные. Функции не являются переменными. Функции — это отдельный вид сущности.   -  person Kerrek SB    schedule 09.08.2015


Ответы (1)


Проблема

Оператор volatile void (*userOverflowFunction)(void); говорит, что у вас есть указатель на функцию, возвращающую тип volatile void.

Хотя volatile void является довольно концептуальным (легче понять, что такое volatile int), он все же является допустимым типом возвращаемого значения и не соответствует возвращаемому типу doSomething() .

Решение

Вы сказали, что хотите, чтобы указатель был изменчивым.

Перевод этого на C++ дает: void (* volatile userOverflowFunction)(void); и вот вам первое открытие.

Второе обнаружение, которое вы описываете, соответствует определению типа указателя функции, а затем говорит, что это изменчивый указатель этого типа. Оба эквивалентны.

Изменить: дополнительные примечания

Подход, который вы используете, совершенно логичен: ваша функция не изменчива (как уже указал Керрек в комментариях), но ваш обработчик прерывания должен получить значение указателя, который мог быть изменен изменчивым образом.

Вам также может быть интересно прочитать о std::signal. Поскольку ваш указатель функции не является volatile std::sig_atomic_t, вам следует подумать о том, чтобы сделать его атомарным, используя второй подход: std::atomic<voidFuncPtr> userOverflowFunction;

person Christophe    schedule 08.08.2015
comment
Кристоф, отличный ответ. Спасибо! Размещение volatile, чтобы сделать указатель изменчивым, было тем, что меня так смутило. Мне никогда не приходило в голову, что у вас может быть изменчивый тип данных, возвращаемый функцией, поскольку мне это никогда раньше не требовалось. - person Gabriel Staples; 09.08.2015
comment
Что касается обеспечения атомарного доступа, то этот код работает на 8-битном микроконтроллере Atmel AVR (ATmega328), а доступная библиотека C определена в AVR-Libc. Насколько я знаю, у меня нет доступа к std::signal. Скорее всего, атомарный доступ обеспечивается отключением прерываний. Я пошел дальше и добавил код обратно в свой вопрос, чтобы продемонстрировать это. Кроме того, AVR-libc предоставляет макрос атомарного блока для обеспечения атомарного доступа для чтения и записи здесь: nongnu.org/avr-libc/user-manual/group__util__atomic.html. Однако внутри ISR прерывания автоматически отключаются при входе... - person Gabriel Staples; 09.08.2015