factory.hpp 3.84 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
\file structure.hpp
\date 18/04/2021
\author Yann Boucher
\version 1
\brief Factory

Fichier contenant la class singleton template Factory, à qui on peut associer diverses classes constructibles à partir d'un nom sous forme de chaîne de caractères.
**/


#ifndef FACTORY_HPP
#define FACTORY_HPP

#include <unordered_map>
#include <memory>
#include <vector>
#include <type_traits>

20
21
22
namespace detail
{
template <typename _Base, typename Child>
23
bool register_to_factory(const char* name);
24
25
}

26
27
28
29
30
31
32
33
34
35
/**
\class Factory
\brief Représente une fabrique fabriquant des objets de classe mère Base

Cette classe permet de lister l'ensemble des objects constructibles par cette dernière, ainsi que la construction d'un objet à partir d'un nom.
**/
template <typename Base>
class Factory
{
    template <typename _Base, typename Child>
36
    friend bool detail::register_to_factory(const char* name);
37
38
39
40
41

public:
    //! \brief Retourne un objet créé à partir de choice
    //!
    //! Fabrique un objet du type représenté par choice.
42
    //! \param choice Le nom de la classe à fabriquer, tel que présent dans la liste retournée par list_choices().
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
    //! \return Un unique_pointer de type Base pointant vers l'objet construit, ou nullptr si choice est invalide.
    static std::unique_ptr<Base> make(const std::string& choice)
    {
        if (!get().child_register.count(choice))
            return nullptr;

        return get().child_register.at(choice)();
    }

    //! \brief Retourne l'ensemble des choix possibles.
    //!
    //! Retourne dans un std::vector l'ensemble des objets fabriquables par la Factory selon le nom auquel ils ont été enregistrés.
    //! Pour créer un objet d'un type donné, il suffit de passer le nom tel que retourné par list_choices() à make().
    //! \return La liste des noms d'objets fabriquables sous forme d'un std::vector.
    static std::vector<std::string> list_choices()
    {
        std::vector<std::string> labels;
        for (const auto& pair : get().child_register)
            labels.emplace_back(pair.first);
        return labels;
    }

private:
    static Factory<Base>& get()
    {
        static Factory<Base> instance;
        return instance;
    }

    template <typename Child>
73
    bool register_child(const char* name)
74
    {
75
        child_register[name] = []() -> std::unique_ptr<Base>{ return std::make_unique<Child>(); };
76
77
78
79
80
81
82
83
84
85
86
87
88
        return true;
    }

private:
    std::unordered_map<std::string,  std::unique_ptr<Base>(*)()> child_register;
};

//! \brief Ajoute la classe child à la liste des objets fabriquables par Factory<base>.
//!
//! \param base La classe de base de child.
//! \param child Le type de classe dérivée de base que la fabrique devra pouvoir construire.
//! Ajoute la classe child à la liste des objets fabriquables par Factory<base>.
//! Le nom de la classe tel que considéré par factory est obtenu par un appel à Child::name(), il est donc nécéssaire que l'enfant possède une méthode statique de signature 'static std::string name()'.
89
#define REGISTER_FACTORY_ENTRY(base, child, name) \
90
91
92
namespace detail { \
/* nécéssaire pour que la transition soit enregistrée parmis la liste de classes de transitions*/ \
/* variable globale static permettant seulement de forcer l'enregistrement d'une telle classe dans la factory, 'static' permettant l'instantiation de cette variable dans des fichiers .cpp différents lors de l'inclusion*/ \
93
static bool const fact_##base##_##child##_registered = register_to_factory<base, child>(name); \
94
95
96
97
98
99
}

//! \internal namespace contenant les détails d'implémentation de Factory
namespace detail
{
template <typename Base, typename Child>
100
inline bool register_to_factory(const char* name)
101
102
{
    static_assert (std::is_base_of<Base, Child>::value, "Le type Child doit être dérivé de Base");
103
    return Factory<Base>::get().template register_child<Child>(name);
104
105
106
107
108
}
}


#endif // FACTORY_HPP