2017-08-18 2 views
0

Je ne suis pas sûr que ce soit possible du tout ce que j'essaie d'atteindre ces 3 dernières heures sans aucune approche sensorielle.Définition d'un type de gabarit variadique avec une liste d'arguments inconnue comme type de valeur std :: map

J'ai un modèle variadique comme ceci:

template<typename... Ts> 
class xunit_test_item { 
public: 
    template<typename F, typename... Args> 
    xunit_test_item(const std::string& name, F&& func, Args&&... args) 
    : m_name(name) 
    , m_f(std::forward<F>(func)) 
    , m_args(std::forward<Args>(args)...) 
    { 
    } 

    // ... 

private: 
    const std::string m_name; 
    std::function<void (Ts...) > m_f; 
    std::tuple<Ts...> m_args; 
}; 

enveloppements Au-dessus de modèle un élément de test qui est essentiellement un titulaire d'un foncteur avec des arguments passés le long d'une chaîne pour nommer cet élément.

Les objets de ce type doivent être stockés dans un fichier std :: map comme type de valeur. Ceci est géré dans une classe qui contient un conteneur std::map en tant que membre, une méthode d'enregistrement pour ajouter des éléments xunit_test_item et une méthode d'exécution, qui itère sur la carte et exécute toutes les fonctions stockées xunit_test_item stockées.

La classe ressemble fondamentalement ceci:

class unit_test { 

    // Define a map type of xunit_test_items 
    template<typename ...Ts> 
    using testitem_map_t = std::map<size_t, xunit_test_item<Ts...>>; 

public: 
    unit_test() = delete; 
    unit_test(const std::string& group_name) 
    : m_tm() 
    , m_group_name(group_name) 
    , m_test_id(0) 
    { 

    } 
    // Register an functor item 
    template<typename F, typename ...Args> 
    void add_test(const std::string& name, F&& fun, Args&& ...args) { 
     m_tm.emplace(++m_test_id, xunit_test_item<Args...>(
      name, std::forward<F>(fun), std::forward<Args>(args)...)); 
    } 

    void run() 
    { 
     // ... 

     for (auto& t : m_tm) { 
      // ... 
      t.second->run(); 
      // ... 
     } 
    } 

private: 
    testitem_map_t m_tm; // <-- Compiler correctly reports an error 
    std::string m_group_name; 
    size_t m_test_id; 
}; 

Le problème que j'ai est de trouver la bonne approche de syntaxe/différente pour définir le xunit_test_item comme le type de valeur de l'élément de carte m_tm. Le code ci-dessus ne compile pas - sans aucun doute - car je dois spécifier une liste d'arguments pour le membre m_tm avec son type testitem_map_t. Je n'ai aucune idée comment définir ce type et déclarer correctement le membre m_tm. La liste des arguments est inconnue à ce stade. Peut-être mes classes ci-dessus ont un problème de conception, je me demande s'il existe un moyen plus ou moins simple pour y parvenir. Je peux également complètement ne pas voir une solution simple.

Serait heureux d'apprendre et obtenir une bonne suggestion. Merci d'avance!

Mise à jour

fraîches par jour, frais et essayer une tasse de café ...

Je refactorisé tout le corps de la méthode add_test utilisant un lambda à 2 niveaux et qui semble fonctionner jusqu'à présent. En tant que fonctions de test (les cas de test réels), je peux ajouter des lambdas, des pointeurs de fonction avec des arguments arbitraires ou non.

Je ne sais pas si c'est la meilleure solution, ou si elle peut être mieux codée, peut-être que quelqu'un veut commenter sur ce sujet. Aussi la raison pour laquelle je n'entre pas cela comme réponse. Mon espoir avec ma question initiale était d'apprendre peut-être du sucre C++ 14 comme solution ... si possible.

Ceci est une première version testée à peu près ...

template<typename F, typename ...Args> 
void add_test(const std::string& name, F&& fun, Args&& ...args) { 
    std::function<void(Args...)> fun_xargs = 
     [name, fun](auto&&... args) { 
      std::cout << " inner lambda\n"; 
      auto x = xunit_test_item<Args...>(name, fun, std::forward<Args>(args)...); 
      x.run(); 
     }; 

    std::function<void()> fun_void = 
     [=] { 
      std::cout << "outer lambda\n"; 
      fun_xargs(args...); 
     }; 

    m_tm.emplace(++m_test_id, fun_void); 
} 
+2

wrap 'run' dans un lambda et le stocker comme' std :: fonction 'sur la carte – vu1p3n0x

+0

Note: le paramètre' Args ... 'de' xunit_test_item :: xunit_test_item' ne peut jamais se résoudre en un paquet convertible en 'Ts ...', donc vous pouvez aussi bien utiliser 'Ts .. .' là. – Caleth

+0

@caleth: Bien noté! –

Répondre

0

Il vous semble seulement besoin:

class unit_test { 
public: 
    unit_test() = delete; 
    unit_test(const std::string& group_name) 
    : m_tm(), m_group_name(group_name) 
    { 

    } 

    // Register an functor item 
    void add_test(const std::string& name, std::function<void()> f) { 
     m_tm.emplace(name, std::move(f)); 
    } 

    void run() 
    { 
     // ... 
     for (const auto& t : m_tm) { 
      // ... 
      t.second->run(); 
      // ... 
     } 
    } 

private: 
    std::map<std::string, std::function<void()>> m_tm; 
    std::string m_group_name; 
}; 
+0

On dirait que ma première version, mais je l'ai étendu pour garder la fonction de test, ses arguments arbitraires, le nom du test dans un modèle séparé 'xunit_test_item'. Je montre juste le constructeur, il a quelques autres méthodes avec une méthode d'exécution 'run' pour la fonction passée. BTW, la conception est similaire à la réponse acceptée de https://stackoverflow.com/questions/16868129/how-to-store-variadic-template-arguments. Mon essai actuel est un wrapper lambda dans la méthode 'add_test' pour ajouter un' xunit_test_item' varié, mais je ne sais pas si cela résoudra mon problème. –

+0

Quelles sont les caractéristiques que vous voulez de 'xunit_test_item' et en particulier celles que vous voulez accéder via' m_tm'? – Jarod42

+0

Il n'y a pas vraiment beaucoup de méthodes. Les compositeurs function/functor, une méthode 'run' (en fait c'est la' run' exécutée dans 'unit_test :: run'), last but not least quelques getters, la chaîne de nom du cas de test et un argument stringisé getter (args to string) en utilisant le membre std :: tuple. J'étais heureux d'avoir concentré tout cela dans un modèle et aussi un bon endroit pour de futures améliorations, par exemple le temps d'exécution ou le tel. Mais comme je le vois, il s'avère être un cauchemar quand il s'agit de stocker ces objets dans la carte. Et l'essai de lambda me donne des problèmes de déduction d'argument ... –