Bonjour à tous,
Je ne sais pas si c'est possible, mais j'essaye actuellement de créer un gestionnaire de ressources sous la forme d'une classe template variadique, possédant une méthode templatisée de création d'une ressource d'un type donné, permettant grâce à une expression constante et une assertion statique de vérifier si le type de ressource spécifié est autorisé par la classe (en le comparant aux types du template variadique) et d'interrompre la compilation dans le cas contraire.
Le problème est que je sèche sur le code de la méthode de vérification du type de ressource.
Voici un bout de code (simplifié) pour vous donner une idée :
template<typename... Types>
class Manager
{
public:
template<typename Type, typename... Arguments>
size_t create(Arguments... arguments)
{
static_assert(testTypes<Type>(), "Template function does not allow this type !");
Type* ptr = new Type(Arguments...);
...
return nextId();
}
template<typename Type>
constexpr bool testTypes(); // méthode de vérification du type de ressource
size_t nextId()
{
static size_t id;
return id++;
}
...
};
typedef Manager<sf::Texture, sf::Font, sf::SoundBuffer> ResourceManager;
ResourceManager resources();
resources.create<sf::Texture>(); // Compilation OK
resource.create<sf::Sprite>(); // Compilation ERROR
J'ai quelques pistes pour le code de la méthode testTypes() :
- Pour obtenir le nombre de type passés en template j'utilise -> sizeof...(Types)
- Pour vérifier que deux types sont les mêmes j'utilise -> std::is_same<Type1, Type2>
- Je peut normalement décompresser Types avec l'opérateur ... -> Types...
Maintenant il reste à créer une boucle en méta-programmation (je sais pas si c'est possible),
afin de balayer les types grâce au code :
template <int N, typename... Args> struct ElementType;
template <int N, typename T, typename... Args>
struct ElementType<N, T, Args...> {
static_assert(N < sizeof...(Args) + 1, "overflow!");
typedef typename elementType<N - 1, Args...>::type type;
};
template <typename T, typename... Args>
struct ElementType<0, T, Args...> {
typedef T type;
};
// Par exemple :
elementType<1, sf::Texture, sf::Font, sf::SoundBuffer>::type>::value // donnera le type sf::Font
J'espère que je suis assez clair, et désolé par avance sinon, le sujet est assez difficile je trouve... :-\
Pour contourner un problème assez similaire, j'ai créer une classe qui puisse englober n'importe quelle ressource (qui se charge !) SFML
template<class S>
class MemoryRessource
{
public:
/// Loads a ressource from memory, and saves its offset
MemoryRessource(const long offset, const std::vector<char>& bytes) :
m_offset(offset),
m_buffer(),
{
if(!m_buffer.loadFromMemory(&bytes[0], bytes.size()))
{
throw std::logic_error("Could not load data from memory");
}
}
~MemoryRessource()
{
/// Nothing to delete, m_buffer will be destroyed automatically
}
/// Returns the ressource (ie: sf::Texture)
inline S& get() { return m_buffer; }
/// Returns the ressource's offset
inline const long getOffset() const { return m_offset; }
protected:
MemoryRessource() { }
private:
long m_offset;
S m_buffer;
};
Le tout, dans un manager:
template<class S>
class Memory
{
public:
/* pleins de fonctions inutiles ici :D */
/// Accessing the ressource
inline S& getRessource(const unsigned long key) { return m_loaded[key]->get(); }
private:
std::map<unsigned long, std::shared_ptr<MemoryRessource<S>>> m_loaded;
};
Tu peux aussi bien ajouter une classe abstraite à Memory, et comme ca tu auras un type global pour chaque manager de ressources.