this* pointer: Selbst bestimmen, als welches objekt eine funktion aufgerufen wird... ?



  • Tachyon schrieb:

    lk schrieb:

    [...]Und virtuelle funktionen will ich auch nicht, da die etwas langsamer sind....

    Langsamer als was? Als ein ähnlicher Mechanismus von Dir selbst gebastelt? Wohl kaum. Für Plugins braucht man wohldefinierte Interfaces. Und dafür bieten sich unter C++ Klassen mit rein virtuellen Funktionen an.

    Ich will ja nicht direkt Plugins machen.

    Virtuelle funktionen sind einen ganz kleinen tacken langsamer als normale nicht-virtuelle funktionen. Ich werde auch viele funktionen in den plugins aufrufen, sehr oft...



  • lk, du weichst Tachyons Frage aus.

    Wenn du eine Fallunterscheidung brauchst, kannst du nicht auf virtuelle Funktionen verzichten, aber gleichzeitig keine Alternative ( if , switch , etc.) anbieten.



  • Der Aufruf einer virtueller Funktion kostet etwas ja. Allerdings bekommst du dafür ja auch etwas, dass du in einer "normalen" Funktion nachbilden musst (irgendeine Art von verzweigung). Normalerweise braucht man sich da keine Sorgen machen.
    Deine Aufgabe habe ich allerdings eher in diese Richtung verstanden:

    #include <map>
    #include <iterator>
    #include <functional>
    #include <stdexcept>
    
    class M
    {
    public:
        typedef std::map<std::string, std::function<void(void)> > MethodMap;
        typedef std::insert_iterator<MethodMap> Inserter;
        typedef MethodMap::const_iterator Iterator;
    
        virtual ~M(){};
    
        void init()
        {
            getfuncs(Inserter(methods_, methods_.begin()));
        }
    
        void Call(const std::string& method)
        {
            Iterator it = methods_.find(method);
            if (it == LastMethod())
                throw std::runtime_error("Unknown method name.");
            it->second();
        }
    
        Iterator FirstMethod() const
        {
            return methods_.begin();
        }
    
        Iterator LastMethod() const
        {
            return methods_.end();
        }
    
    private:
        virtual void getfuncs(Inserter i) = 0;
        MethodMap methods_;
    };
    
    // irgendwo
    class init : public M
    {
    public:
        void foo() { std::cout << "fooo\n"; }
        void bar() { std::cout << "baar\n"; }
    private:
        void getfuncs(Inserter i)
        {
            i = std::make_pair("foo", std::bind(&init::foo, this));
            ++i = std::make_pair("bar", std::bind(&init::bar, this));
        }
    };
    
    int main() 
    {
        M* m = new init; 
        m->init();
        std::cout << "available methods:\n";
        for(M::Iterator i = m->FirstMethod(), e = m->LastMethod();
            i != e; ++i)
        {
            std::cout << i->first << "\n";
        }
    
        std::cout << "call:";
        std::string method;
        std::cin >> method;
        try
        {
            m->Call(method);
        }
        catch(std::exception& e)
        {
            std::cout << e.what();
        }
    
        delete m;
        return 0;
    }
    

    edit: Jetzt bin ich mir doch nicht mehr sicher was du meinst. Wenn es dir immer nur um die get und set geht, dann ist das tatsächlich ganz einfach mit virtuellen Methoden am effektivsten.



  • Hi.
    Ich versuche das geschehen nachzuverfolgen. Ich komme hierbei nicht weiter:

    Fehlermeldung
    Wenn ich list->push_back(..) entferne, compiliert es.

    class BASE
    {
    public:
    	typedef pair<string, function<int(int)> > FEntry;
    	typedef vector<FEntry> FList;
    
    	virtual FList* getFuncs() = 0;
    };
    
    class MOD : public BASE
    {
    public:
    	virtual FList* getFuncs()
    	{
    		FList* list = new FList();
    
    		/*
    		 *   i = std::make_pair("foo", std::bind(&init::foo, this));
    		 * ++i = std::make_pair("bar", std::bind(&init::bar, this));
    		 * */
    
            // HIER
    		list->push_back( (FEntry)make_pair( "mow", bind(&MOD::mow, this) ));
    
    		//ENTRY("Sub", sub); // mittels define
    
    		return list;
    	}
    
    	int mow(int in)
    	{
    		return in * 2;
    	}
    
    	int sub(int in)
    	{
    		return in - 100;
    	}
    };
    
    int main()
    {
        // noch leer
    	return 0;
    }
    


  • Du kannst this nicht an MOD::mow binden. Der Typ des zweiten Arguments von bind muss schon mit dem übereinstimmen, den MOD::mow erwartet, also int. Außerdem sollte man die Referenz (glaube ich) besser durch boost::ref ersetzen.

    Hast du schonmal überlegt auf Funktoren umzusteigen, anstatt Funktionen zurückzugeben?



  • lk schrieb:

    Virtuelle funktionen sind einen ganz kleinen tacken langsamer als normale nicht-virtuelle funktionen. Ich werde auch viele funktionen in den plugins aufrufen, sehr oft...

    Du willst also dynamic binding ohne eine zusätzliche Indirektion? Sry aber das ist ganz einfach nicht möglich.



  • > Du kannst this nicht an MOD::mow binden. Der Typ des zweiten Arguments von bind muss schon mit dem übereinstimmen, den MOD::mow erwartet, also int. Außerdem sollte man die Referenz (glaube ich) besser durch boost::ref ersetzen.  
    >   
    > Hast du schonmal überlegt auf Funktoren umzusteigen, anstatt Funktionen zurückzugeben?
    
    Ich kenn mich mit Boost nicht so gut aus. Beispielcode wäre nett...  
    
    Sollte ich dann `boost::bind(&MOD::mow, _1, this)` aufrufen? Für zwei Parameter dann `_1, _2` ?  
    
    Im Detail, was macht der Code genau ?


  • lk schrieb:

    Sollte ich dann boost::bind(&MOD::mow, _1, this) aufrufen? Für zwei Parameter dann _1, _2 ?

    Nein, boost::bind(&MOD::mow, this, _1 ).



  • lk schrieb:

    Tachyon schrieb:

    lk schrieb:

    [...]Und virtuelle funktionen will ich auch nicht, da die etwas langsamer sind....

    Langsamer als was? Als ein ähnlicher Mechanismus von Dir selbst gebastelt? Wohl kaum. Für Plugins braucht man wohldefinierte Interfaces. Und dafür bieten sich unter C++ Klassen mit rein virtuellen Funktionen an.

    Ich will ja nicht direkt Plugins machen.

    Virtuelle funktionen sind einen ganz kleinen tacken langsamer als normale nicht-virtuelle funktionen. Ich werde auch viele funktionen in den plugins aufrufen, sehr oft...

    so what... Lies Dich mal in das Thema: "Premature Optimizing" ein, denn genau das machst Du gerade. Du versuchst ein etabiliertes, optimiertes System (virtuelle Funktion) durch etwas ähnliches zu ersetzen was unterm Strich eher langsamer als schneller sein wird. Abgesehen davon, selbst wenn der Aufruf einer virtuellen Funktion langsamer ist, so macht das vielleicht 0.000001% Deiner Rechnerperformance aus. Optimier lieber erstmal an anderen Stellen die auch eine meßbare Auswirkung auf die Performance haben.



  • Dynamic Binding geht eben über eine Indirektion. Wenn bei dem was du vorhast der Overhead dieser zusätzlichen Indirektion zu groß ist dann kannst du schlicht und einfach kein dynamic Binding verwenden, denn dieser Overhead ist rein prinzipieller Natur und hat nichts mit irgendeiner Ineffizienz in der Implementierung von virtuellen Funktionen zu tun. Was auch immer du bastelst wirst du diesen Overhead nicht loswerden. Wenn du dir ein Ersatzsystem für virtuelle Funktionen baust dann wird dieses System immer noch eine Indirektionsschicht brauchen. Der einzige Unterschied ist am Ende nur dass du beim selbst gebastelten System deine Funktionspointer selbst verwaltest und bei virtuellen Funktionen der Compiler das für dich übernimmt. Der Overhead liegt aber in der Natur der Sache und ist daher in deinem System genauso drin, auch wenn du das per Hand in Assembler schreibst ändert sich da nix dran.

    Die Aussage dass virtuelle Funktionen langsam sind ist jedenfalls vollkommen Sinnlos.



  • loks schrieb:

    so what... Lies Dich mal in das Thema: "Premature Optimizing" ein, denn genau das machst Du gerade. Du versuchst ein etabiliertes, optimiertes System (virtuelle Funktion) durch etwas ähnliches zu ersetzen was unterm Strich eher langsamer als schneller sein wird. Abgesehen davon, selbst wenn der Aufruf einer virtuellen Funktion langsamer ist, so macht das vielleicht 0.000001% Deiner Rechnerperformance aus. Optimier lieber erstmal an anderen Stellen die auch eine meßbare Auswirkung auf die Performance haben.

    Ein anderes Problem wäre, dass ich dann 2 zusätzliche klassen für jede klasse machen müsste. Übrigens hab ichs gerade hingekriegt...

    Ein schwacher Punkt wird wohl Baz::assign(..) sein... 😞

    Mein Code jetzt:
    (in die main() funktion gucken 😃 )

    class Foo
    {
    public:
        typedef function<int(int)> Func;
        typedef pair<string, Func > FEntry;
        typedef vector<FEntry> FList;
    
        //typedef int(Foo::* F)(int); // one static function
        //typedef vector< F > FL; // list of all of them
    
        virtual FList* getFuncs() = 0;
    };
    
    class Bar : public Foo
    {
    private:
        int data;
    
    public:
        virtual FList* getFuncs()
        {
            FList* list = new FList();
    
            #define ENTRY(x,y) list->push_back(std::make_pair( string( x ) , boost::bind( &Bar:: y , this, _1 ) ));
    
            list->push_back( std::make_pair(string("mow"), boost::bind(&Bar::mow, this, _1) ) );
            list->push_back( std::make_pair(string("sub"), boost::bind(&Bar::sub, this, _1) ) );
    
            // hehehe :)
            ENTRY("set", set);
            ENTRY("get", get);
    
            /*
             *   i = std::make_pair("foo", std::bind(&init::foo, this));
             * ++i = std::make_pair("bar", std::bind(&init::bar, this));
             * */
    
            // list->push_back( (FEntry)make_pair( "mow", boost::bind(&Bar::mow, 435, this) ));
    
            //ENTRY("Sub", sub);
    
            return list;
        }
    
        int set(int val)
        {
            data = val;
            return val;
        }
    
        int get (int dummy)
        {
            return data;
        }
    
        int mow(int in)
        {
            return in * 2;
        }
    
        int sub(int in)
        {
            return in - 100;
        }
    };
    
    struct Baz
    {
        typedef Foo::Func f;
    
        int assign(Foo* foo)
        {
            Foo::FList *list = foo->getFuncs();
            Foo::FEntry cur;
    
            //cout << "Baz::assign] list = " << list << endl;
    
            for (int i=0; i<list->size(); i++)
            {
                // switch mit strings o.O ?
    
                cur = list->at(i);
    
                //cout << "Baz::assign - for] cur.1=\"" << cur.first << " &cur.2=" << &cur.second << endl;
    
                if (!cur.first.compare("mow")) {
                    mow = cur.second;
                    continue;
                } else if (!cur.first.compare("sub")) {
                    sub = cur.second;
                    continue;
                } else if (!cur.first.compare("set")) {
                    set = cur.second;
                    continue;
                } else if (!cur.first.compare("get")) {
                    get = cur.second;
                    continue;
                }
                cout << "Baz::assign - for] Pointless function: " << cur.first << endl; // keine Übereinstimmung gefunden
            }
            return 0;
        }
    
        f mow;
        f sub;
        f set;
        f get;
    };
    
    int main()
    {
        Foo *ptr = new Bar(); // hier würde eigentlich eine funktion aus der Dynamischen bibliothek
        // aufgerufen werden: make();
    
        /*
        Foo::FList *flist = ptr->getFuncs();
        Foo::FEntry e_mow = flist->at(1);
        Foo::Func mow = e_mow.second;
    
        Foo::Func set = flist->at(2).second;
        Foo::Func get = flist->at(3).second;
    
        cout << set(352) << endl;
        cout << get(0) << endl;
        */
    
        Baz *bar = new Baz(); // "pseudo klasse"
    
        bar->assign(ptr);
    
        bar->set(372);
        cout << bar->get(0) << endl;
    
        return 0;
    }
    

Anmelden zum Antworten