2
votes

chemin de jointure des paramètres variadiques c ++

J'essaie d'écrire une fonction de chemin de jointure avec un modèle variadique. Voici comment je fais:

template<typename T>
T&& join_path (T&& path) {
    return path;
}

template<typename T, typename ... Args>
std::string join_path (T&& path1, Args&& ... paths)
{
    static_assert(std::is_same<typename std::decay<T>::type, std::string>::value ||
                  std::is_same<typename std::decay<T>::type, const char *>::value,
                  "T must be a basic_string");

    std::string p2 = join_path(std::forward<Args>(paths)...);
    if (!p2.empty() && p2[0] == '/')
        return path1 + p2;

    return path1 + '/' + p2;
}

Mais il y a un problème, quand je passe une chaîne littérale comme join_path("system", path) le T est considéré comme const char * . Je ne peux donc pas utiliser l'opérateur +. Comment puis-je y remédier?

Un correctif auquel je pense est de return std::string(path1) + '/' + p2; . Mais n'introduirait-il pas de copie supplémentaire?

c++

2 commentaires

path1 + ('/' + p2) éviterait une copie.


Notez que C ++ 17 introduit std::filesystem::path


3 Réponses :


2
votes

Vous pouvez convertir explicitement le premier paramètre en std::string . Vous pouvez vous assurer que ce n'est pas répété en utilisant une expression de repli (C ++ 17):

template<class ... Strings>
std::string join_path (std::string path, Strings&& ... paths)
{
  path += ((std::string(paths) + '/') + ...);
  if constexpr (!sizeof...(paths))
    path1.pop_back(); // remove last /                                                                                         
  return path;
}

PS Vous pouvez utiliser std::decay_t<T> au lieu de typename std::decay<T>::type .


0 commentaires

3
votes

Pour éviter la situation std::string(std::string) , jetez un œil à cette solution par Simple . Cela fonctionne plus rapidement que votre solution et solution de Kostas.

Voici la solution modifiée pour votre cas d'utilisation:

inline std::string const& to_string(std::string const& s) { return s; }

template<typename... Args>
std::string join_path(Args const&... args)
{
    std::string result;
    std::string s;
    using ::to_string;
    using std::to_string;
    int unpack[]{0, (result += ((!(s = to_string(args)).empty() && s[0]=='/') ? "" : "/") + s, 0)...};
    static_cast<void>(unpack);
    return result;
}

En dehors de cela, si vous vous inquiétez de std::string(const char*) , je pense que ce n'est pas facilement évitable.


1 commentaires

L'expression fold de C ++ 17 pourrait remplacer l'astuce de unpack .



4
votes

Vous pouvez utiliser std::string_view depuis C ++ 17:

std::string join_path(std::initializer_list<std::string_view> paths)
{
    std::string res;
    const char* sep = "";
    for (auto p : paths) {
        res += (!p.empty() && p[0] == '/') ? "" : sep
        res += p;
        sep = "/";
    }
    return res;
}

template<typename ... Ts>
std::string join_path (Ts&&... paths)
{
    static_assert(((std::is_same<typename std::decay<Ts>::type, std::string>::value ||
                  std::is_same<typename std::decay<Ts>::type, const char *>::value) || ...),
                  "T must be a basic_string");
    return join_path({paths...});
}

Démo


6 commentaires

Vous voudrez peut-être remplacer res += sep par res += (!p.empty() && p[0]=='/') ? "" : sep . A part ça, excellente solution rapide! Beaucoup plus rapide que celui que j'ai posté.


Terminé, pas sûr de ce que OP veut gérer avec cela, il n'y a pas de vérification de la fin de path1 .


Bonne observation! Je pense que c'est une erreur (que j'ai ratée aussi ...). Mais il semble que la fonctionnalité doit être similaire à path.join en Python.


Merci, cela fonctionne très bien! Une question cependant, est-ce que cela aide à utiliser for (auto &p : paths) ou sont-ils les mêmes?


std::string_view est un petit objet (2 pointeurs), donc par valeur c'est bien.


Je suis à peu près sûr que la seule raison pour laquelle la vérification de la vacuité est effectuée est de tenir compte de la dernière récursivité dans l'implémentation de la question, et de ne pas s'attendre à ce qu'un chemin transmis soit vide. Excellente solution cependant.