Avatar billede hurra Novice
22. september 2005 - 14:53 Der er 6 kommentarer og
1 løsning

postmessage og events

Jeg har lavet en program med en masse units. Alle disse units kan sende en masse forskellige beskeder tilbage til hoved programmet, med PostMessage(), hvor jeg fanger beskeden med:
Application->OnMessage = AppMessage;

I funktionen AppMessage har jeg så en masse if-else-if sætninger og det kode der skal udføres på den type besked der nu bliver fanget.

Kan man ikke registrere beskederne, så der bliver én funktion til hver besked? fx.
OnUnit1TimerTimeOut()
OnUnit2MenuClick()

Jeg ved godt at min kode stadig kommer til at fylde det samme, måske end lidt mere. En løsning kunne også være at i AppMessage() kunne kalde funktionen OnUnit1TimerTimeOut(), i stedet for at ha kode til den i AppMesage().

Bare det at skulle registrere alle typerne af beskeder til enkelte funktion ville jo svare meget til sidst nævnte exempel, så i virkligheden ville jeg vel helt at registreringen kunne ske i mine units, så hvis man opretter en funktion i sit hovedprogram der hedder OnUnit1TimerTimeOut(), bliver den kaldt når timeren løber ud, hvis ikke funktionen findes sker der ikke noget.

Sikke en lang smørre, normalt ville jeg nok ikke selv have læst så meget hvis ikke der havde været billeder i :)

Men er der nogen af jeg der har nogle bud ? Jeg bruger forresten bcb6.
Avatar billede mr_chaos Praktikant
23. september 2005 - 08:06 #1
Hver message har forhåbentlig et unikt id, så vil jeg da foreslå at når man registrere en message, så give man en funktionspointer med, derefter ligger man det ind i en hashtabel, så key er id og value er funktionspointeren.

Når du så skal håntere din besked, så laver du et lookup i hashtabellen, og få en funktionspointer ud som du så kalder.

Dette gør det også ret nemt at få flere beskeder til at kalde samme metode, evt. kunne du definere at alle funktionspointerne skulle tage en message med som parameter.
Avatar billede hurra Novice
23. september 2005 - 11:56 #2
Jeg er ikek helt med... Hvad er en hashtable? Og hvordan linker jeg message til funktion via den table?

fx:
typedef void (*EventHandle)(void);
EventHandle handles[100];

Så er der noge med at jeg kan bruge map til det der med arrayet, det kan jeg ikke bare lige skrive uden ad lige nu, men så:

handles[RegisterWindowMessage("CommonControlsMenuItemClickInstrumentsIni"))] = &InstrumentIniClick;

Er det sådan noget her du mener ?
Avatar billede hurra Novice
23. september 2005 - 13:50 #3
Prøv lige at se det her:
typedef void (*EventHandle)(unsigned char side, int handling);
map<int, EventHandle>handles;

void /*TForm1::*/TestMouseClick(unsigned char side, int handling)
{
  Form1->Caption = "Der er blevet trykket på musen, fra side "+IntToStr(side);
}

__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  handles[RegisterWindowMessage("TestMouseClick")] = &TestMouseClick;
  Application->OnMessage = AppMessage;

  PostMessage(Application->Handle, RegisterWindowMessage("TestMouseClick"), 1, 0);
}

void __fastcall TForm1::AppMessage(TMsg& AMessage, bool& Handled)
{
  if(handles[AMessage.message])
      handles[AMessage.message](AMessage.lParam, AMessage.wParam);
}

Er det sådan noget her du mener ? Jeg syntes det er ret tæt på at være smart, men:
Jeg har prøvet at lade TestMouseClick() være en del af min klasse, men så får jeg en fejl:
[C++ Error] Unit1.cpp(26): E2034 Cannot convert 'void (* (_closure )(unsigned char,int))(unsigned char,int)' to 'void (*)(unsigned char,int)'
Kan jeg slippe for den ?
Avatar billede mr_chaos Praktikant
23. september 2005 - 14:01 #4
Det var lige sådan jeg havde tænkt mig det (som i den sidste) :).

