Commit a8d3d498 authored by Yann Boucher's avatar Yann Boucher
Browse files

Improved documentation for REGISTER_FACTORY_ENTRY

parent 7e6fe1ab
Pipeline #76530 passed with stages
in 16 seconds
/**
\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>
/**
\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>
friend bool register_to_factory();
public:
//! \brief Retourne un objet créé à partir de choice
//!
//! Fabrique un objet du type représenté par choice.
//! \param choice Le nom de la classe à fabriqué, tel que présent dans la liste retournée par list_choices().
//! \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>
bool register_child()
{
child_register[Child::name()] = []() -> std::unique_ptr<Base>{ return std::make_unique<Child>(); };
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()'.
#define REGISTER_FACTORY_ENTRY(base, child) \
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*/ \
static bool const fact_##base##_##child##_registered = register_to_factory<base, child>(); \
}
//! \internal namespace contenant les détails d'implémentation de Factory
namespace detail
{
template <typename Base, typename Child>
inline bool register_to_factory()
{
static_assert (std::is_base_of<Base, Child>::value, "Le type Child doit être dérivé de Base");
return Factory<Base>::get().template register_child<Child>();
}
}
#endif // FACTORY_HPP
...@@ -22,7 +22,8 @@ HEADERS += \ ...@@ -22,7 +22,8 @@ HEADERS += \
../include/property.hpp \ ../include/property.hpp \
../include/propertyvisitors.hpp \ ../include/propertyvisitors.hpp \
../include/structure.hpp \ ../include/structure.hpp \
../include/neighborhood.hpp ../include/neighborhood.hpp \
../include/factory.hpp
# Default rules for deployment. # Default rules for deployment.
......
...@@ -10,6 +10,7 @@ private slots: ...@@ -10,6 +10,7 @@ private slots:
void test_property(); void test_property();
void test_loader_saver_visitor(); void test_loader_saver_visitor();
void test_structure(); void test_structure();
void test_factory();
}; };
#endif // CELLULUT_TESTS_HPP #endif // CELLULUT_TESTS_HPP
#include "cellulut_tests.hpp"
#include "factory.hpp"
class Transition
{
public:
virtual std::string say_hi() = 0;
};
class LifeLikeTransition : public Transition
{
public:
static std::string name()
{ return "life-like"; }
virtual std::string say_hi()
{ return "Life hi!\n"; }
};
REGISTER_FACTORY_ENTRY(Transition, LifeLikeTransition);
class GenerationsTransition : public Transition
{
public:
static std::string name()
{ return "generations"; }
virtual std::string say_hi()
{ return "Generations hi!\n"; }
};
REGISTER_FACTORY_ENTRY(Transition, GenerationsTransition);
void CellulutTests::test_factory()
{
QCOMPARE(Factory<Transition>::list_choices().size(), 2);
QCOMPARE(Factory<Transition>::list_choices()[0], "life-like");
QCOMPARE(Factory<Transition>::list_choices()[1], "generations");
std::unique_ptr<Transition> life_trans = Factory<Transition>::make("life-like");
QVERIFY(life_trans != nullptr);
QCOMPARE(life_trans->say_hi(), "Life hi!\n");
std::unique_ptr<Transition> gens_trans = Factory<Transition>::make("generations");
QVERIFY(gens_trans != nullptr);
QCOMPARE(gens_trans->say_hi(), "Generations hi!\n");
QVERIFY(nullptr == Factory<Transition>::make("<invalid>"));
}
...@@ -15,6 +15,7 @@ INCLUDEPATH += ../include ...@@ -15,6 +15,7 @@ INCLUDEPATH += ../include
SOURCES += \ SOURCES += \
../src/propertyvisitors.cpp \ ../src/propertyvisitors.cpp \
factory_tests.cpp \
property_test.cpp \ property_test.cpp \
propertyvisitors_test.cpp \ propertyvisitors_test.cpp \
cellulut_tests.cpp \ cellulut_tests.cpp \
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment