[C++] Problem mit überladener Member-Funktion

Das geht auch viel eleganter :) Allerdings ist das schon etwas "advanced" und ich weiß nicht, ob es Dir soviel bringt, wenn Du es einfach benutzt ohne zu wissen wie es funktioniert. Aber wenn Du es wissen willst, sag bescheid ;)
 
Hallo daemon777,

nur mal als Gedanke.

Eine Funktion die eine Zeile scannt und alle einzelnene Wörter in eine stl Liste stellt. D.h. es werden einfach alle Leerzeichen, Tabulatoren, ... entfernt.

Der erste Eintrag in dieser Liste beinhaltet dann das Kommandowort. Dieses Kommandowort wird dann in einer Tabelle gesucht. Diese Tabelle beinhaltet das Kommandowort und die dazugehörige Funktion/Methode die dazu aufgerufen wird.

Diese Kommandofunktion bekommt als Parameter die stl Liste mit den Parametern übergeben und kann diese dann auswerten.

Das ganze ist in Code relativ kurz, gut erweiterbar und übersichtlich.

Du brauchst:

- Eine Methode zum erstellen der Parameterliste.
- Eine Strukturdefinition
- und einen "Dreizeiler" um diese Tabelle zu parsen.

MfG

Arnd
 
Cool danke :D

7H3 N4C3R meinst du das gleiche ? ^^
Hab mir doch gedacht dass ihr noch einen eleganteren Weg kennt sonst hätte ich ja nicht gefragt :evillol:

Und zu der Sache mit dem Code: Immer her damit :D

Was Strings angeht hab ich leider noch ziemlich nachholbedarf wie ihr seht :( Denke ansonsten werde ich keine großen Probleme haben ( jedenfalls hoffe ich das )
 
Japps, im Prinzip meinen wir das gleiche.
Die Kommandofunktion kann man z.B. durch eine Map von Strings und Funktoren realisieren. Das ist an sich supereinfach zu benutzen.
 
Mein Ansatz ist mal wieder ein Mischmasch aus C und C++ :-). Die Funktionsweise ist die selbe, nur die Realiserung in reinem C++ ist sicher noch eleganter aber auch schwieriger zu verstehen wenn man es noch nie gesehen hat.

Versuch Dich doch erst mal selber daran. An Hand der Beschreibung sollte es doch eigentlich kein Problem sein. Das einzige knifflige sehe ich in der Deklaration eines Funktionspointers :-).

MfG

Arnd
 
Ich hab grad eine Fehlermeldung mit der ich nciht klar komme :D
Und zwar geht es um einen unaufgelösten externen Verweis.

Hab auch versucht alles auszukommentieren was neu ist aber der Verweis geht nicht weg.

-------------------Konfiguration: maps - Win32 Release--------------------
Kompilierung läuft...
main.cpp
Linker-Vorgang läuft...
main.obj : error LNK2001: Nichtaufgeloestes externes Symbol "public: int __thiscall Datei::dread(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class Karte &)" (?dread@Datei@@QAEHV?$basic_string@DU?$char_trai
ts@D@std@@V?$allocator@D@2@@std@@AAVKarte@@@Z)
Release/maps.exe : fatal error LNK1120: 1 unaufgeloeste externe Verweise
Fehler beim Ausführen von link.exe.

maps.exe - 2 Fehler, 0 Warnung(en)

Code ist eigentlich der gleiche wie oben. Also ich bekomme die Fehlermeldung auch wenn ich den Code wieder "kürze".
 
Alsooo, mal grundsätzlich zu unaufgelösten Verweisen (oder auf englisch: unresolved external):

Mal ein winziges Beispiel:
Code:
void funktion1(int);

void funktion2()
{
  funktion1( 5);
}

funktion1 wird hier mit einer Definition bekannt gemacht. funktion2 kann sie nun also aufrufen und der Compiler ist happy. Jetzt kommt aber der Linker und muss alle Objekt-Dateien zusammensetzen zu einer Exe (jede CPP-Datei wird zu einer Objekt-Datei kompiliert).

