Problem bei Funktionszeiger!



  • Ich hab jetzt das Problem,dass ich über den Basisklassenzeiger nicht die Attribute der Objekte aus den abgeleiteten Klassen ändern kann. Wie löse ich das Problem am besten?

    Zusätzlich will ich noch Funktionen ohne Rückgabewert und Argumente übergeben, habe dazu den Quelltext wie folg erweitert:

    template<>
    struct CommandNM1<void,void> : Command
    {
        typedef void( *FuncPtr)( void );
    
    	string					   Id;
        FuncPtr                    Function;
    
        CommandNM1( FuncPtr Func, string Id_c ) :
    		Id(Id_c),
            Function( Func )
        {
        }
    
        virtual void execute(const int& arg)
        {
            Function(  );
        }
    
    	virtual void execute()
        {
            Function();
        }
    
    	virtual string getId()
        {
            return Id;
        }
    
    };
    

    und die Funktion Command make_command( ResultType (Func)( ArgumentType ), ArgumentType Arg, string Id )

    überladen:

    template<typename ResultType, typename ArgumentType>
    Command* make_command( ResultType (*Func)( ArgumentType ), string Id )
    {
        return new CommandNM1<ResultType,ArgumentType>( Func, Id );
    }
    

    wenn ich jetzt

    void test(void ){std::cout<<"hallo"; }
    
    int main()
    {
    
    	Flash flash52401;
    	int t=12;
    	flash_init_52401(flash52401);
    
    	Command* c1 = make_command(test ,"hallo");  ...
    

    aufrufe sagt der compiler:
    Fehler 1 error C2780: 'Command *make_command(ObjectType &,ResultType (__thiscall ObjectType::* )(ArgumentType),ArgumentType,std::string)': Erwartet 4 Argumente - 2 unterstützt
    Fehler 4 error C2780: 'Command *make_command(ResultType (__cdecl *)(ArgumentType),ArgumentType,std::string)': Erwartet 3 Argumente - 2 unterstützt
    Fehler 2 error C2784: "Command *make_command(ResultType (__cdecl *)(ArgumentType),std::string)": template-Argument für "überladener Funktionstyp" konnte nicht von "überladener Funktionstyp" hergeleitet werden.
    Fehler 3 error C2784: "Command *make_command(ResultType (__cdecl *)(ArgumentType),std::string)": template-Argument für "überladener Funktionstyp" konnte nicht von "überladener Funktionstyp" hergeleitet werden.

    bin am verzweifeln....



  • Funktionsaufrufe ohne Argument sind eine neue Template Klasse, die du mit den bisherigen Templates nicht abdecken kannst. Wenn du member und non-member Funktionen damit erschlagen willst brauchst du 4 neue Template Klassen (jeweils 2 für non-member und 2 für member Funktionen, wegen der void Spezialisierung). Wenn du aus denen allerdings auch noch die Rückgabewerte auslesen willst ufert das Ganze extrem aus.
    Um das zu vereinfachen könnte man die Basisklasse mit einem Variant Datentyp ausstatten, der den Rückgabewert eines Commands aufnimmt. Damit geht zwar die Typsicherheit flöten, aber man muss keine halsbrecherischen Konstrukte bauen, um an das Ergebnis eines Funktionsaufrufs zu kommen. Du brauchst zwar immer noch 4 templates für jede Kombination einer Funktion (void/non-void und member/non-member), bist damit aber dann auch fertig.



  • vielen dank... hab den quellcode jetzt erweitert(für (member-funktion ohne rückgabe und argument) und für( non member ohne rückgabe und ohne argument) ).

    Jetzt habe ich folgendes Problem: Ich habe auch Funktionen mit Verweise bspw.

    void set_startaddress(const int &addr)
    

    ...muss ich dafür auch wieder extra eine template-kalsse erstellen??



  • Ich würde den Übergabeparameter von const int& auf int ändern, intrinsische Datentypen per const reference zu übergeben macht wenig Sinn, die kannst du besser per value übergeben. Dann brauchst du auch kein neues Template.



  • Ich würde den Übergabeparameter von const int& auf int ändern, intrinsische Datentypen per const reference zu übergeben macht wenig Sinn, die kannst du besser per value übergeben. Dann brauchst du auch kein neues Template.

    habe ich jetzt so gemacht... aber wieso macht das wenig Sinn und was versteht man unter intrinsische Datentypen(Datentypen wie int, char usw. die zum Standart gehören, also integrierte?)? per reference wird doch nciht extra eine kopie des Objekts erstellt, ist das nicht leistungsschonender?

    Jetzt ist mal wieder ein ganz anderes Problem aufgetreten... ich habe gestern auf die klasse fertig geschrieben, meine command objekt erstellt und alles lief. jetzt habe ich das projekt in visual studio 2010 eingefügt(vorher vs2008) und der compiler gibt mir den fehler:

    Fehler 1 error LNK2005: "class Command * __cdecl make_command(void (__cdecl*)(void),class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?make_command@@YAPAVCommand@@P6AXXZV?basic_string@DU?basic\_string@DU?char_traits@D@std@@V?$allocator@D@2@@std@@@Z) ist bereits in command.obj definiert. c:\Users\administrator\documents\visual studio 2010\Projects\server-software\Server-Software\main.obj
    Fehler 2 error LNK1169: Mindestens ein mehrfach definiertes Symbol gefunden. c:\users\administrator\documents\visual studio 2010\Projects\server-software\Debug\Server-Software.exe 1

    aus...wodran könnte das liegen??? ist doch der gleiche quellcode?

    Hier mein code aus der class command:

    #include <string>
    #include "string.h"
    using namespace std;
    // Abstrakte Basisklasse für alle möglichen Commands
    class Command
    {
    public:
    
        virtual ~Command()
        {
        }
    
    	void operator()(const int& arg)
        {
            execute(arg);
        }
    
    	void operator()()
        {
            execute();
        }
    
    	bool operator==( string Id)
        {
    		if(Id.compare(this->getId()))
    			return false;
    		else
    			return true;
        }
    
    private:
    
    	virtual void execute(const int& arg) = 0;
    	virtual void execute() = 0;
    
    	virtual string getId() = 0;
    
    };
    
    // Implementation für non-member Funktion mit einem Argument und Rückgabewert
    template<typename ResultType, typename ArgumentType>
    struct CommandNM1 : Command
    {
        typedef ResultType( *FuncPtr)( ArgumentType );
    
    	string					   Id;
        ResultType                 Result;
        ArgumentType			   Argument;
        FuncPtr                    Function;
    
        CommandNM1( FuncPtr Func, ArgumentType Arg, string Id_c ) :
    		Id(Id_c),
            Function( Func ),
            Argument( Arg ),
            Result( ResultType() )
        {
        }
    
    	virtual void execute()
        {
            Result = Function(Argument);
        }
    
    	virtual string getId()
        {
            return Id;
        }
    };
    
    // Spezialisierung für non-member Funktionen mit einem Argument und ohne Rückgabewert
    template<typename ArgumentType>
    struct CommandNM1<void,ArgumentType> : Command
    {
        typedef void( *FuncPtr)( ArgumentType );
    
    	string					   Id;
        ArgumentType			   Argument;
        FuncPtr                    Function;
    
        CommandNM1( FuncPtr Func, ArgumentType Arg, string Id_c ) :
    		Id(Id_c),
            Function( Func ),
            Argument( Arg )
        {
        }
    
    	virtual void execute(const int& arg)
        {
            Function(arg);
        }
    
    	virtual void execute()
        {
            Function(Argument);
        }
    
    	virtual string getId()
        {
            return Id;
        }
    
    };
    
    template<typename ResultType, typename ArgumentType>
    Command* make_command( ResultType (*Func)( ArgumentType ), ArgumentType Arg, string Id )
    {
        return new CommandNM1<ResultType,ArgumentType>( Func, Arg, Id );
    }
    
    //Implementation für Non-Member Funktionen ohne Rückgabewert und Argument
    struct CommandNM1V : Command
    {
        typedef void( *FuncPtr)( void );
    
    	string					   Id;
        FuncPtr                    Function;
    
        CommandNM1V( FuncPtr Func,  string Id_c ) :
    		Id(Id_c),
            Function( Func )
        {
        }
    
    	virtual void execute(const int& arg)
        {
            Function();
        }
    
    	virtual void execute()
        {
            Function();
        }
    
    	virtual string getId()
        {
            return Id;
        }
    
    };
    
    Command* make_command( void (*Func)( void ), string Id )
    {
        return new CommandNM1V( Func, Id );
    }
    
    // Implementation für member Funktion mit einem Argument und Rückgabewert
    template<typename ObjectType, typename ResultType, typename ArgumentType>
    struct CommandM1 : Command
    {
        typedef ResultType( ObjectType::*FuncPtr)( ArgumentType );
    
    	string					   Id;
        ResultType                 Result;
        ArgumentType	           Argument;
        FuncPtr                    Function;
        ObjectType&                Object;
    
        CommandM1( ObjectType& Obj, FuncPtr Func, ArgumentType Arg, string Id_c ) :
    		Id(Id_c),
            Function( Func ),
            Object( Obj ),
            Argument( Arg ),
            Result( ResultType() )
        {
        }
    
        virtual void execute(const int& arg)
        {
            Result = (Object.*Function)( arg );
        }
    
    	virtual void execute()
        {
            Result = (Object.*Function)(Argument);
        }
    
    	virtual string getId()
        {
            return Id;
        }
    };
    
    // Implementation für member Funktion mit einem Argument und ohne Rückgabewert
    template<typename ObjectType, typename ArgumentType>
    struct CommandM1 <ObjectType ,void ,ArgumentType> : Command
    {
        typedef void( ObjectType::*FuncPtr)( ArgumentType );
    
    	string					   Id;
        ArgumentType               Argument;
        FuncPtr                    Function;
        ObjectType&                Object;
    
        CommandM1( ObjectType& Obj, FuncPtr Func, ArgumentType Arg, string Id_c ) :
    		Id(Id_c),
            Function( Func ),
            Object( Obj ),
            Argument( Arg )
        {
        }
    
        virtual void execute(const int& arg)
        {
            (Object.*Function)( arg );
        }
    
    	virtual void execute()
        {
            (Object.*Function)(Argument);
        }
    
    	virtual string getId()
        {
            return Id;
        }
    };
    
    template<typename ObjectType, typename ResultType, typename ArgumentType>
    Command* make_command( ObjectType& Obj, ResultType (ObjectType::*Func)( ArgumentType ), ArgumentType Arg, string Id )
    {
        return new CommandM1<ObjectType,ResultType,ArgumentType>( Obj, Func, Arg, Id );
    } 
    
    // Implementation für member Funktion mit keinem Argument und ohne Rückgabewert
    template<typename ObjectType>
    struct CommandM1V : Command
    {
        typedef void( ObjectType::*FuncPtr)( void );
    
    	string					   Id;
        FuncPtr                    Function;
        ObjectType&                Object;
    
        CommandM1V( ObjectType& Obj, FuncPtr Func,  string Id_c ) :
    		Id(Id_c),
            Function( Func ),
            Object( Obj )
        {
        }
    
    	virtual void execute(const int& arg)
        {
            (Object.*Function)(  );
        }
    
    	virtual void execute()
        {
            (Object.*Function)();
        }
    
    	virtual string getId()
        {
            return Id;
        }
    };
    
    template<typename ObjectType>
    Command* make_command( ObjectType& Obj, void (ObjectType::*Func)( void ), string Id )
    {
        return new CommandM1V<ObjectType>( Obj, Func,  Id );
    }
    


  • pat.de schrieb:

    per reference wird doch nciht extra eine kopie des Objekts erstellt, ist das nicht leistungsschonender?

    Die Referenz wird intern üblicherweise als Zeiger übergeben, welcher auf gängigen Systemen mindestens so viel Speicher wie int verbraucht. Außerdem kostet die Zeigerindirektion beim Zugriff zusätzlich.



  • pat.de schrieb:

    Jetzt ist mal wieder ein ganz anderes Problem aufgetreten... ich habe gestern auf die klasse fertig geschrieben, meine command objekt erstellt und alles lief. jetzt habe ich das projekt in visual studio 2010 eingefügt(vorher vs2008) und der compiler gibt mir den fehler:

    Fehler 1 error LNK2005: "class Command * __cdecl make_command(void (__cdecl*)(void),class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?make_command@@YAPAVCommand@@P6AXXZV?basic_string@DU?basic\_string@DU?char_traits@D@std@@V?$allocator@D@2@@std@@@Z) ist bereits in command.obj definiert. c:\Users\administrator\documents\visual studio 2010\Projects\server-software\Server-Software\main.obj
    Fehler 2 error LNK1169: Mindestens ein mehrfach definiertes Symbol gefunden. c:\users\administrator\documents\visual studio 2010\Projects\server-software\Debug\Server-Software.exe 1

    aus...wodran könnte das liegen??? ist doch der gleiche quellcode?

    Ist nicht der gleiche Quellcode, entweder nicht an der gleichen Stelle (Header/Source), oder du hattest den header vorher nur einmal eingebunden, oder make_command (die nicht-Template-Funktion) neu hinzugefügt.

    Dein Problem ist eines der üblichen für die Fehlermeldung: Du hast die ODR verletzt, indem du eine Funktion mit externer Linkage in mehreren Übersetzungseinheiten definiert hast.
    Externe Linkage haben alle Funktionen, die
    - nicht static sind
    - nicht in anonymen namespaces liegen
    - nicht inline sind

    In deinem Fall ist letzteres der Knackpunkt: du hast den header mit der Definition einer nicht-inline, nicht-template Funktion in zwei Übersetzungseinheiten eingebunden und damit in beiden ÜEs eine Definition dieser Funktion. Der Linker kann nicht erkennen, dass es die selbe Definition ist und er eine von beiden ignorieren könnte. Die Funktionstemplates sind von der ODR nicht betroffen, da nimmt der Linker einfach die erstbeste Instantiierung und geht stillschweigend davon aus, dass alle gleich sind.



  • Warum hast du in Command , CommandNM1 und Command die Methode execute( const int& arg ) eingefügt? Der Sinn hinter dem Ganzen Konstrukt ist doch, dass Command selbst keine Ahnung davon haben sollte, wie das tatsächliche konkrete Command ausgeführt werden soll und lediglich die execute() Methode aufruft. Wenn du jetzt wieder unterschiedliche Methoden für Aufrufe des konkreten Commands benutzt brauchst du den ganzen Aufwand nicht zu treiben.

    Ausserdem gehört Id in die Basisklasse Command , damit sparst du dir die virtuelle getId Methode und musst nicht in jeder Ableitung das Attribut Id bereitstellen. Ich würde auch keinen Vergleichsoperator für Command/std::string Vergleiche bereitstellen, dafür hast du doch die getId Methode.

    Warum der MSVC2010 meckert kann ich leider nicht sagen, habe hier auch keine Installation, um das zu testen. Aber vielleicht macht es Sinn, bei den make_command Funktionen die fixen Parameter nach vorn zu stellen (also die Id) und die template Parameter nach hinten, nicht dass der Compiler über Template Instantiierungen stolpert.

    template<typename ResultType, typename ArgumentType>
    Command* make_command( const std::string& Id, ResultType (*Func)( ArgumentType ), ArgumentType Arg )
    

    Lustigerweise übergibst du int Argumente, die ziemlich billig zu kopieren sind, als const Reference, aber wirklich teure Objekte ( std::string ) per Value. 😕



  • Vielleicht solltest du das auch auf mehrere Dateien aufteilen, z.B.

    - Command.h/Command.cpp : Implementation der Basisklasse Command
    - Command0.h : Templates und Funktionen für Commands ohne Argument
    - Command1.h : Templates und Funktionen für Commands mit einem Argument

    usw.



  • du hattest den header vorher nur einmal eingebunden

    aus irgendeinem grund hat vs2008 die source-datei(command.ccp), die command.h inkludiert, nicht kompiliert und deswegen auch keinen fehler angezeigt(weil

    du hast den header mit der Definition einer nicht-inline, nicht-template Funktion in zwei Übersetzungseinheiten eingebunden und damit in beiden ÜEs eine Definition dieser Funktion.

    )

    VS2010 hat diese datei(command.cpp) kompiliert und erkannt dass, ich eine nicht-template Funktion in zwei Übersetzungseinheiten eingebunden habe und hat daraufhin einen fehler ausgegeben. fazit: template und nicht template funktionen in der header deklariert und in der source-datei definiert... jetzt klappts



  • pat.de schrieb:

    fazit: template und nicht template funktionen in der header deklariert und in der source-datei definiert... jetzt klappts

    Nur solange, bis du die template-Funktionen außerhalb der source benutzt, in der sie definiert werden. Von templates müssen nämlich bei der Instantiierung immer die kompletten Definitionen vorliegen.



  • Ausserdem gehört Id in die Basisklasse Command, damit sparst du dir die virtuelle getId Methode und musst nicht in jeder Ableitung das Attribut Id bereitstellen. Ich würde auch keinen Vergleichsoperator für Command/std::string Vergleiche bereitstellen, dafür hast du doch die getId Methode.

    habe jetzt getid entfernt und nur den == operator dringelassen, weil ich ja strings miteinander vergleichen will und dann bei gleichheit, soll das kommando(die funktion) ausgeführt werden.

    Warum hast du in Command, CommandNM1 und Command die Methode execute( const int& arg ) eingefügt? Der Sinn hinter dem Ganzen Konstrukt ist doch, dass Command selbst keine Ahnung davon haben sollte, wie das tatsächliche konkrete Command ausgeführt werden soll und lediglich die execute() Methode aufruft. Wenn du jetzt wieder unterschiedliche Methoden für Aufrufe des konkreten Commands benutzt brauchst du den ganzen Aufwand nicht zu treiben.

    ich hab im moment noch keine ahnung, wie ich über den basisklassenzeiger der kommandos auf die argumente zugreifen kann. die basisklasse weiss ja nicht, mit welchem typ das argument deklariert ist.

    dann hat sich grad noch folgende frage ergeben: In manchen Templates(abgeleitete Klasse) kann ich den Member Id der basisklasse über die initialisierungsliste ohne Probleme initialisieren und in anderen muss ich den standartkonstruktor aufrufen....warum?

    // Abstrakte Basisklasse für alle möglichen Commands
    class Command
    {
    public:
    
    	Command(const string& Id_c): Id(Id_c){}
    
        virtual ~Command()
        {
        }
    
    	void operator()()
        {
            execute();
        }
    
    	bool operator==(const string& sId)
        {
    		if(sId.compare(Id))
    			return false;
    		else
    			return true;
        }
    
    private:
    
    	string Id;
    	virtual void execute() = 0;
    
    };
    
    // Implementation für non-member Funktion mit einem Argument und Rückgabewert
    template<typename ResultType, typename ArgumentType>
    struct CommandNM1 : Command
    {
        typedef ResultType( *FuncPtr)( ArgumentType );
    
        ResultType                 Result;
        ArgumentType			   Argument;
        FuncPtr                    Function;
    
        CommandNM1(const string& Id_c, FuncPtr Func, ArgumentType Arg  ) :
    		Id(Id_c),				//WIESO IST DAS ELEMENT HIER DIREKT INITIALISIERBAR?
            Function( Func ),
            Argument( Arg ),
            Result( ResultType() )
        {
        }
    
    	virtual void execute()
        {
            Result = Function(Argument);
        }
    
    };
    
    // Implementation für member Funktion mit einem Argument und ohne Rückgabewert
    template<typename ObjectType, typename ArgumentType>
    struct CommandM1 <ObjectType ,void ,ArgumentType> : Command
    {
        typedef void( ObjectType::*FuncPtr)( ArgumentType );
    
        ArgumentType               Argument;
        FuncPtr                    Function;
        ObjectType&                Object;
    
        CommandM1(const string& Id_c, ObjectType& Obj, FuncPtr Func, ArgumentType Arg ) :
    		Command(Id_c),//WIESO HIER NUR ÜBER KONSTRUKTOR?
    		Object( Obj ),
            Function( Func ),
            Argument( Arg )
    
        {
        }
    
    	virtual void execute()
        {
            (Object.*Function)(Argument);
        }
    
    };
    


  • Weil das Template des ersten Beispiels nie instantiiert wird und der Code daher nie übersetzt wird.

    Warum willst du auf die Argumente der Commands zugreifen? Alles, was ein Commandobjekt zur Ausführung braucht, besitzt es selbst (guck dir mal die Definition von CommandNM1::execute() an).



  • die argumente sollen doch dynamisch sein... zur laufzeit sollen sich die werte des arguments ändern.



  • Die Command Objekte werden erzeugt, benutzt und anschließend zerstört. Wenn du also 2x eine bestimmte Funktion aufrufen möchtest erzeugst du zwei Command Objekte vom gleichen Typ, statt nur eins zu erzeugen und das wiederzuverwenden.



  • ich hatte mir das so vorgestellt, dass ich zu beginn meine commando-objekte erzeuge

    void cmd_init(void)
    {
    	Command* set_startaddr= make_command("set_startaddr",flash_cmds, &Flash_cmds::setStartaddr,0  );
    	Command* set_endaddr= make_command("set_endaddr",flash_cmds,&Flash_cmds::setEndaddr, 0 );
    	//Command* set_debug= make_command(flash_cmds,&Flash_cmds::set, 1,"set_debug");
    	//Command* set_lock= make_command(flash_cmds,&Flash_cmds::,0 ,"set_lock");
    	Command* set_progcontent= make_command("set_progcontent",flash_cmds,&Flash_cmds::setProgcontent,0 );
    	Command* set_flashmode= make_command("set_flashmode",flash_cmds,&Flash_cmds::setFlashmode,0 );
    
    	Command* start_read = make_command("start_read",flash_cmds,&Flash_cmds::start_read );
    	Command* start_program = make_command("start_program",flash_cmds,&Flash_cmds::start_program );
    	Command* start_masserase = make_command("start_masserase",flash_cmds,&Flash_cmds::start_masserase );
    	Command* start_verfiy = make_command("start_verify",flash_cmds,&Flash_cmds::start_verify );
    	Command* start_compare = make_command("start_compare",flash_cmds,&Flash_cmds::start_compare );
    
    }
    

    anschließend werden in einem empfangspuffer die empfangenen strings gespeichert. der erste string wird aus dem puffer geladen und überprüft, ob es sich um ein befehl oder argument handelt. falls befehl, dann prüfen ob befehl argument benötigt und nächsten string aus puffer holen und prüfen ob es wirklich ein argument ist. falls es zutrifft wird befehl+ evtl argument ausgeführt.

    die befehlsüberprüfung soll mit dem vergleichsoperator durchgeführt werden
    bspw.

    if(empf.vec==set_startaddr)
    {
    set_startaddr.arg(++empf.vec);
    set_startaddr();
    }



  • Dann brauchst du den ganzen Firlefanz hier überhaupt nicht. Wenn du beim Eingang jedes Telegramms das Telegramm parst, um den Typ der aufgerufenen Funktion zu bestimmen, und dann die Funktion über einen Funktionspointer aufzurufen, dann kannst du die Funktion auch direkt aufrufen. Ich verstehe zwar, was du technisch vorhast, aber irgendwie verstehe ich die Absicht dahinter nicht.

    pat.de schrieb:

    if(empf.vec==set_startaddr)
    {
    set_startaddr.arg(++empf.vec);
    set_startaddr();
    }

    Warum nicht sofort

    if( empf.vec == set_startaddr )
    {
       FlashDingens.set_startaddr( SomeArg );
    }
    

    Wofür Commands und Funktionszeiger?



  • das hatte ich ja schon auch schon vorher... aber dann wollte ich das noch mit den funktionszeigern ausprobieren. zur übung ist ja sowas nicht schlecht, falls man später mal vor so einem problem steht und dann nicht weiter weiss.



  • DocShoe schrieb:

    Die Behandlung von Rückgabewerten oder Aufrufparametern muss irgendwie durch die entsprechenden abgeleiteten Klassen durchgeführt werden, deshalb haben hat CommandNM1 die Elemente Result und Argument .

    ich weiss noch nicht so genau, wie ich jetzt an den result-member rankomme. klar könnte ich jetzt für jedes objekt

    dynamic_cast
    

    anwenden, aber ist das die einzige lösung???

    ...komme leider nicht weiter und wäre über eure hilfe sehr dankbar


Anmelden zum Antworten