[Wieder offen] Anzahl Funktionsparameter abhängig von Template Argument?



  • Ich habe so etwas ähnliches auch mal gebaut. Man kann mehrere Parameter in einem zusammenfassen. Ich hatte dazu eine point<T,N>-Klassenfamilie. Die verhält sich so ähnlich wie tr1::array mit dem Unterschied, dass ich mit den Dingern auch rechnen kann.

    template<typename T, unsigned int D> // T = Typ, D=Anzahl der Dimensionen
    class multi_array
    {
       std::vector<T> elements_;
    public:
       void resize( point<unsigned,D> dimensions );
       ...
    };
    


  • FreakY<3Cpp schrieb:

    Das wird hart wenn er mehr als 10 Argumente will.

    Eigentlich nicht, in diesem Fall würde man z.B. auf boost.preprocessor zurückgreifen damit kann man all die spezialisierungen generieren.



  • FreakY<3Cpp schrieb:

    Das wird hart wenn er mehr als 10 Argumente will.

    Neulich, als ich meine Autos in einem zehndimensionalen Array verwalten wollte, hat mich mein Rechner nach

    ma10.resize(10,20,30,40,50,60,70,80,90,100);//36288000000000000 Elemente
    

    einfach ausgelacht.



  • volkard schrieb:

    FreakY<3Cpp schrieb:

    Das wird hart wenn er mehr als 10 Argumente will.

    Neulich, als ich meine Autos in einem zehndimensionalen Array verwalten wollte, hat mich mein Rechner nach

    ma10.resize(10,20,30,40,50,60,70,80,90,100);//36288000000000000 Elemente
    

    einfach ausgelacht.

    😃 👍 🤡



  • Aso nein klar, hab was verwechselt 😃



  • Auch auf die Gefahr hin dass mich jetzt wer lyncht:

    #include <algorithm>
    
    struct _t {};
    static const _t _;
    
    template <int DIM>
    struct ResizeDimensions
    {
      int r[DIM];
    };
    
    template <int DIM>
    ResizeDimensions<DIM+1> operator, (ResizeDimensions<DIM> lhs, int rhs)
    {
      ResizeDimensions<DIM+1> ret;
      std::copy(lhs.r, lhs.r+DIM, ret.r);
      ret.r[DIM] = rhs;
      return ret;
    }
    
    ResizeDimensions<1> operator, (_t, int i)
    {
      ResizeDimensions<1> ret;
      ret.r[0] = i;
      return ret;
    }
    
    //das hier durch deine Klasse ersetzen...
    template <int DIM>
    class foo
    {
    public:
      void bar(ResizeDimensions<DIM> rd)
      {}
    };
    
    int main()
    {
      foo<2> f2;
      foo<5> f5;
      f2.bar((_,1,2)); //OK
      f5.bar((_,1,2,3,4,5)); //OK
      f5.bar((_,1,2,3,4,5,6)); //FEHLER
    }
    

    die doppelten Klammern sind leider nötig damit zuerst der operator,() als solcher erkannt wird und nicht 3 bzw. 6 bzw. 7 verschiedene Parameter angenommen werden.



  • Ich seh schon, etwas elegantes gibt´s da wohl nicht.
    Danke für die Ansätze, aber ich glaube, ich lass es erst einmal so, wie´s gerade ist (Parameter mit Defaultwerten 1 für unbenutzte Dimensionen),



  • Update: Mir ist doch noch was anderes eingefallen. Die Funktion soll ja nur so aussehen wie eine Funktion, kann aber natürlich auch ein Funktionsobjekt sein, dass den operator() implementiert. Meine aktuelle Lösung sieht jetzt so aus:

    #include <vector>
    #include <assert.h>
    
    #include <boost/noncopyable.hpp>
    
    // forward Deklaration für array Klasse
    template<typename T, unsigned int D>
    class array;
    
    // forward Deklaration für Resizer Objekt
    template<typename T, unsignd int D>
    class array_resizer;
    
    // Spezialisierung für 1D-resizer
    template<typename T>
    class array_resizer<T,1> : boost::noncopyable
    {
       // Zeiger auf Array
       array<T,1>* array_;
    
    public:
       array_resizer( array<T,1>* arr ) : array_( arr ) 
       {
          assert( arr != 0 );
       };
    
       // einstelliger operator für 1D Array
       void operator()( unsigned int d0 );
    };
    
    // Spezialisierung für 2D-resizer
    template<typename T>
    class array_resizer<T,2> : boost::noncopyable
    {
       // Zeiger auf Array
       array<T,2>* array_;
    
    public:
       array_resizer( array<T,2>* arr ) : array_( arr )
       {
          assert( arr != 0 );
       };
    
       // zweistelliger operator für 2D Array
       void operator()( unsigned int d0, unsigned int d1 );
    };
    
    template<typename T, unsigned int D>
    class array
    {
       // Vektor mit Elementen
       std::vector<T> Elements_;
    
       // Resizer Objekt ist friend für direkten Zugriff auf den Vektor
       friend class array_resizer<T,D>;
    
    public:
       array() : resizer_( this )
       {
       }
    
       // Objekt muss leider public sein, damit es aussieht wie eine Methode
       // Spielt aber keine Rolle, da Resizer Objekte nicht kopierbar sind
       array_resizer<T,D> resize;
    };
    
    // Implementation der array_resizer Operatoren
    template<typename T, 1>
    class array_resizer<T,1>::operator( unsigned int d0 )
    {
       array_->Elements_.resize( d0 );
    }
    
    template<typename T,2>
    class array_resizer<T,2>::operator( unsigned int d0, unsigned int d1 )
    {
       array_->Elements_.resize( d0 * d1);
    }
    

    Ist etwas unschön, da man für jede Dimensionsanzahl ein eigenes Resizer Objekt braucht, aber dafür sieht es aus wie eine normale Methode.
    Hat da jemand Einwände/Verbesserungsvorschläge?



  • DocShoe schrieb:

    Hat da jemand Einwände/Verbesserungsvorschläge?

    Ich bin mir relativ sicher, daß eine Lösung, die auf irgendein Verbiegen obskurer Sprachfeatures verzichtet, mittelfristig besser wartbar sein wird. Ich finde solche Experimente grundsätzlich durchaus reizvoll, aber in Produktivcode eher nicht - öffentliche Datenobjekte, die so tun müssen, als seien sie Funktionen, sind ein Indiz, daß da etwas nicht stimmt. Lieber etwas in der Richtung, die evilissimo vorschlug, wenngleich ich die Lösung unter Verzicht auf Generizität noch etwas vereinfachen würde:

    // data_holder/resizer_base hin, MultiArrayBase/MultiArraySpec her, die Nomenklatur ist zweifellos verbesserungsbedürftig.
    template <typename T, size_t Dim>
      class MultiArrayBase
    {
    protected:
        void doResize (size_t newSize);
        ...
    };
    
    template <typename T, unsigned Dim>
      class MultiArraySpec;
    
    template <typename T>
      class MultiArraySpec <T, 1> : public MultiArrayBase <T, 1>
    {
    public:
        void resize (size_t s0) { doResize (s0); }
    };
    template <typename T>
      class MultiArraySpec <T, 2> : public MultiArrayBase <T, 2>
    {
    public:
        void resize (size_t s0, size_t s1) { doResize (s0 * s1); }
    };
    ...
    
    template <typename T, size_t Dim>
      class MultiArray : public MultiArraySpec <T, Dim>
    {
        ...
    };
    

    Deine N Spezialisierungen für N unterstützte Dimensionen mußt du ohnehin schreiben, und dann spricht eigentlich nichts gegen die einfachere Variante. Zudem kannst du so später um weitere spezialisierungsbedürftige Funktionen erweitern.

    Wenn es schon generisch sein muß, dann bitte so, denn wir wollen uns ja nicht auf einen Member-Container mit dem Namen elements_ festlegen 😉

    template <template <typename, size_t> class M, template <typename> class Base, void (Base::* ResizeFunc) (size_t), typename T>
      struct resizer_base <M <T, 1> > : public Base <T>
    {
        void resize (size_t s0) { (this->*ResizeFunc) (s0); }
    };
    


  • Das war mal ein richtig guter Vorschlag, bin dir sehr dankbar dafür. Hast meinen Gedanken zuende gedacht, ich bin wohl auf dem halben Weg stehengeblieben. Statt die Schnittstelle durch interne Objekte zu definieren ist es wohl tatsächlich schlauer, die Schnittstelle durch Vererbung zu definieren. Ich werd´s das gleich mal ausprobieren.


Anmelden zum Antworten