Nun merkt der Linker aber: "Hmm... funktion2 ruft funktion1 auf, ich suche sie mal in allen Objekt-Dateien, die ich so habe. Jetzt finde ich aber ich finde das Ding nirgendwo. Ich melde dem Programmierer also einen unaufgelösten Verweis."



Ich hoffe das hilft schonmal grundsätzlich zu dieser Fehlermeldung. :) Warum die jetzt kommt, kann viele Gründe haben. Wenn man mit einem System arbeitet, dass makefiles hat, können z.B. die Abhängigkeiten veraltet sein und es wird nicht alles übersetzt. IDEs wie VisualStudio nehmen einem diese Arbeit normalerweise ab.
Viel häufiger passiert es aber, dass man z.B. vergessen hat, die Funktion zu implementieren. Oder aber, die Definition unterscheidet sich von der Deklaration. (Definition ist normalerweise das, was man in den Header schreibt und Deklaration das "Ausprogrammieren", das man in der CPP-Datei macht). Beispiel dafür:
Code:
// test.h

void funktion1( int i);

// test.cpp

void funktion1( double i);
In diesem Fall gibt es nur eine Implemenation für double, nicht aber für int. Der Aufruf von funktion1 mit int würde hier einen nicht aufgelösten externen Verweis ergeben.

Ich hoffe, das hilft dir bei der Lösung des Problems. Ansonsten hilf uns nochmal mit aktuellem Quellcode ;)
 
Damit hab ich jetzt den (die) Fehler gefunden.

War ja irgendwie auch klar das ich da fehler reinbaue wenn sich die Funktion 100 mal ändert die Klasse an sich aber nicht. Da waren ja Fehler drinn das gibts gar nicht :D

Danke auch für die Erklärung wir mir in Zukunft sicherlich noch oft weiterhelfen.

Hab bestimmt bald die nächste Frage zum Code :D
 
Hehe, kein Problem mit der Erklärung ;) Compiler und Linker sind deine Freunde (meistens... :freak: ), sie sprechen nur ne komische Sprache :D

Und Fragen, immer wieder gern ;)
 
So und schon komm ich wieder nicht weiter :D

Ich hab mir gedacht so lange es sich um den gleichen Code handelt sollte das der Übersichtshalber auch in einem Thread stehen. Wenn ich falsch gedach hab bitte sagen :)

Und zwar geht es um den Funktionspointer. Ein bischen was hab ich schon zusammen bekommen allerdings bekomme ich eine Fehlermeldung dass der parameter nicht Konvertiert werden kann. Dürfte eigentlich kein allzu großer Fehler sein aber ich versteh die Sprache des Kompilers immer noch nicht so ganz :D

Hier ist mal der Code:

Code:
#ifndef _datei_
#define _datei

#include <StdIO.h>
#include <IO.h>
#include <FCntl.h>
#include <iostream>
#include <string>
#include <fstream>
#include <karte.h>

using namespace std;

DWORD GetFileSize(char* pcFilename)
{
	DWORD dwSize;

	int iFile = open(pcFilename, O_BINARY | O_RDONLY);
	dwSize = filelength(iFile);
	close(iFile);

	return dwSize;
}

struct SParameters
{
	int		iNumParameters;
	int		aiInteger[16];
	float	afFloat[16];
	string	aString[16];
};

struct SCommand
{
	char acName[256];
	void (* pFunction)(SParameters&);
};

class Claden
{
  private:
	char*		m_pcScript;
	int			m_iScriptSize;
	SCommand	m_aCommand[1024];
	int			m_iNumCommands;
  
  public:
	Claden(char* filename);
	~Claden();
	
	void AddCommand(char* pcName, void (* pFunction)(SParameters&));
	SCommand* FindCommand(char* pcName);

	inline char* GetScript()	{return m_pcScript;}
	inline int	 GetScriptSize(){return m_iScriptSize;}

};


