Design pattern für unbekannten Typ



  • Hallo!

    In einer C++-Library habe ich eine Datenstruktur. In die können Anwender der
    Library Punkte einfügen bzw. darin suchen. Die Library braucht die Koordinaten
    x,y,z, der Typ der Punkte ist egal. Um die Library so allgemein wie möglich zu
    schreiben, soll der User seine eigene Punkt-Klasse verwenden können. Mir fallen
    dafür zwei Lösungen ein:

    a) Die offensichtliche Lösung bieten Templates. Allerdings sollte der Source
    Code im wesentlichen in der Library bleiben und Templates erfordern viel Code in
    den Header-Files.

    b) Man könnte Handles machen, die void-Pointer und die Koordinaten x,y,z
    enthalten. Bei void-Pointern ist mir aber unwohl zumute, ich weiß nicht, ob das
    eine saubere Lösung ist, die man aus der Hand geben kann.

    Gibts passende Design-Pattern für sowas?



  • cbook schrieb:

    a) Die offensichtliche Lösung bieten Templates. Allerdings sollte der Source Code im wesentlichen in der Library bleiben und Templates erfordern viel Code in den Header-Files.

    Template-Libs sind doch gut!
    Ist das Problem, daß Euer Code geheim gehalten werden muss?
    Ab jetzt gehe ich mal dacon aus.

    cbook schrieb:

    b) Man könnte Handles machen, die void-Pointer und die Koordinaten x,y,z
    enthalten. Bei void-Pointern ist mir aber unwohl zumute, ich weiß nicht, ob das
    eine saubere Lösung ist, die man aus der Hand geben kann.

    Kein Problem: Um die geheime void*-Lib kannste untraschlanke Template-Wrapper anbieten, die per static_cast für den User T* draus machen. Oder gar void*,sizeof,alignof für die geheime Lib und T für den User.

    cbook schrieb:

    Gibts passende Design-Pattern für sowas?

    Das würde mich ängstigen.



  • Wenn du keine Templates haben willst, brauchste wohl Type-Erasure.

    class generic_point
    {
    public:
    	template <class P>
    	generic_point(P p)
    	: impl_(new basic_point<P>(p))
    
    	some_type x()
    	{
    		return impl_->x();
    	}
    
    	// analog y, z etc
    
    private:
    	struct base_point
    	{
    		virtual ~base_point() noexcept = default;
    
    		virtual some_type x() = 0;
    	};
    
    	template <class P>
    	struct basic_point
    	{
    		// constructor
    
    		some_type x() override
    		{
    			return p.x();
    		}
    
    		P p;
    	};
    
    	std::unique_ptr<base_point> impl_;
    };
    

    Den Overhead ist das aber definitiv nicht wert.



  • @volkard: Ja, der Source soll im wesentlichen geheim bleiben. Das Problem bei Algorithmen ist, daß sie aufwändig zu entwickeln, aber leicht zu verstehen sind. Wennst Dein Zeug verkaufen willst, sollten zumindest die Kernkomponenten geheim bleiben. Schlanke Template-Wrapper für den void* pointer hatte ich auch schon im Sinn, danke für die Bestätigung.

    @nathan: Lässige Idee, danke. Overhead ist halt auch ein Thema.



  • was spricht gegen Interfaces?
    die library arbeitet mit abstracten Typen welcher der Benutzer implementieren muss?
    oder verstehe ich das Problem nicht?



  • Hi,

    wofür brauchst du denn einen generischen Punkt für den Benutzer? Das verstehe ich nicht ganz. Was ist das für eine Bibliothek?

    Ich finde, das ist alles Overhead und die Flexibilität, die ihr dadurch meint zu erreichen, erscheint mir als Over-Engineering. Die Generik kostet Performance. Das ist mit einer Template-Lösung noch am wenigsten schlimm, aber wenn ich mir Nathans Lösung so anschaue, dann wieder doch.

    Wenn die Klasse einfach über Koordinaten x,y,z zugreift, dann würde ich den Algorithmus eben einfach so gestalten, schön finde ich das aber nicht, z.B.:

    template<typename Point>
    class SomeAlgo
    {
    public:
        Point add(const Point& a, const Point& b)
        {
            Point c;
            c.x = a.x + b.x;
            c.y = a.y + b.y;
            c.z = a.z + b.z;
            return c;
        }
    };
    

    Da wird aber auch wieder klar, dass x,y,z öffentlich sein müssen und auch nicht als Array implementiert sind, das ist dann also doch wieder implementierungsspezifisch.

    Oder:
    Die low-level-Variante, die aber high-speed ist, ist, dass ihr einfach ein Array von Koordintaen verlangt, der Typ kann ja vom Benutzer vorgegeben werden.

    template<typename CoordType>
    class SomeAlgo
    {
    public:
        std::array<CoordType, 3> addAllPoints(std::vector<CoordType> coords)
        {
            // coords[0] := x1, coords[1] := y1, coords[2] := z1, coords[3] := x2...
    
        }
    };
    

    oder so ähnlich. Wenn der Benutzer jetzt einen struct-Typ ohne Overhead hat, kann er ein Array davon auch einfach umcasten zu einem Array von CoordTypes, das erscheint mir am generellsten... ist aber unschön damit zu programmieren und der Code wird unübersichtlich.

    Also wenn Performance kein ganz großes Anliegen ist, würde ich wohl einfach einen Typ vorgeben.

    Gruß
    E



  • cbook schrieb:

    Gibts passende Design-Pattern für sowas?

    da fällt mir das Adapter-Pattern ein, wobei hier die konkreten Punktklasse des Anwenders die Rolle des Dienstes spielt (s. Link).

    Ansonsten schaue Dir mal die Bibliothek boost.polygon an. Die haben das identische Problem, was Du auch hast. Die Lösung besteht in einer Spezialisierung Bibliothekseigener Templates mit der konkreten User-Klasse - z.B.: http://www.boost.org/doc/libs/1_56_0/libs/polygon/doc/gtl_point_concept.htm


  • Mod

    Nathans Lösung ist ja auch einfach inperformant. Ordentliche Type-Erasure sieht man hier: http://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates



  • am einfachsten ist wohl ein möglichst C-like interface aufzubauen wo der Benutzer die Daten einfach als double* übergibt (mit 3*n elementen für n Punkte). Und dann dazu noch nen hübscher C++ wrapper für eure Lieblingstypen, fertig.



  • Gerade ist mir eine Lösung eingefallen, die den Algo-Code verbergen kann und sauschnell bleibt. *freu* 😃

    Werner Salomon schrieb:

    http://www.youtube.com/watch?v=UU_mL0dOqlw (Bernd Brot: "Mist")



  • Arcoth schrieb:

    Nathans Lösung ist ja auch einfach inperformant. Ordentliche Type-Erasure sieht man hier: http://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates

    Nathans Lösung ist die Standardlösung.
    Der Artikel den du verlinkst macht in diesem Zusammenhang auch keinen Sinn, da dabei keine unterschiedlichen Objekte in der generischen Klasse gespeichert werden. Das ist nämlich die Schwierigkeit bei der Sache.



  • @cbook
    Dein Denkfehler ist dass du die Klasse als Container für Objekte beliebigen Typs siehst, die die Attribute X, Y und Z haben.
    Sieh die Klasse lieber als Spatialen Index, in dem du zu einem XYZ-Triplet (Key) einen Wert ablegen kannst.
    Und der Wert muss nicht unbedingt generisch sein. Eine Implementierung mit void* oder size_t ist vollkommen ausreichend.

    Wie volkard schon schreibt...

    volkard schrieb:

    Kein Problem: Um die geheime void*-Lib kannste untraschlanke Template-Wrapper anbieten, die per static_cast für den User T* draus machen.


Log in to reply