[gelöst] Variablen durch Makro oder Preprozessor erzeugen



  • Und was hält dich nun davon ab, es so zu machen?

    #include <iostream>
    
    class IA
    {
    public:
        virtual void start() = 0;
    };
    
    template <int i> class B : public IA
    {
    
    private:
    
        static B* m_This;
    
    public:
    
        B()
        {
            m_This = this;
        }
    
        virtual void start()
        {
            std::cout << "start at instance: " << m_This << std::endl;
        }
    
        static void callback()
        {
            std::cout << "callback " << &callback << " at instance: " << m_This << std::endl;
            return;
        }
    };
    
    B<0>* B<0>::m_This;
    B<1>* B<1>::m_This;
    B<2>* B<2>::m_This;
    B<3>* B<3>::m_This;
    
    int main()
    {
        IA* k[4];
    
        k[0] = new B<0>();
        k[1] = new B<1>();
        k[2] = new B<2>();
        k[3] = new B<3>();
    
        for( int x=0; x<4; x++)
            k[x]->start();
    
        // diese Aufrufe würden vom Treiber des Herstellers kommen
        B<0>::callback();
        B<1>::callback();
        B<2>::callback();
        B<3>::callback();
    
        return 0;
    }
    

    Dein Code compiliert übrigens nur mit dem Microsoft-Compiler, mit G++ beispielswise nicht.



  • Was mich davon abhält ? Gar nichts, ich suche nur noch die Lösung über N 😉

    Rein aus Interesse, was meckert denn der G++ an ?



  • Was g++ sagt, kannst du dir zb auf ideone.com angucken: http://ideone.com/dr9Wg5
    codepad.org mag deinen code auch nicht: http://codepad.org/elrfRQ4O
    Welcher Compiler dahinter steckt, weiß ich aber nicht. Es ist aber generell gut, sowas immer mal zu testen, damit der code auch standardkonform und portabel bleibt/wird.

    Ich meinte auch eher, warum du unbedingt diese Dinger mit const int a0, a1 usw. haben willst statt einfach direkt Literale zu verwenden.

    Dabei, das automatisch bis n zu haben, kann ich dir leider nicht helfen. Nexus' Lösung reicht ja auch nicht. Mal schaun, was noch so kommt hier. 🙂
    Komisch, dass das nötig ist, finde ich es trotzdem. 😉



  • Guter Tip, vielen Dank. Auch wenn g++ für mich kein Thema ist hat es mich schon interessiert und hier ist nun eine funktionierende Version. Schon erstaunlich was VC++ so alles durchgehen lässt. Macht man sich wenig Gedanken, wenn man mit anderen Compilern so gut wie nie in Berührung kommt.

    #include <iostream>
    
    const int a0 = 0;
    const int a1 = 1;
    const int a2 = 2;
    const int a3 = 3;
    
    class IA
    {
    public:
    	virtual void start() = 0;
    };
    
    template <int i> class B : public IA
    {
    private:
    	static B* m_This;
    public:
    	B();
    	virtual void start();
    	static void callback();
    };
    
    template<int i> B<i>::B()
    {
    	m_This = this;
    }
    
    template <int i> void B<i>::start()
    {
    	std::cout << "start at instance: " << m_This << std::endl;
    }
    
    template <int i> void B<i>::callback()
    {
    	std::cout << "callback " << &callback << " at instance: " << m_This << std::endl; 
    	return;
    }
    
    template<int i> B<i>* B<i>::m_This;
    
    int main()
    {
    	IA* k[4];
    
    	k[0] = new B<a0>();
    	k[1] = new B<a1>();
    	k[2] = new B<a2>();
    	k[3] = new B<a3>();
    
    	for( int x=0; x<4; x++)
    		k[x]->start();
    
    	// diese Aufrufe würden vom Treiber des Herstellers kommen
    	B<a0>::callback();
    	B<a1>::callback();
    	B<a2>::callback();
    	B<a3>::callback();
    
    	return 0;
    }
    

    Danke an alle erstmal an dieser Stelle für Eure Mühe !!!



  • Du machst so einiges komisch. Die Definition der statischen Variablen solltest du ein Mal schreiben, und nicht für jede Instanziierung. Die a -Variablen brauchst du nach wie vor nicht. Und natürlich musst du Destruktoren virtuell machen und Speicher wieder freigeben, aber ich hoffe, das tust du in deinem richtigen Projekt schon.

    Du kannst meinen Ansatz mit Rekursion auch auf ein Funktionstemplate anwenden. Unter Verwendung von C++11 ( std::array und std::unique_ptr ) kann ein sauberer Ansatz so aussehen:

    #include <array>
    #include <memory>
    #include <iostream>
    
    // int-to-type Idiom für Überladung
    template <unsigned int I>
    struct Int {};
    
    struct Base
    {
    	typedef std::unique_ptr<Base> Ptr;
    
    	virtual void exec() = 0;
    	virtual ~Base() {}
    };
    
    template <unsigned int I>
    struct Derived : Base
    {
    	virtual void exec() { std::cout << "Derived<" << I << ">\n"; }
    };
    
    // Template-rekursive Funktion
    template <unsigned int N, unsigned int I>
    void fillElements(std::array<Base::Ptr, N>& array, Int<I>)
    {
    	array[I].reset(new Derived<I>);
    	fillElements(array, Int<I+1>());
    }
    
    // Abbruchbedingung für Rekursion
    template <unsigned int N>
    void fillElements(std::array<Base::Ptr, N>&, Int<N>)
    {
    }
    

    Anwendung:

    int main()
    {
    	std::array<Base::Ptr, 10> elements; 
    	fillElements(elements, Int<0>());
    	// fertig!
    
    	for (auto itr = elements.begin(); itr != elements.end(); ++itr)
    		(*itr)->exec();
    }
    

    Wie du siehst, muss die Grösse nur ein einziges Mal angegeben werden. Du hast kein Copy&Paste, keine Schleife, kein Boilerplate-Code.



  • Nexus schrieb:

    Du machst so einiges komisch. Die Definition der statischen Variablen solltest du ein Mal schreiben, und nicht für jede Instanziierung. Die a -Variablen brauchst du nach wie vor nicht. Und natürlich musst du Destruktoren virtuell machen und Speicher wieder freigeben, aber ich hoffe, das tust du in deinem richtigen Projekt schon.

    Gebe ich Dir Recht in allen Punkten, und natürlich beschränken sich diese Unzulänglichkeiten nur auf mein kleines "Bastelbeispiel" für meinen Beitrag.

    Kann hier ja schlecht ein paar 1000 Zeilen Code posten 😉



  • Wichtiger als diese Anmerkung in meinem Post ist allerdings der Code 😉



  • Nexus schrieb:

    Wichtiger als diese Anmerkung in meinem Post ist allerdings der Code 😉

    Ich weiss, und DEN versuche ich gerade zu verstehen. Das braucht noch ein wenig 🙂



  • Ich glaube ich habe es verstanden, zumindest weiss ich schon mal WAS der Code macht, aber an manchen Stellen bin ich mir noch nicht so sicher WARUM er es macht.

    Aber zumindest habe ich meinen Beispiel Code einmal angepasst und ja, es funktioniert. 1000 Dank dafür !!!

    Hiern noch für Interessierte das modifizierte Beispiel:

    #include <array>
    #include <iostream>
    #include <memory>
    
    // ------------------------- generic interface
    
    class IA
    {
    public:
    	virtual void start() = 0;
    };
    
    // ------------------------- driver implementation
    
    template <int i> class B : public IA
    {
    private:
    	static B* m_This;
    public:
    	B();
    	virtual void start();
    	static void callback();
    };
    
    template<int i> B<i>::B()
    {
    	m_This = this;
    }
    
    template <int i> void B<i>::start()
    {
    	std::cout << "start at instance: " << m_This << std::endl;
    }
    
    template <int i> void B<i>::callback()
    {
    	std::cout << "callback " << &callback << " at instance: " << m_This << std::endl; 
    	return;
    }
    
    template<int i> B<i>* B<i>::m_This;
    
    // ------------------------- creation template
    
    template <unsigned int I> class Int {};
    
    template <unsigned int N, unsigned int I> void create( std::array< std::shared_ptr<IA>, N>& array, Int<I>)
    {
    	array[I].reset( new B<I>);
        create( array, Int<I+1>());
    }
    
    template <unsigned int N> void create(std::array< std::shared_ptr<IA>, N>&, Int<N>)
    {
    	return;
    }
    
    // ------------------------- test purpose only
    
    template <unsigned int N, unsigned int I> void callback( std::array< std::shared_ptr<IA>, N>& array, Int<I>)
    {
    	(static_cast<B<I>*>(array[I].get()))->callback();
        callback( array, Int<I+1>());
    }
    
    template <unsigned int N> void callback(std::array< std::shared_ptr<IA>, N>&, Int<N>)
    {
    	return;
    }
    
    // ------------------------- test it all
    
    int main()
    {	
    	const unsigned int NUM = 25;	
    
    	std::array< std::shared_ptr<IA>, NUM> l_IA;
    
    	create( l_IA, Int<0>());	
    
    	for (auto it = l_IA.begin(); it != l_IA.end(); ++it)
    		(*it)->start();	
    
    	callback( l_IA, Int<0>());
    
    	return 0;
    }
    

    Mir ist noch nicht ganz klar, warum man den Int() Wrapper (?) braucht und warum man als Parameternamen "array" verwenden kann / darf. Ich habe es einmal mit einem willkürlichen Namen probiert, was auch funktioniert. Gibt es da einen Unterschied ?



  • Int brauchst du, damit du Funktionstemplates überladen kannst. Sonst müsstest du spezialisieren, was nicht partiell geht und auch sonst ein paar Überraschungen auf Lager hat. Ich vermeide Spezialisierung von Funktionstemplates grundsätzlich. Siehe auch More C++ Idioms.

    array ist ein Bezeichner wie jeder andere, Visual Studio macht nur Syntax Highlighting, weil es ein Schlüsselwort in einer Spracherweiterung (C++/CLI) ist. Ist etwas nervig, ich weiss.

    Warum nimmst du shared_ptr und nicht unique_ptr ? Damit hast du recht viel unnötigen Overhead...



  • Ähm, ja, Synthax Highlighting, wie peinlich, ich sollte mal Pause machen 🙂

    Deine Erklärung ist sehr gut. Mir war gar nicht so richtig klar das jeder Aufruf von fillElements() ein überladener Aufruf ist.

    Die Sache mit den shared_ptr ist historisch begründet. Die ganze Bibliothek an der ich schreibe benutzt für das generische Interface shared_ptr. Daran lässt sich auch wohl nichts mehr ändern.


Anmelden zum Antworten