Claden::Claden(char* filename) : m_pcScript(NULL), m_iScriptSize(0), m_iNumCommands(0)
{
	if(filename == NULL)  return;

	FILE* pFile = fopen(filename, "rb");

	if(pFile != NULL)
	{
		DWORD dwFileSize = GetFileSize(pFile,NULL/*Ka was das hier soll*/);
		m_pcScript=new char[dwFileSize];
		ZeroMemory(m_pcScript, dwFileSize);

		fread(m_pcScript, 1, dwFileSize, pFile);
		fclose(pFile);

		m_iScriptSize = (int)(dwFileSize);
	}
}

Claden::~Claden()
{
	delete m_pcScript;
}

void Claden::AddCommand(char* pcName, void (* pFunction)(SParameters&))
{
	if(!pcName || !pFunction || m_iNumCommands >= 1024) return;

	strcpy(m_aCommand[m_iNumCommands].acName, pcName);
	m_aCommand[m_iNumCommands].pFunction = pFunction;

	m_iNumCommands++;
}

SCommand* Claden::FindCommand(char* pcName)
{
	if(!pcName) return NULL;

	for(int i=0; i<m_iNumCommands; i++)
	{
		if(!strcmp(pcName, m_aCommand[i].acName))
		{
			return &m_aCommand[i];
		}
	}

	return NULL;
}



class Datei
{

private:
	std::string m_dateiname;
	std::string m_levelname;
	
	int m_NumCommands;	
public:
	
	inline Datei();
	
	int dread(char* fileName, Karte& karte);

	// Die Script Funktionen
	inline void name(SParameters& p);
	void SetField(SParameters& p);
};

inline Datei::Datei()
{
	m_NumCommands=1;	
};


inline void Datei::name(SParameters& p)
{
	
}

inline void Datei::SetField(SParameters& p)
{

}

int Datei::dread(char* fileName, Karte& karte)
{
	Claden script(fileName);

	for(int i = 0; i <m_NumCommands;i++)
	{
		script.AddCommand("name", name);
		script.AddCommand("SetField", SetField);
	}  

  return 1;
}
#endif

Fehlermeldung ist folgende:

--------------------Konfiguration: maps - Win32 Release--------------------
Kompilierung läuft...
main.cpp
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\laden.h(152) : error C2664: 'AddCommand' : Konvertierung des Parameters 2 von 'void (struct SParameters &)' in 'void (__cdecl *)(struct SParameters &)' nicht moeglich
Keine Funktion mit diesem Namen im Gueltigkeitsbereich stimmt mit dem Zieltyp ueberein
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\laden.h(153) : error C2664: 'AddCommand' : Konvertierung des Parameters 2 von 'void (struct SParameters &)' in 'void (__cdecl *)(struct SParameters &)' nicht moeglich
Keine Funktion mit diesem Namen im Gueltigkeitsbereich stimmt mit dem Zieltyp ueberein
Fehler beim Ausführen von cl.exe.

maps.exe - 2 Fehler, 0 Warnung(en)


Der Code ist jetzt leider etwas lang aber wenn ich Teile weglasse verberge ich möglicherweise den Fehler.

Hoffe ihr könnt mir mal wieder weiterhelfen.
 
Naja... du gehst gleich auf's ganze :) Was Du da willst sind keine Funktions-Zeiger, sondern Methoden-Zeiger. Das ist ein wichtiger Unterschied.

Während eine Funktion frei aufgerufen werden kann, gehört zu einer Methode immer das dazugehörige Objekt! (Methoden operieren nur auf Daten von Objekten)

D.h., Du musst zuerst einen Methodenzeiger formen und dann zusätzlich im AddCommand noch das Objekt übergeben, von dem Du die Methode aufgerufen haben willst.

Methodenzeiger definiert man so:

Code:
class A
{
public:
   A( int i) : i_(i) {}
   void print() { std::cout << i_ << std::endl; }
   void increment() { ++i_; }
private:
   int i_;
};

int main()
{
  A a1 (5);
  A a2 (6);
  A* pa1 = &a1;

  typedef void (A::* Methode)();
  Methode m = &A::print;

  (a1.*m)(); // 5
  (a2.*m)(); // 6

  m = &A::increment;
  (a1.*m)();
  m = &A::print;

  (pa1->*m)(); // 6
}

