Template Argument



  • Hallo,

    ich habe folgende Situation. In einem Projekt stelle ich ein Mesh Objekt von Laufzeit-definierter Dimensionalität auf Compilezeit-definierte um. Dafür wird die Klasse in ein Template umgewandelt. Ich habe also ein Template Mesh und einen Shared Pointer Typ auf selbiges:

    template<int dim> class Mesh;
    
    template<int dim> using PtrMesh = std::shared_ptr<Mesh<dim>>;
    

    Nun wird PtrMesh im ganzen Projekt oft verwendet. Kein Problem ist es, wenn es nur ein Funktionsargument ist

    template<int dim> void fun(PtrMesh<dim> mesh);
    

    und dim wird durch den Aufrufer festgelegt.

    Wenn der Typ allerdings in einer Member Variable einer Klasse verwendet wird

    template<int dim>
    class X {
      PtrMesh<dim> mesh;
    };
    

    Muss diese Klasse immer komplett in ein Template umgewandelt werden und entspechend muss beim Instanzieren dim bekannt sein und mitgegeben werden. So breitet sich die Änderung wesentlich weiter aus, als ich es anfänglich vermutet habe... 😡

    Gibt es Möglichkeiten dies Abhängigkeit zu brechen oder herum zu arbeiten?

    Man könnte boost::any verwenden, jedoch ist das auch nicht transparent für die Benutzer von PtrMesh, hat also auch weitreichende Änderungen zur Folge.

    C++ Standard ist 11.

    Vielen Dank,
    Florian



  • Da müsste man erstmal wissen, was dim überhaupt darstellen soll und ob das wirklich ein Templateparameter sein muss.
    Aber eine Möglichkeit damit umzugehen ist, eine abstrakte Basisklasse Mesh zu nutzen und das Klassentemplate davon erben zu lassen. Hantiert wird dann nur mit Zeigern auf die Basisklasse.
    Ob das in diesem Fall Sinn macht... kommt drauf an.



  • dim soll die Dimensionalität des Meshes angeben. Ausgangspunkt der Idee es zur Compilezeit festzulegen ist, dass wir gerne boost.geometry für räumliche Abfragen auf dem Gitter benutzen würden. Dies geht jedoch nur, wenn die Dimensionalität zur Compilezeit definiert ist.

    Das Problem bei der Idee mit der Basisklasse ist, wenn Mesh abhängige Typen (also Typen in denen dim vorkommt) als Rückgabewert hat, so kann ich die Methode nicht in der Basisklasse deklarieren. Somit braucht der Benutzer von PtrMesh (bzw. dann PtrBase) einen Cast und damit wieder die Information zu dim zur Compilezeit.

    Beispiel:

    #include <iostream>
    #include <memory>
    #include <Eigen/Core>
    
    using namespace std;
    
    class Base {
    public:
      virtual double getX() { return 0; };
      virtual void setX(double x) {};
      virtual void say() { cout << "Base" << endl; }
    
      };
    
    template<int dim>
    class Mesh : public Base
    {
    public:
      double getX() { return coords[0]; }
      void setX(double x) { coords[0] = x; }
      void say() { cout << "Vertex" << endl; }
    
      Eigen::Matrix<double, dim, 1> getCoords() { return coords; }
    
      Eigen::Matrix<double, dim, 1> coords;
    };
    
    template<int dim>
    using PtrMesh = std::shared_ptr<Mesh<dim>>;
    using PtrBase   = std::shared_ptr<Base>;
    
    class MeshUser
    {
    public:
      explicit MeshUser(PtrBase p) {
        v = p;
      }
    
      PtrBase v;
      void say() {
        v->say();
        // v->getCoords(); // geht nicht
        auto m = static_pointer_cast<Mesh<3>>(v);
        m->getCoords();
      }
    };
    
    int main() {
      PtrMesh<3> p = std::make_shared<Mesh<3>>();
      p->say();
    
      MeshUser vc(p);
      vc.say();    
    }
    


  • Welchen Ansatz du auch wählst, du musst ihn komplett durchziehen. Und das nicht vordergründig aus technischen, sondern schon aus rein logischen Gründen.

    Wenn du die Meshklasse als Blackbox implementierst, muss auch eine entsprechende Coord-Blackbox her, welche ebenso wie Mesh alle Operationen anbietet, die für alle dim-Werte anwendbar sind. Diese kannst du dann in der Klasse MeshUser nutzen, ohne die genaue Dimensionalität zu kennen. Wenn es aber keine sinnvollen gemeinsamen Operationen gibt oder die Klasse MeshUser trotzdem genau wissen muss, mit welcher Dimension sie arbeitet, dann gebietet es sich allein deswegen, MeshUser zu einem Template zu machen - denn es sind dann unterschiedliche Klassen, die unterschiedliche Dinge nach einer anderen Logik tun.


Anmelden zum Antworten