Multicast-Closures?



  • audacia schrieb:

    -=]xXx[=- schrieb:

    Evlt. wäre auch ein Abbruch des Multicast sinnvoll?!?

    Wie würdest du dir das konkret vorstellen? Einen optionalen Parameter würde ich eigentlich gern vermeiden.
    Vielleicht mit einer speziellen Exception...

    Kein Plan 🙂
    Ich hatte nur die Idee 😉 Ich würde es vermutlich ganz unkompliziert über einen direkten Zugriffe auf irgendeine Flag machen... Ob das gut ist, darüber lässt sich noch streiten....

    mfg
    xXx



  • Mittlerweile bin ich schon recht weit fortgeschritten. Folgende Dateien dürften Funktionalität und Syntax ansatzweise demonstrieren:

    multicast_demo.hpp:

    //---------------------------------------------------------------------------
    
    #ifndef main_unitH
    #define main_unitH
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    
    #include <ucl/bcc/multicastsupport.hpp>
    //---------------------------------------------------------------------------
    class TFrmMain : public TForm
    {
    __published:	// Von der IDE verwaltete Komponenten
        TButton *Button1;
        TButton *Button2;
        TButton *Button3;
        TLabel *Label1;
        TMemo *MmoOutput;
        TButton *BtnSwap13;
        void __fastcall Button1Click(TObject *Sender);
        void __fastcall Button2Click(TObject *Sender);
        void __fastcall Button3Click(TObject *Sender);
        void __fastcall BtnSwap13Click(TObject *Sender);
    private:	// Benutzer-Deklarationen
        ucl::MulticastContainer mcc; // Besitzer der MulticastClosure-Objekte
    public:		// Benutzer-Deklarationen
    
        __fastcall TFrmMain(TComponent* Owner);
        void __fastcall AnotherEventHandler(TObject *Sender);
        void __fastcall FooBar(TObject *Sender);
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TFrmMain *FrmMain;
    //---------------------------------------------------------------------------
    #endif
    

    multicast_demo.hpp:

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #include <ucl/bcc/multicast.hpp>
    #pragma hdrstop
    
    #include "main_unit.h"
    #include <ucl/bcc/multicastspec.hpp>
    
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TFrmMain *FrmMain;
    
    //---------------------------------------------------------------------------
    __fastcall TFrmMain::TFrmMain(TComponent* Owner)
        : TForm(Owner)
    {
            /*
             * ucl::asMulticast() erstellt das MulticastClosure-Objekt bei Bedarf.
             * die Objekte werden in der MulticastContainer-Klasse registriert und
             * gelöscht, wenn deren Destruktor aufgerufen wird.
             */
    
            // einen Event-Handler zu Button1->OnClick hinzufügen
        ucl::asMulticast (mcc, Button1->OnClick).push_back (this->AnotherEventHandler);
    
            // ein Closure explizit in ein Multicast-Closure umwandeln
        ucl::asMulticast (mcc, Button2->OnClick);
    
            // Referenz auf MultiCast-Objekt zurückgeben
        ucl::MulticastClosure1 <void, TObject*>& mc = ucl::asMulticast (mcc, Button3->OnClick);
    
            // einen Event-Handler hinzufügen
        mc.push_front (this->Button1Click);
    
            // MulticastClosure-Objekte definieren eine Containerschnittstelle
        ucl::MulticastClosure1 <void, TObject*>::iterator i = mc.begin ();
        mc.insert (++i, this->Button2Click);
    
            // diese Anweisungen sind äquivalent:
        //mc (this);               // direkter Aufruf des MulticastClosure-Objekts
        //Button3->OnClick (this); // Aufruf des Events, dem das Objekt gehört
    }
    
    //---------------------------------------------------------------------------
    
    void __fastcall TFrmMain::Button1Click(TObject *Sender)
    {
        MmoOutput->Lines->Add (AnsiString (__FUNC__) + " called.");
    }
    void __fastcall TFrmMain::Button2Click(TObject *Sender)
    {
        MmoOutput->Lines->Add (AnsiString (__FUNC__) + " called.");
        if (Sender == Button2)
        {
            ucl::MulticastClosure1 <void, TObject*>& mc
                = ucl::asMulticast (mcc, Button2->OnClick);
    
                // hier eine weitere Anwendung der Containerschnittstelle
            if (mc.contains (this->FooBar))
                mc.remove (this->FooBar);
            else
                mc.push_back (this->FooBar);
        }
    }
    void __fastcall TFrmMain::Button3Click(TObject *Sender)
    {
        MmoOutput->Lines->Add (AnsiString (__FUNC__) + " called.");
    }
    
    void __fastcall TFrmMain::AnotherEventHandler(TObject *Sender)
    {
        MmoOutput->Lines->Add (AnsiString (__FUNC__) + " called.");
    }
    void __fastcall TFrmMain::FooBar(TObject *Sender)
    {
        MmoOutput->Lines->Add (AnsiString (__FUNC__) + " called.");
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TFrmMain::BtnSwap13Click(TObject *Sender)
    {
        ucl::asMulticast (mcc, Button1->OnClick)
            .swap (ucl::asMulticast (mcc, Button3->OnClick));    
    }
    //---------------------------------------------------------------------------
    

    ucl::asMulticast() läßt sich nun ohne explizite Angabe der Template-Parameter verwenden - allerdings nur, weil es in ucl/bcc/multicastsupport.hpp für TNotifyEvent spezialisiert wurde. Leider ist der BCC hier etwas buggy.

    ucl::MulticastClosureN <void, TObject*> ließe sich seit C++Builder 2007 auch in Funktorschreibweise schreiben (MulticastClosure <void (TObject*)>), aber das habe ich noch nicht implementiert.

    Hier das Demoprojekt, allerdings noch ohne den interessanten Quelltext - den muß ich noch ein wenig überarbeiten.



  • Aus dem Projekt habe ich mal eine kleine Bibliothek gebastelt, die hier verfügbar ist:
    http://www.audacia-software.de/de/win/ucl/index.htm

    Header-Dateien, Quelltext, Demo-Projekt und Dokumentation liegen bei. Die Bibliothek läuft ab C++Builder 6 (mit C++Builder 5 habe ich es noch nicht getestet).

    Über Feedback jeder Art würde ich mich freuen 🙂



  • Update auf v0.1.1:
    - Unterstützung für C++Builder 5, 6, 2006 und 2007 (auf C++Builder 3 und 4 habe ich keinen Zugriff, und C++Builder 1 ist definitiv zu alt)
    - kleine Bugfixes

    Leider scheint das Interesse ja doch nicht so groß gewesen zu sein...



  • audacia schrieb:

    Leider scheint das Interesse ja doch nicht so groß gewesen zu sein...

    Also ich kann nur für mich sprechen, also ich wüsste nicht wo ich das einsetzten sollte bzw. hat mir diese Funktionalität noch nie gefehlt.



  • Das geht mir genau so. 😞



  • Ich hab in meinem Projekt einen Anwendungsfall, hab das aber einfach über nen Vector mit Funktions-Pointern gelöst.
    Und momentan hab ich keine Zeit das umzubauen ^^

    mfg
    xXx



  • VergissEs schrieb:

    Also ich kann nur für mich sprechen, also ich wüsste nicht wo ich das einsetzten sollte

    Beispiel: ein dezentrales Kommunikationstool (z.B. ein Multiuser-Chat ohne zentralen Server). Auf das Senden einer Nachricht durch einen "Send"-Button müssen alle Teilnehmer reagieren. Für die Kommunikation mit den Teilnehmern sorgt jeweils eine Instanz einer Klasse. Jedes dieser Objekte registriert mittels Multicast-Closure einen Eventhandler bei dem OnClick-Event des Buttons. Wenn Chat-Teilnehmer und entsprechende Objekte hinzukommen, registrieren diese zur Laufzeit einen weiteren Handler, wenn sich ein User verabschiedet, entfernt das Objekt im Destruktor seinen Handler wieder.

    Natürlich ginge das auch anders. Aber das dürfte die sauberste Methode sein 😉



  • Ich würde für diesen Fall eine Liste der Klasseninstanzen nehmen und die dann abarbeiten. 🙂



  • Das ist ein konzeptioneller Unterschied. Im einen Fall melden sich die "Listeners" zentral beim Ereignis an, im anderen kümmert sich das Ereignis selbst um die Verteilung.

    Klar kann man das auch anders machen, aber wenn du so etwas mit mehreren Events zugleich hast, mußt du für jedes einen Wrapper bauen. Und da wird die Multicast-Closure-Lösung dann plötzlich viel übersichtlicher 😉


Anmelden zum Antworten