Delegaadid
Delegaadid on tüübikindlad (type-safe) funktsiooniviidad. Viidatav funktsioon võib olla ükskõik millise klassi liikmesfunktsioon (meetod), klassi staatiline funktioon või standardne C funktsioon. Antud juhul on delegaat objekt, mis sisaldab viita funktsiooni algusele ja vajadusel ka viita objekti andmete algusele (this pointer). Delegaadi objekt on mallklassist (template class), mille malli parameetrid tagavad tüübikindluse. Viidatav funktsioon võib omada kuni 8 parameetrit ja tagastatav väärtus võib olla nii void kui ka ükskõik millisest ise määratud tüübist.
Delegaatide kasutusalaks on tagasiside funktsioonid (callback functions) ja sündmustest teavitamine. Tüüpilisel juhtumil objekt A omab sündmust S ja objekt B on sellest huvitatud. Sel juhul B registreerib end objekt A juures selleks, et saada sündmuse S toimumisel teavitatud. Olenevalt delegaadi tüübist võib üks delegaat sisaldada ühte või mitut funktsiooniviita. Seega objekti A juures võib registreerida end ka objekt C. Taolisel juhul sündmuse S toimumisel saavad teavitatud nii objekt B kui ka objekt C. Objekti A juures registreerinud objektid (B ja C) võivad kuulutada objektile A, et nad ei ole enam sündmusest S huvitatud.
Kui delegaati kasutatakse tagasiside funktsioonina, siis väljakutsutava funktsiooni F üheks parameeteriks on delegaat D. Funktsioon F alustab protsessiga P. Funktsiooni F väljakutsuja soovib saada infot protsessi P olekumuutuse kohta. Delegaadi D poolt viidatav funktsioon kutsutakse välja juhul, kui toimus funktsiooni F väljakutsuja poolt määratud olekumuutus.
Delegaatide tüübid
AVR C++ Lib's on kolme põhitüüpi delegaate:
- FastDelegate ehk kiirdelegaat - objekt sellest tüübist sisaldab ühte funktsiooniviidet. Kiirdelegaate on üheksa alamtüüpi: FastDelegate0, FastDelegate1, Fastdelegate2, …, FastDelegate8. Sufiksina esinev number näitab viidatava funktsiooni parameetrite arvu. Kõige sagedamini kasutatav FastDelegate on defineeritud FastDelegate0 lühema kirjaviisina. Üks kiirdelegaat võtab SRAM's ruumi 6 baiti.
- MultiDelegate ehk hulkdelegaat - .NET Framework'i eeskujul loodud dünaamiline massiiv objektidest tüübist FastDelegate0. Funktsiooni viitasid saab hulkdelegaati programmi töö ajal juurde lisada ja eemaldada.
- DataDelegate ehk andmedelegaat - põhineb hulkdelegaadil, mille elemendid on klassist FastDelegate1. Pakub võimalust viidatud funktsioonidele edastada piiramatul hulgal andmeid. Kasutaja saab kirjutada koodi, mis täidetakse enne ja/või pärast elementdelegaatide viidatavate funktsioonide väljakutsumist. Andmed edastatakse kasutaja poolt defineeritud ja klassist DelegateController pärineva klassi tüüpi (kontrollerklassi) objekti abil.
Delegaadi põhitüübid on esitatud keerukuse järjekorras alustades kõige lihtsamast. Eelistada tasuks alati lihtsamat, sest nii saab mikrokontrolleri ressurssi kokku hoida.
Katkestuste delegaatid
Delegaadid AVR C++ Lib's on mõeldud esmajoones katkestuste tarbeks. Katkestusdelegaat tuleb enne kasutamist aktiveerida ühe järgneva makroga:
- USING_FAST_DELEGATE
- USING_MULTI_DELEGATE
- USING_DATA_DELEGATE
Need makrod võtavad parameetriks katkestuse nime (näiteks: USING_FAST_DELEGATE(TIMER1_COMP); ). USING_DATA_DELEGATE võtab kaks parameetrit: lisaks katkestuse nimele ka katkestuse delegaadi kontrollerklassi nime (näiteks: USING_DATA_DELEGATE(INT0, ExternalInterruptController); ). Ühe katkestuse peale saab kasutada ainult ühte delegaati ja aktiveeritud delegaadi korral ei saa kasutada tavalist katkestuse alamprogrammi ehk järgnevaid makrosid:
- INTERRUPT_HANDLER - tavaline katkestuse alamprogramm
- EVOCABLE_INTERRUPT_HANDLER - simuleeritava katkestuse alamprogramm. Võimaldab välja kutsuda katkestuse klassi funktsiooni Evoke().
- RECURSIVE_INTERRUPT_HANDLER - katkestuse alamprogramm, kus katkestused on globaalselt vaikimisi lubatud.
- EVOCABLE_RECURSIVE_INTERRUPT_HANDLER - simuleeritava katkestuse alamprogramm, kus katkestused on globaalselt vaikimise lubatud.
- EXCLUDE_INTERRUPT - katkestuse alamprogrammi ja delegaadi aktiveerimise keelamine.
Näited
Kasutamise näide 1
/* UserSourceFile.cpp */ #include <ExternalInterrupt.h> #include <Delegate.h> using namespace CppDelegate; using namespace AVRCpp; template <class MyTimer> class User { private: void OnOverFlow() { /* Do Something */ } public: User() { MyTimer::OverFlowInterrupt::Me<MultiDelegate>().Add( this, &User<MyTimer>::OnOverFlow); } void IncreaseInterest() { MyTimer::OverFlowInterrupt::Me<MultiDelegate>().Add( this, &User<MyTimer>::OnOverFlow); } void DecreaseInterest() { MyTimer::OverFlowInterrupt::Me<MultiDelegate>().Remove( this, &User<MyTimer>::OnOverFlow); } ~User() { MyTimer::OverFlowInterrupt::Me<MultiDelegate>().RemoveAll( this, &User<MyTimer>::OnOverFlow); } }; // User typedef Timer::TimerCounter1 FirstTimer; typedef Timer::TimerCounter2 SecondTimer; USING_MULTI_DELEGATE(TIMER1_OVF); USING_MULTI_DELEGATE(TIMER2_OVF); USING_FAST_DELEGATE(INT0); EVOCABLE_INTERRUPT_HANDLER(INT1) { /* ... */ } class MainClass { private: User<FirstTimer> user1; User<FirstTimer> user2; User<SecondTimer> user3; public: MainClass() : user1(), user2(), user3() { using namespace Timer; FirstTimer::OverFlowInterrupt::Enable(); SecondTimer::OverFlowInterrupt::Enable(); } // MainClass CONSTRUCTOR void Foo() { /* Nelja korra võrra rohkem kutsutakse välja user1.OnOverFlow() meetodit ühe katkestuse kohta. */ user1.IncreaseInterest(); user1.IncreaseInterest(); user1.IncreaseInterest(); user1.IncreaseInterest(); } // Foo ~MainClass() { FirstTimer::OverFlowInterrupt::Me<MultiDelegate>().Clear(); SecondTimer::OverFlowInterrupt::Me<MultiDelegate>().Clear(); } }; // MainClass void x() { /* ... */ } int main() { using namespace ExternalInterrupt; MainClass object; // Simulate interrupt Interrupt0::Evoke(); Interrupt1::Evoke(); // FastDelegate allows only one (member)function to be called on interrupt Interrupt1::Me<FastDelegate>().Bind(x); // Change target in runtime Interrupt1::Me<FastDelegate>().Bind(&obect, &MainClass::Foo); // This notation is also available Interrupt1::Me<FastDelegate>() = x; return 0; } // main
Kasutamise näide 2
#include <ExternalInterrupt.h> #include <Delegate.h> using namespace CppDelegate; using namespace AVRCpp; class SomeClass { public: void SomeFunction() {} }; class StepCounter : public DelegateController<StepCounter> { public: SomeClass *something; private: int32_t counter; bool enabled; typedef InputPin4<PortC> CheckPin; bool Before() { if (enabled) { // Initialize required parameters something = new SomeClass(); // Direction ? if (CheckPin::IsSet() ) counter++; else counter--; // Targets are notified about counter change return true; } // No change, targets are not notified return false; } // Before void After() { // Clean up delete something; } // After public: StepCounter() : DelegateController<StepCounter>(), counter(0), enabled(false) { CheckPin::InitInput(); } // StepCounter CONSTRUCTOR int16_t GetStepsCount() { return counter; } inline void Enable() { enabled = true; } inline void Disable() { enabled = false; } inline bool IsEnabled { return enabled; } }; USING_DATA_DELEGATE(INT1, StepCounter); void x(StepCounter &stepCounter) { int32_t steps = stepCounter.GetStepsCount(); SomeClass *something = stepCounter.something; /* Process parameters 'steps' and 'something' */ /* For example ... */ something->SomeFunction(); } // x class User { private: void y(StepCounter &stepCounter) { int32_t steps = stepCounter.GetStepsCount(); // For example... if (steps > 70000) stepCounter.Disable(); } // y public: User() { using namespace ExternalInterrupt; GlobalInterrupts::Enable(); Interrupt1::Pin::InitInput(); Interrupt1::SetEnabledEvent(Fall); // This .. Interrupt1::Me<DataDelegate<StepCounter> >().Add(x); // ... is identical to this Interrupt1::Me<DataDelegate<StepCounter> >() += x; // Add a memberfunction to DataDelegate Interrupt1::Me<DataDelegate<StepCounter> >().Add(this, &User::y); } // User CONSTRUCTOR } user;