Forum: PC-Programmierung Pointer auf Klassenmethoden


von Andreas Siebel (Gast)


Angehängte Dateien:

Lesenswert?

Hallo!

Folgendes Problem:

Ich versuche ein DLL zu schreiben, dessen Funktionen aus VC++ und VB
aufgerufen werden und die eine Funktion des aufrufenden Programmes
aufrufen kann. Dabei tritt das Problem auf, dass ich aus der DLL eine
Methode einer Klasse im Hauptprogramm aufrufen muss. Ich hab als
Beispiel folgende Klasse geschrieben:
HEADER
class CCaller
{
  typedef void (*CALL_FUNC)(char para);
public:
  void call_function(void);
  void set_function(CALL_FUNC func);
  CCaller();
  virtual ~CCaller();

private:
  CALL_FUNC m_function_to_call;
};
BODY
CCaller::CCaller()
{

}

CCaller::~CCaller()
{

}

void CCaller::set_function(CALL_FUNC func)
{
  this->m_function_to_call = func;
}

void CCaller::call_function()
{
  this->m_function_to_call(10);
}

was in C super funktioniert.

Jetzt binde ich das ganze in C++ folgendermaßen ein wobei m_caller vom
Typ CCaller ist.

void CMein_problem_mfcDlg::OnOK()
{
  // TODO: Zusätzliche Prüfung hier einfügen
  this->m_caller.set_function(&CMein_problem_mfcDlg::call_me);
  this->m_caller.set_function(call_me);
  this->m_caller.call_function();
  CDialog::OnOK();
}

void CMein_problem_mfcDlg::call_me(char param)
{
  char temp[10];
  this->MessageBox(itoa(param,temp,10));
}

Für die beiden set_function Zeilen bekomme ich Konvertierungsfehler
error C2664.

Ich hab schon einige Seiten im Internet durchforstet und habe nur
Lösungsmöglichkeiten erspäht, die sich auf Zeiger innerhalb einer
Klasse, aber nicht über Klassen hinweg erstrecken. Ähnliche Infos sind
auch im MSDN

Als Anhang hab ich das VC Projekt hinzugefügt.

Schonmal Danke für eure Mühen.

von Rufus T. Firefly (Gast)


Lesenswert?

Um Memberfunktionen einer Klasse von anderer Stelle per Pointer
aufzurufen, müssen diese Memberfunktionen static deklariert sein; beim
Aufruf über einen Pointer fehlt nämlich der auf ein Objekt der Klasse
verweisende this-Pointer.

In Deinem Fall mag es außerdem hilfreich sein, die Zeile mit dem
typedef aus der Klassendeklaration hinauszuverlagern.

Ansonsten: Poste bitte die vollständigen Fehlermeldungen des Compilers,
nicht jeder kennt die Fehlernummern oder hat Lust, nachzusehen, was
exakt die bedeuten mögen.

von Andreas Siebel (Gast)


Lesenswert?

Danke für deine Antwort.

Mein grundsätzliches Problem ist, wie ich aus C dem C++ Programm
verstänlich machen kann, dass was passiert ist. Eine static deklarierte
Funktion kann ja leider wieder nicht auf die Instanz der Klasse
zugreifen und wie in meinem Beispiel z.B. eine Messagebox öffnen.
Hättest du da nen Tip für mich?

Hier noch die Fehlermeldungen:

C:\Programme\Microsoft Visual
Studio\MyProjects\mein_problem_mfc\mein_problem_mfcDlg.cpp(175) :
error C2664: 'set_function' : Konvertierung des Parameters 1 von
'void (__thiscall CMein_problem_mfcDlg::*)(char)' in 'void (__cdecl
*)(char)' nicht moeg
lich
        Es gibt keinen Kontext, in dem diese Konvertierung moeglich
ist
C:\Programme\Microsoft Visual
Studio\MyProjects\mein_problem_mfc\mein_problem_mfcDlg.cpp(176) :
error C2664: 'set_function' : Konvertierung des Parameters 1 von
'void (char)' in 'void (__cdecl *)(char)' nicht moeglich
        Keine Funktion mit diesem Namen im Gueltigkeitsbereich stimmt
mit dem Zieltyp ueberein

von Wolfram (Gast)


Lesenswert?

deklariere eine static Funktion und übergib ihr den this zeiger der
Instanz des Objectes dann kannst du innerhalb der Funktion drauf
zugreifen. Wenn du das Programmübergreifend machen willst also
c-dll mit Visualbasicclient dafür gibt es COM
schau mal nach COM ATL Programmierung. die Callbackroutinen kanns du
dan in VB als Eventroutinen deklarieren und du kannst auch komplette
Objekte übergeben.

von Rufus T. Firefly (Gast)


Lesenswert?

Der erste Satz von Wolfram bringt es auf den Punkt.

Aus diesem Grunde bieten viele Windows-Funktionen, denen
Callbackfunktionspointer zu übergeben sind, auch die Möglichkeit, einen
benutzerdefinierten Wert zu übergeben, der dann bei Aufruf der
Callbackfunktion mitgeliefert wird.

Alternativ ließe sich, sofern vom betreffenden Objekt nur eine Instanz
existiert, ein Pointer darauf als globale Variable ablegen und in der
statischen Memberfunktion als this-Äquivalent verwenden.

von Andreas Siebel (Gast)


Lesenswert?

Danke Wolfram!

