Bibliothèque de gestion d'inventaire

Présentation
Cette bibliothèque header-only propose une classe de gestion d'inventaire.
La classe inventaire permet d'ajouter et supprimer plusieurs éléments d'un inventaire.
Un programme d'exemple montre comment on peut s'en servir.

Je n'ai pas écrit le support des fichiers, peut-être dans une autre version.

Le code est conçu pour servir d'exemple.
Il introduit donc un certain nombre de concept classique qui servent habituellement.


  • méthodes constantes d'une classe

  • argument passé par référence constante

  • constructeur par défaut et liste d'initialisation

  • validation d'argument avec ou sans exceptions, selon la gravité

  • try ... catch et std::cerr

  • délégation de méthode (begin() et end())

  • opérateur << vers un ostream

  • formatage de nombre écrit dans un ostream

  • template de méthode

  • template de classe et typedef public

  • usage de typename dans une template de classe

  • garde contre les doubles inclusions.

  • du bon usage du commentaire

  • usage de l'anglais et du français dans un code (l'exemple s'exécute en français)

Nos ressources disponibles
le header: inventory.hpp
Code C++ :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
73
74
75
76
77
78
79
80
81
82
83
84
#ifndef INVENTORY_HEADER_INCLUDED 
#define INVENTORY_HEADER_INCLUDED 1 
  
#include <map> 
#include <stdexcept> 
  
namespace inventory { 
  
template <typename Item, typename Size = long> 
class inventory { 
public: 
	typedef Size size_type; 
	typedef Item item; 
  
private: 
	typedef std::map<item, size_type> internals_type; 
	internals_type internals; 
	size_type full_size; 
  
public: 
	typedef typename internals_type::const_iterator const_iterator; 
  
public: 
	/* 
	creates an empty inventory 
	*/ 
	inventory() : full_size(0) {} 
  
	/* 
	adds one or more items 
	returns the number of this item kind after addition. 
	throws std::invalid_argument if count is negative 
	*/ 
	size_type add(item const& that, size_type count = 1) { 
		if (count<0) throw std::invalid_argument("can't add negative element count"); 
		full_size+=count; 
		return internals[that] += count; 
	} 
  
	/* 
	removes one or more items. 
	returns true if remove was successful, false if there were not enough items 
	throws std::invalid_argument if count is negative 
	*/ 
	bool remove(item const& that, size_type count = 1) { 
		if (count<0) throw std::invalid_argument("can't remove negative element count"); 
  
		//no need to run search if 0. 
		if (count==0) return true; 
  
		auto where = internals.find(that); 
		if (where == internals.end() || where->second < count) { 
			return false; 
		} 
		full_size-=count; 
		where->second -= count; 
		return true; 
	} 
  
	/* 
	gives the number of items in this inventory 
	*/ 
	size_type size() const {return full_size;} 
  
	/* 
	gives the number of item kinds 
	*/ 
	size_type distinct() const {return internals.size();} 
  
	/* 
	gives the count of a specific item in this inventory. 
	*/ 
	size_type count(item const& that) const { 
		auto where = internals.find(that); 
		return (where != internals.end()) ? where->second : 0; 
	} 
  
	const_iterator begin() const {return internals.begin();} 
	const_iterator end() const {return internals.end();} 
}; 
  
}//inventory:: 
  
#endif

et le main, inventory_example.cpp
Code C++ :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <string> 
  
#include <iostream> 
#include <iomanip> 
  
#include "inventory.hpp" 
  
template <typename Item, typename Size> 
std::ostream& operator<<(std::ostream& os, inventory::inventory<Item, Size> const& i) { 
	os << std::right; 
	for(auto const& content : i) { 
		os << std::setw(8) << content.second << " x " << content.first << '\n'; 
	} 
	return os << "------------\ntotal :\t" << i.size() << " éléments"; 
} 
  
