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;