Ich hoffe mal, ich hab die Syntax so freihand hinbekommen. Wenn nicht, meckern D:
 
Hallo daemon777,

das Problem mit dem Objektzeiger kannst Du alternativ umgehen, wenn Du die Methode als static deklarierst. Dann wird kein Objekt benötigt.

Was mir auch aufgefallen ist, das einige Sicherheitsabfragen fehlen.

DWORD GetFileSize(
HANDLE hFile, // handle to file
LPDWORD lpFileSizeHigh // high-order word of file size
);

GetFileSize erwartet ein HANDLE und keinen FILE*. Es kann 0 zurück kommen und ein new mit 0 ist nicht wirklich gut. Der zweite Parameter von GetFileSize() ist übrigens für Files gedacht deren Länge 4GB übersteigt.

Du solltest daher schon Deine eigene Funktion verwenden. Die es ja auch gibt :-).
Allerdings wird das open nicht auf Fehler abgefragt! Und ein zweimaliges open auf das selbe File muss auch nicht unbedingt funktionieren.

Das hier ist auch nicht wirklich schön:
SCommand m_aCommand[1024];
Ich würde vorschlagen:
std::list<SCommand> m_ListOfCommands;


MfG

Arnd
 
Irgendwie steh ich grad aufm Schlauch :D

Will jetzt nicht den Code wieder zerhacken. gibt es denn eine einfache Lösung wie ich aus meinem Code einen funktionierenden formen kann ? Und noch mal ne allgemeinere Frage: Was bedeutet (__cdecl *) ?
 
Versuch mal:

Code:
	// Die Script Funktionen
	static void name(SParameters& p);
	static void SetField(SParameters& p);

cdecl ist eine Aufrufkonvention, die festlegt wie Parameter auf dem Stack übergeben werden. Das spielt bei gemischter Verwendung von C und C++ eine Rolle.


MfG

Arnd
 
Kompilieren und Linken ist jetzt fehlerfrei aber sobald eine Instanz der Claden Klasse erstellt wird stürtzt das Programm ab. Also bei der Zeile "Claden script(fileName);".
Das mit dem std::list<SCommand> m_ListOfCommands; hab ich jetzt noch nicht gemacht. Würde das helfen ? Wenn ich da std::list<SCommand> m_aCommand drauß mache muss ich dann im Code sonst noch was ändern oder funktioniert das dann wie bisher ?

Und überhaupt was heißt std::list<SCommand> ? Ich hab das bisher noch nie benutzt :D

Sorry wenn ich so viele Fragen habe aber ich bin im Moment ein wenig übermotiviert :p
 
Hallo daemon777,

das das Programm jetzt linkt ist doch schon mal ein Schritt in die richtige Richtung :-).

Das static hat natürlich ein paar Konsequenzen, die auch beachtet werden müssen.
In einer static Methode bist Du zwar in der aktuellen Klasse, aber es ist kein Objekt vorhanden auf das Du zu zugreifen kannst.

D.h. Du brauchst eine Referenz auf das Objekt mit dem Du arbeiten willst. Wenn das viele verschiedene sind, machst es wahrscheinlich mehr Sinn den Ansatz von 7H3 N4C3R weiterzuverfolgen.

Wo das Programm genau abstürzt das verrät Dir am besten der Debugger.

Am einfachsten übergibst Du der statischen methode einen Pointer auf das Objekt mit der es arbeiten soll.

z.B:
Code:
            Datei::name(<Parameter>, <Referenz auf Objekt>);

Das std:list bedingt auch eine andere Handhabung. Schliesslich ist das eine Liste variabler Länge und kein statisches Array mehr:

Hinzufügen zur Liste:
Code:
  std::list<CString> lListOfStrings;
  lListOIfStrings.push_back( "Hallo Welt" );
  lListOIfStrings.push_back( "Hallo Welt2" );
  lListOIfStrings.push_back( "Hallo Welt3" );