typedef std::string item; 
int main() { 
	const item cle = "clé à molette"; 
  
	inventory::inventory<item> inventaire; 
	inventaire.add(cle, 4); 
	std::cout << "Inventaire initial:\n" << inventaire << std::endl; 
  
	const int n = 2; 
	std::cout << "Utilisation de " << n << " clés" << std::endl; 
  
	inventaire.remove(cle, n); 
	inventaire.add("morceau d'acier tordu", n); 
	std::cout << "Inventaire:\n" << inventaire << std::endl; 
  
  
	try { 
		std::cout << "Tentative de retirer -2 clés." << std::endl; 
		const bool reussite = inventaire.remove(cle, -2); 
		std::cout << "Ce fut accepté, " << (reussite ? "et réalisé!" : "mais pas réalisé.")<< std::endl; 
	} catch (std::invalid_argument const& e) { 
		std::cerr << "Impossible! D'ailleurs je suis curieux de voir -2 clés posées sur une table." << std::endl; 
	} 
  
	return 0; 
}
Téléchargement
Compatibilité
Linux Mac Windows
2  0 
Détails
Catégories : Codes sources C++
Avatar de ternel
Expert éminent sénior
Voir tous les téléchargements de l'auteur
Licence : Libre
Date de mise en ligne : 21 septembre 2014




Avatar de ternel ternel - Expert éminent sénior https://www.developpez.com
le 23/09/2014 à 15:04
Je vais le faire, mais ça me prendra un peu de temps pour faire quelque chose de propre.

-std=c++0x et -stc=c++11 sont équivalents en ce que 0x servait de projet à la 11.
sur les 4.6.x, c++0x est le seul disponible, et correspond à une partie (croissante avec la version) de la nouvelle norme
Avatar de Flob90 Flob90 - Membre expert https://www.developpez.com
le 23/09/2014 à 17:01
Dans ce goût là :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <map> 

namespace inventory { 
  
template<class,class> 
class inventory;

template<class>
struct item;

template<class Item, class Size>
struct item<inventory<Item,Size>>
{ using type =Item; }

template<class>
struct size;

template<class Item, class Size>
struct size<inventory<Item,Size>>
{ using type =Size; }

template <class Item, class Size> 
class inventory {
private:
	using item_type =typename item<inventory>::type; 
	using size_type =typename size<inventory>::type; 
	using internals_type =std::map<item_type,size_type>; 

	internals_type internals; 
	size_type full_size; 
  
public: 
	using const_iterator =typename internals_type::const_iterator; //Je laisse celui-ci dedans pour raison de compatibilité
  
public: 
	inventory();
	size_type add(item_size const& that, size_type count);
	bool remove(item_size const& that, size_type count);
	size_type size() const;
	size_type distinct() const;
	size_type count(item_size const& that) const; 
	const_iterator begin() const;
	const_iterator end() const;
}; 
  
}
L'idée c'est que quand tu commences à vraiment avoir besoin des traits, c'est bien plus simple de les avoir avec une convention de nommage ::tuype (c'est la convention dans type_trait par exemple). Typiquement ça permet de les utiliser avec boost.mpl.
Avatar de ternel ternel - Expert éminent sénior https://www.developpez.com
le 17/09/2014 à 12:52
Par contre, j'ai un problème pour ajouter le code source, je ne sais pas ce qu'il s'y trouve.
Avatar de ternel ternel - Expert éminent sénior https://www.developpez.com
le 22/09/2014 à 11:12
Merci à Winjerome pour avoir débloqué la situation des codes sources.

N'hésitez pas à donner votre avis, il y a probablement des petites choses à améliorer.
Avatar de PilloBuenaGente PilloBuenaGente - Membre éclairé https://www.developpez.com
le 23/09/2014 à 14:39
Bonjour Leternel,

Cet exemple est interessant et aborde pas mal de concepts.

je suis loin d'être le mieux placé pour apporter conseil.. mais bon, j'me lance :

Peut-être utiliser using au lieu de typedef;

