2
votes

Instanciation explicite pour un ensemble de types

Dans mon code, je dois généralement écrire une fonction qui prend un type "Path-like", c'est-à-dire quelque chose que je peux convertir en boost :: filesystem :: path , par exemple

  • QString
  • std::string
  • const char *
  • etc ...
  • dans A.hpp

    template <typename PathLike> 
    void A::myFunction(PathLike path)
    {
       boost::filesystem::Path p = convertToBoostPath(path);
       //do something...
    }
    
    //Explicit instantiations needed 
    template void A::myFunction(string);  
    template void A::myFunction(QString);
    template void A::myFunction(char const *);
    //....
    

    dans A.cpp

    struct A
    {
      template <typename PathLike> 
      void myFunction(PathLike path);
    };
    

    Le problème est que si Je veux faire la même chose dans une fonction différente B , je dois ajouter à nouveau les instanciations explicites. Peut-être ai-je une mauvaise approche.


    1 commentaires

    Pourquoi ne pas simplement définir la fonction dans un fichier d'en-tête? Vous pouvez utiliser SFINAE pour contraindre le type si vous en avez besoin.


    3 Réponses :


    3
    votes

    Au lieu d'écrire une fonction de modèle qui prend n'importe quel PathLike et fait également le vrai travail, écrivez une fonction de modèle qui prend n'importe quel PathLike , la convertit en boost :: filesystem :: Path comme indiqué, puis appelle une fonction non-template (dont la définition peut être dans le .cpp) qui fera le vrai travail.

    Dans A.hpp code >:

    void A::myFunctionImpl(boost::filesystem::Path path)
    {
      // do something...
    }
    

    In A.cpp:

    class A
    {
    public:
      template <typename PathLike> 
      void myFunction(PathLike path);
    
    private:
      void myFunctionImpl(boost::filesystem::Path path);
    };
    
    
    template <typename PathLike> 
    void A::myFunction(PathLike path)
    {
      myFunctionImpl(convertToBoostPath(path));
    }
    

    Ceci a l'avantage supplémentaire que une mauvaise utilisation de l'interface entraîne des erreurs de compilation, pas des erreurs de l'éditeur de liens.


    0 commentaires

    0
    votes

    Une façon d'instancier explicitement un modèle de fonction pour un pack de types est de prendre l'adresse de chaque instanciation de fonction. Par exemple :

    template<class... Ts>
    void instantiate_myFunction() {
        auto f = [](auto&&) { return 1; };
        auto initializer_list = { f(&A::myFunction<Ts>)...  };
        static_cast<void>(initializer_list);
    }
    
    int main() {
        instantiate_myFunction<std::string, char const*>();
    }
    


    2 commentaires

    Vous devez écrire un instantiate_myFunction différent pour chaque modèle de fonction pour lequel vous voulez faire cela, n'est-ce pas? Pour autant que je sache, la seule chose que vous pouvez faire avec un modèle de fonction est de l'instancier (par opposition aux modèles de classe, qui peuvent être passés en tant que paramètres de modèle de modèle), malheureusement ...


    @MaxLanghof C'est vrai, utiliser la classe Path est la bonne solution, IMO.



    1
    votes

    Que diriez-vous de créer une classe PathLike et de l'utiliser:

    struct A
    {
        void myFunction(PathLike path)
        {
           boost::filesystem::path p = path;
           //do something...
        }
    };
    

    (je marque un autre constructeur comme explicite pour promouvoir filesystem :: path , mais à vous d'ajouter / supprimer explicit).

    Et puis:

    class PathLike
    {
    public:
        explicit PathLike(const QString& path) : mPath(convertToBoostPath(path)) {}
        explicit PathLike(const std::string& path) : mPath(convertToBoostPath(path)) {}
        explicit PathLike(const char* path) : mPath(convertToBoostPath(path)) {}
        PathLike(const boost::filesystem::path& path) : mPath(path) {}
    
        const boost::filesystem::path& get() const { return mPath;}
        operator const boost::filesystem::path&() const { return mPath;}
    private:
        boost::filesystem::path mPath;
    };
    


    0 commentaires