2 verschiedene Rückgabetypen per Template



  • Nathan schrieb:

    heyo schrieb:

    SeppJ schrieb:

    Das lässt sich nicht beantworten, weil in der abstrakten Beschreibung mittels "Base", "Derived", usw. nicht zu sagen ist, wo der Fehler in der Modellierung liegt. Es ist eben nur zu sehen, dass es falsch ist, über einen Base-Zeiger einen exklusiven Member von Dev1 ansprechen zu wollen. Denn woher weißt du, ob der Zeiger auf ein Dev1 zeigt?

    Achso...

    Du könntest uns genauere Details geben, dann können wir dir bei der Modellierung helfen.

    boost::variant ist soviel ich herausgefunden habe nicht dazu in der lage verschiedene Klassen/Typen zurückzugeben.

    Doch. variant<char, int, float> kann entweder einen char, einen int oder einen float halten. variant<c1, c2> also entweder einen c1 oder einen c2.

    Welche Details würdet ihr benötigen?

    boost::variant wird jedoch nicht so funktionieren wie ich es gerne hätte:

    boost::variant<Dev1, Dev2> dat;
    Dev1 track;
    Dev2 playlist;
    
    dat = track;
    dat = playlist;
    
    template<class T> 
    T getThat(void) {
    	return boost::get<T>(dat);
    }
    
    //Impossible
    /*
    T getThat(void) {
    	if(foo)
    		return Dev1;
    	else
    		return Dev2;
    }
    */
    


  • Spricht was gegen

    template <typename T> T getThat() { static_assert(false, "Not implemented!"); }
    template <> int getThat() { return 1; }
    template <> double getThat() { return 2.5; }
    
    int main() {
    
    	auto varInt = getThat<int>(); // Geht
    	auto varDbl = getThat<double>(); // Geht auch
    	auto varChr = getThat<char>(); // Fehler, not implemented
    }
    

    ?



  • heyo schrieb:

    Welche Details würdet ihr benötigen?

    Was Dev1 und Dev2 repräsentieren.

    boost::variant wird jedoch nicht so funktionieren wie ich es gerne hätte:

    Für sowas ist apply_visitor glaube ich gedacht.



  • Ja, denn es würde dann nur wieder auf dasselbe hinauslaufen:

    if(foo)
    	auto var = getThat<Dev1>;
    else 
    	auto var = getThat<Dev2>;
    
    var; //Unsichtbar
    
    //--------
    
    auto var; //Nicht möglich
    if(foo)
    	...
    


  • Nathan schrieb:

    heyo schrieb:

    Welche Details würdet ihr benötigen?

    Was Dev1 und Dev2 repräsentieren.

    boost::variant wird jedoch nicht so funktionieren wie ich es gerne hätte:

    Für sowas ist apply_visitor glaube ich gedacht.

    Base repräsentiert die Basisfunktionen/Variablen, die dann Track (Dev1) und Playlist (Dev2) erben.
    Track und Playlist haben jedoch noch unabhängige Funktionen, welche nur in den einzelnen Klassen vorhanden sind.
    Dann gibt es noch eine Klasse, welche eine Track und eine Playlist besitzt, sowie einen indikator, ob es jetzt ein Track oder eine Playlist ist.
    Nun wäre es ja geschickt wenn der Endbenutzer der Bibliothek nicht immer beide Anweisungen behandeln muss, obwohl er nur eine benötigt.

    apply_visitor ist jedoch nicht in der Lage (sowie keine andere Funktion) dynamisch andere Typen zurückzugeben.


  • Mod

    Was ist denn die Gemeinsamkeit von Tracks und Playlists? Dass man sie abspielen kann? Das klingt irgendwie nach Wurstbrot erbt von Supermarkt, weil beide was mit "kaufen" zu tun haben. Klingt eher so, als bestünde eine Playlist aus Tracks.



  • SeppJ schrieb:

    Klingt eher so, als bestünde eine Playlist aus Tracks.

    Genau das habe ich auch gedacht. In dem Fall bietet sich doch an den Track als Playlist mit nur einem Track (sich selbst) zurück zugeben.



  • Dann würde sich das Composite-pattern anbieten.



  • Ja eine Playlist enthält auch einen std::vector<Playlist> , jedoch enthält die Klasse Playlist eigene Funktionen, wie zB. die anzahl der Tracks usw. Tracks können jedoch auch alleine sein, heißt also dass Tracks zwar in einer Playlist vorhanden sind, jedoch auch alleine "stehen".


  • Mod

    heyo schrieb:

    Ja eine Playlist enthält auch einen std::vector<Playlist> , jedoch enthält die Klasse Playlist eigene Funktionen, wie zB. die anzahl der Tracks usw. Tracks können jedoch auch alleine sein, heißt also dass Tracks zwar in einer Playlist vorhanden sind, jedoch auch alleine "stehen".

    Und wo kommt in dieser Beschreibung eine gemeinsame Basis von Track und Playlist vor?



  • @heyo
    D.h. du willst Playlisten aus Tracks und weiteren "Unterplaylisten" zusammensetzen können?
    Also wie Verzeichnisse Files und weitere Unterverzeichnisse enthalten können?

    In dem Fall könnte man z.B. machen...
    Variante 1:

    class PlaylistItem
        abstrakt
    
    class Playlist : PlaylistItem
        ptr_vector<PlaylistItem> items;
    
    class Track : PlaylistItem
        ...
    

    Analogie zu nem File-System wäre:
    PlaylistItem entspricht FileSystemEntity
    Playlist entspricht Directory
    Track entspricht File

    Variante 2 (IMO sauberer, da der Track so nix von dem Playlisten-Gedöns wissen muss):

    class Playlist
        abstrakt
    
    class SingleTrackPlaylist : Playlist
        Track track;
    
    class CompositePlaylist : Playlist
        ptr_vector<Playlist> items;
    

    Analogie zu nem File-System wäre:
    Playlist entspricht FileSystemEntity
    SingleTrackPlaylist entspricht File
    CompositePlaylist entspricht Directory
    Track entspricht dem Inhalt des Files (BLOB)



  • hustbaer schrieb:

    class Playlist
        abstrakt
    
    class SingleTrackPlaylist : Playlist
        Track track;
    
    class CompositePlaylist : Playlist
        ptr_vector<Playlist> items;
    

    Und zum Traversieren dann das Visitor-Pattern benutzen. (Kein dynamic_cast!)



  • Weil Track und Plalist zB beide ein Artwork, Länge, Titel usw. haben.



  • Ups, sorry hab die neuen Beiträge nicht gesehen.
    Werde heute Abend noch Antworten. 🙂


  • Mod

    heyo schrieb:

    Weil Track und Plalist zB beide ein Artwork, Länge, Titel usw. haben.

    Dann haben sie eine gemeinsame Basis, aber das heißt noch lange nicht, dass diese Objekte auch gemeinsam über Basisklassenzeiger verwaltet werden müssen. Es sei denn, deine Playlists haben Eigenschaften, wie von hustbaer beschrieben, dann kannst du auch eine seiner dort vorgeschlagenen Lösungen nehmen.

    Wenn aber beispielsweise Playlists nur aus Tracks bestehen können (im Gegensatz zu Tracks und Unterplaylists) und dein Playerhauptprogramm bloß mit Playlists arbeitet, dann kommst du nie in die Verlegenheit, nicht zu wissen, welche Art von Objekt vorliegt. Weil dann dein Hauptprogramm einen vector<Playlist> (oder sonstigen Container) hat und deine Playlists einen vector<Track>. Trotzdem kannst du diese Objekte dann auch in Funktionen nutzen, die auf der Basisklasse arbeiten, denn jeder Track und jede Playlist ist dann schließlich auch eine gültige Instanz der Basisklasse.



  • hustbaer schrieb:

    D.h. du willst Playlisten aus Tracks und weiteren "Unterplaylisten" zusammensetzen können?
    Also wie Verzeichnisse Files und weitere Unterverzeichnisse enthalten können?

    Nein, Unterplaylisten existieren nicht.

    Wenn ich euch richtig verstanden habe, dann meint ihr einfach Playlist als Base-Klasse für Track zu verwenden?



  • eher:

    #include <vector>
    
    struct playable { void something() {} };
    class track : public playable {};
    class playlist : public playable { std::vector< track > tracks; };
    
    void bar( playable &thing ) { thing.something(); }
    
    int main()
    {
    	std::vector< playlist > playlists( 3 );
    	track foo;
    
    	playlists[ 0 ].something();
    	foo.something();
    
    	bar( playlists[ 0 ] );
    	bar( foo );
    }
    

    ?



  • heyo schrieb:

    Wenn ich euch richtig verstanden habe, dann meint ihr einfach Playlist als Base-Klasse für Track zu verwenden?

    NEIN!
    Wie kann man das was SeppJ geschrieben hat nur so falsch verstehen? *facepalm*

    Du sollst eine Klasse machen wo du reinpackst was gemeinsam ist, und dann sowohl Playlist als auch Track von dieser gemeinsamen Basisklasse ableiten.



  • @hustbare & @Swordfish
    So hab ich es ja bereits. Nur dass ich bspw. eine Funktion get() möchte, welche je nachdem ob das Objekt der HTTP-Request (zB. 16 Objekte werden angefordert, welche dann Objekt für Objekt ausgelesen werden und dann in eine Klasse gefüllt werden) eine Playlist oder ein Track ist.

    Nochmal was ich erreichen möchte, denn wir sind da glaub ich ein bisschen abgekommen:

    class Base {
        public:
    		QPixmap getArtwork(void);
    		std::string getTitle(void);
    		std::string getUsername(void);
    };
    
    class Track : public Base {
        public:
            std::string getStreamURL(void);
    };
    
    class Playlist : public Base {
        public:
            int getTrackCount(void);
    		std::vector<Track> tracks;
    };
    
    class Dashboard {
    	public:	
    		bool isTrack(void);
    		Track track;
    		Playlist playlist;
    
    		//Pseudo - Ziel
    		Track/Playlist get(void) {
    			if(isTrack())
    				return Track;
    			else
    				return Playlist;
    		}
    };
    


  • Sry, falscher Code (könnte ein Mod bitte diese zwei zusammenführen? :)):

    Edit v. Arcoth: Oben reineditiert.


Anmelden zum Antworten