Je ne sais pas si tu souhaites encore faire evoluer ce code, au cas ou :

Une fonction pour tout supprimer(n'apprend pas grand chose certe),
une fonction pour ajouter une liste (template container avec begin et end?!) (aussi en surcharge du constructeur?!)
enfin peut-être quelques opérateurs histoire de pouvoir faire des choses genre :

Code : Sélectionner tout
1
2
3
4
5
6
7
using Invent = inventory::inventory<item>;
const Invent inventaire_de_depart = {{"clee", 3}, {"boite", 1}};

Invent  inventaire_joueur_un(inventaire_de_depart);
const Invent  craft_pioche = {{"clee", 2}}

inventaire_joueur_un -= craft_pioche;
En tout cas bonne initiative ! C'est toujours plus sympa d'apprendre les méchanismes avec un petit code, qu'avec une longue page de théorie.
Avatar de ternel ternel - Expert éminent sénior https://www.developpez.com
le 23/09/2014 à 14:45
C'est vrai, mais comme je n'avais qu'un compilateur d'avant le c++11 (on parlait encore de C++0x), je n'avais ni les initializer-list, et pas forcément des using.

Et il me semble que using ne crée pas un nom.

Je veux dire par là que le code suivant compile
Code : Sélectionner tout
1
2
3
4
5
6
7
struct s {
    typedef int INTEGER;
};
int main() {
    s::INTEGER that = 2;
    return 0;
}
mais celui-ci, j'ai eu des problèmes.
Code : Sélectionner tout
1
2
3
4
5
6
7
struct s {
    using INTEGER = int;
};
int main() {
    s::INTEGER that = 2;
    return 0;
}
Je vais voir ce que je peux rajouter.
Avatar de PilloBuenaGente PilloBuenaGente - Membre éclairé https://www.developpez.com
le 23/09/2014 à 14:51
Citation Envoyé par leternel Voir le message
C'est vrai, mais comme je n'avais qu'un compilateur d'avant le c++11 (on parlait encore de C++0x), je n'avais ni les initializer-list, et pas forcément des using.
OK, je croyais que c++11 et C++0x étaient equivalent (je n'ai pas connu l'avant c++11 ).
Comptes-tu le convertir en c++11 ? Veux tu que j'ebauche les idées de mon précedent message ?
Avatar de Flob90 Flob90 - Membre expert https://www.developpez.com
le 23/09/2014 à 16:14
Bonjour,

Les traits (size_type/item, d'ailleurs item_type serait plus cohérent) j'ai tendance à les préférer en classe de trait extérieur, même si ce n'est pas ce que fait la bibliothèque standard (sauf pour les derniers traits ajoutés) :
Code : Sélectionner tout
1
2
3
4
5
6
7
template<class>
struct item;

template<class Item, class Size>
struct size<inventory<Item,Size>>
{ using type =Item; };
C'est bien plus flexible quand tu as besoin de ces traits pour faire de la méta-programmation et n'handicape pas les autres utilisations (il suffit de réimporter le trait en privé si on veut éviter une réécriture systématique).

Pour moi toutes tes exceptions devraient être des assertions, c'est à l'utilisateur de s'assurer qu'il ne donne pas des valeurs hors contrat (c'est la même chose que operator[] de vector il ne lance pas si tu es hors borne, c'est à toi de t'assurer que tu n'y es pas (j'exclu at de la discussion, cette fonction n'aurait jamais du exister).

Tu aurais pu rajouter un allocateur en paramètre template.
Avatar de ternel ternel - Expert éminent sénior https://www.developpez.com
le 23/09/2014 à 16:43
Pour le coup, Flob90 je ne comprends pas ce que tu suggère avec ton trait externe.

Pour les assertions, je crois qu'il va falloir que je m'y intéresse de plus près.
Quant à l'allocateur, je ne l'ai pas prévu, car je voulais un code à la porté d'un débutant.
Developpez.com décline toute responsabilité quant à l'utilisation des différents éléments téléchargés.