Ved desværre ikke hvordan du slipper for den fejlbesked, hvad er det du får ud af dit handles map, tror fejlen ligger der uden lige at kunne sige hvordan den fixes.
Avatar billede bertelbrander Novice
23. september 2005 - 21:00 #5
Det er "lidt" bøvlet:

#include <iostream>
#include <map>
class X
{
public:
  typedef int (X::*fptr)(int a, int b);
  std::map <char, fptr>Map;
  int add(int a, int b) { return a + b; }
  int sub(int a, int b) { return a - b; }

  X()
  {
      Map['+'] = &X::add;
      Map['-'] = &X::sub;
  }

  int Some(int a, int b)
  {
      return ((*this).*(Map['+']))(a, b);
  }
};

int main()
{
  X x;
  std::cout << x.Some(3, 4) << std::endl;
}

Læs også:
http://www.parashift.com/c++-faq-lite/pointers-to-members.html
Avatar billede hurra Novice
26. september 2005 - 12:58 #6
Vildt det virker. Det hele :)

mr_chaos du skal ha points for at besvare mit spørsmål.

bertelbrander, der er noget med at det er ret svært at få afsat nogle points til dig, men du har helt sikkert fortjent nogle. Så send et svar hvis du aligevel vil ha lidt.
Avatar billede hurra Novice
27. september 2005 - 14:17 #7
Jeg har rodet en del mere med det her, og har bland andet også prøvet at lave det hele med MESSAGE_MAP. MESSAGE_MAP havde desværre det problem at den ikke fanger en message posted til application, men kun de messages posted til den form de blev oprettet i.

Men, jeg har lavet det som jeg nævnte jeg ønskede, hvor brugeren bare kan oprette de funktioner han/hun skal bruge, og være ligeglad med resten.

Kort sagt har jeg en form med den funktionallitet jeg ønsker, virtuel, og jeg nedarver fra i mit egentlige projekt.

Det er vidst bedre med lidt exempler. Her er platformen:
class TPlatForm : public TForm
{
  public:
      //Event map, all events will be registred in this table
      typedef void __fastcall(TPlatForm::*EventHandle)(unsigned char FixSite, int action);
      map<int, EventHandle>handles;

      //constructor
      __fastcall TPlatForm(TComponent* Owner):TForm(Owner)
      {
        //set up application event handler
        Application->OnMessage = AppMessage;
        //Register all the known messages
        handles[RegisterWindowMessage("FixSiteTimerTimeOut")] = &TPlatForm::FixSiteTimerTimeOut;
      };

      void __fastcall AppMessage(TMsg& AMessage, bool& Handled)
      {
        if(handles[AMessage.message])
            ((*this).*(handles[AMessage.message]))(fsFixSite(AMessage.wParam), AMessage.lParam);
        else
            OnAppMessage(AMessage, Handled);

      };
      //Events
      virtual void __fastcall FixSiteTimerTimeOut(unsigned char FixSite, int progress){};
      virtual void __fastcall CommonControlsMenuItemClickEnableDebug(unsigned char FixSite, int action)
      {
        if(action)
            DebugGroupBox->Show();
        else
            DebugGroupBox->Hide();
        this->Resize();
      };
      //Default event handler,
      virtual void __fastcall OnAppMessage(TMsg& AMessage, bool& Handled){};
...

Min hoved form starter så med at arve fra TPlatForm
class TMainForm : public TPlatForm
{
...

Hvis nu jeg vil ha en håndtering af TimeOut tingen, kan man bare lave en funktion til det:
void __fastcall TMainForm::FixSiteTimerTimeOut(fsFixSite FixSite, int progress)
{
  switch(progress)
...
Hvis ikke man lave den funktion sker der bare ikke noget når timeren løber ud.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester