2017-02-14 15 views
0

Прежде всего, позвольте мне представить немного моей структуры классов. Я проектирую небольшую библиотеку для устройства ARM (STM32), и я хочу реализовать пару объектов, определяющих различные периферийные устройства, доступные в системе.Выполнение метода из другого объекта в C++

class xGPIO : public Peripheral 
{ 
public: 
    xGPIO(); 
    xGPIO(GPIO_TypeDef * GPIOx1, uint16_t Pinx1); 
    virtual ~xGPIO(); 

    void on(void); 
    void off(void); 
private: 
    void xInitGPIO(void); 

}; 

Идея, которую я имею в виду, чтобы реализовать внутреннюю общественную функцию с именем обратного вызова, которая устанавливает, какая функция является следующий называться на моем объекте таймера. Однако эта функция наверняка будет методом, определенным в другом классе, и я хочу, чтобы он выполнялся из xTimer. Поведение здесь должно состоять в том, что всякий раз, когда таймер заканчивает обратный отсчет, он должен вызывать функцию, сохраненную программистом в методе callBack.

class xTimer : public Peripheral 
{ 
public: 
    xTimer(TIM_TypeDef * tim); 
    virtual ~xTimer(); 
    /* void callBack(void (*f)()); */ 

private: 
    void xInitTimer(void); 
    TIM_HandleTypeDef htim; 
}; 

void xTimer::callBack(void (*f)()) 
{ 
    f(); 
} 

Код под main.cpp woudl быть следующим:

int main(void) 
{ 
    mySYS->initialize(false); 

    xGPIO myGreenLed (GPIOB, GPIO_PIN_0); 

    /* Declare Timers. */ 
    xTimer myTimer(TIM2);  
    // myTimer.callBack(myGreenLed.on); 
} 

Любые подсказки о том, как решить эту проблему? Есть ли простой способ выполнить этот проект в программе на C++?

+0

Это почти, но не на 100% понятное, что вы хотите сделать. Если таймер должен вызывать ранее установленный обратный вызов, то почему 'void xTimer :: callBack (void (* f)())' принимает функцию как параметр? Разве таймер не должен знать, какую функцию вызывать заранее? – user463035818

+0

Вам нужен один таймер для обработки всех обратных вызовов или может быть разными таймерами для каждого обратного вызова? – user463035818

+1

Название довольно вводит в заблуждение.Вы не хотите вызывать метод другого _class_, вы хотите вызвать метод другого _object_, здесь 'myGreenLed'. Это критически важно: если 'xGPIO :: on' был методом класса' static' (т. Е. Не нуждался в объекте), вы могли бы преобразовать '& xGPIO :: on' в' void (*)() '. – MSalters

ответ

3

Вы используете модели C. C++ имеет гораздо более мощный std::function. Ваш xTimer::callBack должен быть определен как

void xTimer::callBack(std::function<void(void)> f) { f(); } 

так можно назвать, как

myTimer.callBack([&](){myGreenLed.on()}); //using lambda 

или

myTimer.callBack(std::bind(&xGPIO::on, &myGreenLed)); 
+0

'std :: function' выполняет динамическое распределение, поэтому не всегда хорошая идея в системе с открытым металлом, если эта функция может использоваться из прерываний (это также включает в себя место, где объекты будут« разрушены »). Может быть лучше обеспечить функторы, созданные вручную в стеке или глобальные переменные. Просто помню, поскольку я вообще не против C++ (на самом деле я пишу C++ RTOS для таких микроконтроллеров - http://distortos.org/). –

+0

@FreddieChopin: Технически говоря, что-либо в C++ может выполнять динамическое распределение в любой точке. Практически говоря, _calling_ 'std :: function' не будет выполнять динамическое распределение. – MSalters

+0

Это зависит от того, что вы подразумеваете под «вызовом». Выполнение связанной функции не будет выполнять динамическое распределение. Но построение, копирование или дескрипция используют 'new' и' delete'. Мы не знаем, как OP хочет его использовать (возможно, обратные вызовы будут удалены сразу после использования - в прерывании? Возможно, обратные вызовы будут добавлены из других прерываний?), Поэтому я просто помещаю заметку, чтобы он знал о факт. –

1

Вы также можете сделать это без std::function при использовании шаблонов непосредственно:

template<class T> 
class xTimer : public Peripheral 
{ 
public: 
    xTimer(TIM_TypeDef * tim); 
    virtual ~xTimer(); 
    void callBack(T &obj, void (*T::f)()) 
    { 
     (obj.*f)(); 
    } 

private: 
    void xInitTimer(void); 
    TIM_HandleTypeDef htim; 
}; 

и может быть использован как:

xTimer<xGPIO> myTimer(TIM2);  
myTimer.callBack(myGreenLed, &xGPIO::on); 

Вы тогда теряет гибкость std::function, и что он не работает с std::bind и лямбды тогда, но получить преимущество, что нет никакого распределения/открепление.

Шаблон также может быть установлен только на уровне callback, но я полагаю, что вы действительно захотите сохранить значение и назовите его позже, тогда вам нужно будет запланировать установку шаблона.

Если ваш вызываемый метод будет только членом определенного одного типа (т. Е. Всегда xGPIO), тогда вам даже не понадобится шаблон.

Также обратите внимание, что если вы храните экземпляр объекта, который будет вызываться позже, вы должны убедиться, что срок его службы остается до тех пор, пока вам не понадобится его вызывать (или вам нужно будет сделать копию, используйте std::shared_ptr и т. Д.). Это также относится к использованию std::function с указателем объекта и std::ref.