Das Problem ist, dass die DLL von Leuten benutzt werden soll, die nicht
allzuviel Ahnung von VB Programmierung haben. In VB ist es ja mit dem
Adress of Operator relativ leicht ner dll eine Callback Funktion zu
übergeben. Wenn ich dass dann noch in ner .bas Datei in ner "Init"
Funktion kapsel müssten man damit zurecht kommen. Das Verwenden der C++
Callback Geschichte soll für mich sein. Mit der statischen Methode ist
das Ganze irgendwie "Vom Zeh durch die Hand um den Hals zur Brust".
Hätte gedacht da gibt es was eleganteres.

Hier also mal der C++ Ansatz mit "static"

class CToCall
{
public:
  void ToCallInstance();
  static void ToCallStatic(char in);
  CToCall();
  virtual ~CToCall();
  static CToCall *mythis;
private:
};
CToCall *CToCall::mythis = 0;

CToCall::CToCall()
{
  this->mythis = this;
}

CToCall::~CToCall()
{
}

void CToCall::ToCallStatic(char in)
{
  mythis->ToCallInstance();
}

void CToCall::ToCallInstance()
{
  //Do Something
}

Und dann folgendes, wo mans braucht

CCaller caller;
CToCall to_call;
caller.set_function(&CToCall::ToCallStatic);
caller.call_function();[/cpp]

das kann man natürlich abwandeln, indem man CToCall an die eigentliche
Klasse vererbt, die den Event braucht und ToCallInstance oder
ToCallStatic  überschreibt usw.

Für weitere Tipps bin ich natürlich dankbar!

von Rufus T. Firefly (Gast)


Lesenswert?

Damit man das verstehen kann, wirst Du ein wenig mehr ausholen müssen.

Was für eine Aufgabe soll die DLL haben; was für ein Aufrufinterface
soll diese DLL bieten? Was hat das ganze mit VB zu tun?

von Andreas Siebel (Gast)


Lesenswert?

Der Code sollte nur als Beispiel Dienen...

Die ToCall Klasse soll die C++-Klasse im Hauptprogramm "simulieren".
Die Caller Klasse liegt dann in der DLL. Wobei die Caller Klasse auch
komplett in C geschrieben sein könnte.

Ziel des ganzen Unterfangens ist eine DLL für die Kommunikation mit
Geräten, die an die Serielle Schnittstelle angeschlossen sind. Die
Kommunikation läuft in Rahmen ab. Die DLL packt nun Daten, die sie über
z.B. über sende_daten(Daten data) erhält. Dann soll sie natürlich auch
dem Hauptprogramm melden wann Daten vom Gerät kommen. Dazu gibt das
Hauptprogramm eine aufzurufende Funktion mit z.B.
set_Callback_Function(CALLBACKFUNCTION func) an die DLL. Diese Funktion
wird dann von der DLL mit Übergabeparametern in welchen die empfangen
Daten liegen aufgerufen.

von wolfram (Gast)


Lesenswert?

genau sowas hab ich vor einer Woche geschrieben
am Seriellen Port haengt ein Gerät was ein Protokoll spricht
die DLL ist zur Kapselung des Protokolls
Ich habe es als COM-DLL gelöst
1. für den Anwender ist es einfacher
BSP im VB6-Client
dim withevents myObj as (Object in COM DLL)
set myObj=new ....
myObj.Methode1 ..

ab da kann man dann eine Eventroutine deklarieren

2. Grund .NET unterstützt derzeit KEINE Callbackroutinen aus normalen
DLL's laut MSDN
(.Net Kapselt die DLL oder COM-DLL mit einer Proxy DLL)

also wenn ein neueres Visual studio zum Einsatz kommt wirds aussr in C
problematisch

Im Netz findet man genügend Bsp. für einfache COM Server
Allerdings lohnt es sich dann darueber nachzudenken ob man nicht die
übertragenen Werte in Properties kapselt.Beispielsweise ADC Werte
bei Zugriff darauf wird ein Lesebefehl ausgelöst...
Ich hatte dein Problem auch da ich einen Thread der das Gerät ständig
abfragte, brauchte. Und der geht nur als static. Sonst kann man ein
CreateThread vergessen.

von Andreas Siebel (Gast)


Lesenswert?

mhhh.. das mit .net ist natürlich wirklich schade :(

ich habs jetzt erstmal mit der static Varianten gelöst und das Polling
der Schnittstelle macht ein eigener Thread, wo wie du schon gesagt hast
das gleiche Probelm auftritt...  aber auch mit der static methode
gelöst.

von A.K. (Gast)


Lesenswert?

Es gibt durchaus pointer auf non-static member functions. Nur sehen die
ein bischen anders aus:

typedef void (CCaller::*CALL_FUNC)(char para);

Der Funktion set_function kann nun eine beliebige void(char) member
function von CCaller oder einer davon abgeleiteten Klasse übergeben
werden. Ist also CMein_problem_mfcDlg von CCaller abgeleitet, dann
sollte der Rest wie oben funktionieren.

Intern werden solche pointer anders dargestellt also normale function
pointer, sie brauchen deutlich mehr Platz.

von A.K. (Gast)


Lesenswert?

Fehlt natürlich noch etwas. Grad der Aufruf sieht anders aus, in
expliziter Form:

void CCaller::call_function()
{
  (this->*(this->m_function_to_call))(10);
}

Wobei sich sicher das eine oder andere this einsparen lässt.

von Chris (Gast)


Lesenswert?

> Wobei sich sicher das eine oder andere this einsparen lässt.

Das zweite this kann man sich sparen, ebenso wie die Klammern um
m_function_to_call.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.