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

Пиша библиотека на Arduino, използвайки C++ класове. Вътре в класа имам частна членска променлива, която е указател към функция.

Проблемът е, че имам нужда указателят да е непостоянен, тъй като указателят към функцията ще бъде зададен извън 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); и ето ви първото ви откритие.

Второто откритие, което описвате, съответства на дефинирането на тип указател на функция и след това казва, че това е непостоянен указател от този тип. И двете са еквивалентни.

Редактиране: допълнителни забележки

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

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

person Christophe    schedule 08.08.2015
comment
Кристоф, страхотен отговор. Благодаря ти! Поставянето на volatile, за да направи показалеца 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