Iterieren der Liste:
Code:
  std::list<CString>::iterator lItem = lListofStrings.begin();

  while( lItem != lListOfStrings.end() )
  {
    cout << *lItem << endl;
    lItem++;
  }

Löschen eines Eintrags:
Code:
  long lCount = 0;

  while( lItem != lListOfStrings.end() )
  {
    if( lCount == 1 )
    {
       lListOfStrings.erase( lItem );
       break;
    }
    lItem++;
    lCount++;
  }

Das ganze ist flexibel einsetzbar. Man muss wesentlich weniger Code zur Verwaltung schreiben. Und es wird nicht unnötig Platz verbraucht.

MfG

Arnd
 
Zuletzt bearbeitet:
So, hab mir grad mal die Zeit genommen, das ganze auf Methodenzeiger umzustellen ;)

Das hier tust Du am besten in einen eigenen Header, so wie's da steht. Nur noch Includeguards einfügen.
Code:
#include <memory>

struct SParameters;

class CallBase
{
public:
   virtual void operator()( SParameters&) const = 0;
   virtual CallBase* clone() const = 0;
   virtual ~CallBase() throw() {}
};
class CallHolder
{
public:
   CallHolder() {}
   explicit CallHolder( std::auto_ptr<CallBase> call) : _call( call) {}
   CallHolder( const CallHolder& other) : _call( other._call->clone())
   {
   }
   CallHolder& operator=( const CallHolder& other)
   {
      CallHolder tmp( other);
      _call = tmp._call;
      return (*this);
   }
   void operator()( SParameters& params) const
   {
      (*_call)( params);
   }
   
private:
   std::auto_ptr<CallBase> _call;
};

template <class T>
class MethCall: public CallBase
{
public:
   typedef T* ObjectPtr;
   typedef void( T::* Method)( SParameters&);

   MethCall( ObjectPtr o, Method m) : _o(o), _m(m) {}
   virtual void operator() ( SParameters& params) const
   {
      (_o->*_m)( params);
   }
   virtual MethCall* clone() const
   {
      return new MethCall( _o, _m);
   }
private:
   ObjectPtr _o;
   Method _m;
};

template <class T>
CallHolder make_call( T* o, void( T::* m)( SParameters&))
{
   return CallHolder( std::auto_ptr<CallBase>( new MethCall<T>( o, m)));
}



In der eigentlichen Datei includest Du dann den Header und machst noch das hier:
Code:
struct SCommand
{
	char acName[256];
	CallHolder pFunction;
};

...

class Claden
{
...
        // Signatur der Deklaration von AddCommand auch anpassen
	void AddCommand(char* pcName, CallHolder pFunction);
...
};

...

int Datei::dread(char* fileName, Karte& karte)
{
	Claden script(fileName);

	for(int i = 0; i <m_NumCommands;i++)
	{
		script.AddCommand("name", make_call( this, &Datei::name));
		script.AddCommand("SetField", make_call( this, &Datei::SetField));
	}  

  return 1;
}


Et voila... sollte gehen ;) Hab jetzt aber nur das Call-Gedöns getestet. Falls es nicht klappt, poste einfach nochmal den Code + Fehlermeldungen. Falls es Dich interessiert, wie das eigentlich genau funktioniert, frag auch nach ;)
 
Zuletzt bearbeitet:
Hallo 7H3 N4C3R,

das nenn ich mal einen Service :-), aber als leichte Kost würde ich das nicht bezeichnen :-).

MfG

Arnd
 
Cool danke für den Code :D

Getestet hab ichs jetzt noch nicht aber ich werd bestimmt noch ein paar Fehler reinbauen :p

Erklährung währe nicht schlecht. Virtuelle Methoden und Templates hab ich nämlich auch noch nie benutzt.
 
Puh, also zumindest mit Polymorphie und virtuellen Methoden solltest Du dich schon ein klein wenig beschäftigt haben, sonst rede ich mir den Mund fusselig *gg* Und das Template da, hm im Prinzip gehts auch ohne. Der Code ist eh nur halb so generisch, wie er sein sollte. Nur dann wird das Verständnis noch schlimmer :)
 
Zurück
Oben