SYSTÈME D'ANNULER-REFAIRE PAR ARBRE (TURS)

cptpingu Messages postés 3838 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 17 juin 2024 - 23 juin 2011 à 18:30
macsou01 Messages postés 45 Date d'inscription mardi 20 mars 2007 Statut Membre Dernière intervention 28 juillet 2011 - 24 juin 2011 à 10:32
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/53316-systeme-d-annuler-refaire-par-arbre-turs

macsou01 Messages postés 45 Date d'inscription mardi 20 mars 2007 Statut Membre Dernière intervention 28 juillet 2011
24 juin 2011 à 10:32
Merci pour ces explications et ces bouts de codes ;).
Je vais mettre en place tout ça bientôt.
cptpingu Messages postés 3838 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 17 juin 2024 124
24 juin 2011 à 00:00
Je te propose aussi une version de ton application principale, qui prend l'entrée standard ou un fichier:

#include "../../src/TURS.hpp"
#include <cstdlib>
#include <vector>
#include <fstream>

void error(int line, const std::string& message)
{
std::cerr << "Ligne " << line << " : " << message << std::endl;
}

int main(int argc, char** argv)
{
typedef std::vector<std::string>::const_iterator iter;
typedef TURS<std::string> TURS_str;
TURS_str turs;
std::string line;
std::vector<std::string> args;
int i = 1;

std::ifstream file;
if (argc > 1)
{
file.open(argv[1]);
if (!file)
{
std::cerr << "Le fichier n'existe pas: " << argv[1] << std::endl;
return 1;
}
}
std::istream& in = (argc > 1) ? file : std::cin;

while (std::getline(in, line))
{
std::istringstream iss(line);

std::string cmd;
iss >> cmd;

while (iss)
{
std::string arg;
iss >> arg;
args.push_back(arg);
}

if (cmd == "action")
{
if(args.size() > 0)
turs.addAction(args[0]);
else
error(i, "Argument manquant pour la commande 'action'");
}
else if (cmd == "undo")
turs.undo();
else if (cmd == "redo")
{
if (args.size() > 0)
turs.redo(atoi(args[0].c_str()));
else
turs.redo();
}
else if (cmd == "print")
{
if (args.size() > 0)
{
for (iter it = args.begin(); it != args.end(); ++it)
{
if (*it == "$tree")
std::cout << turs << " ";
else if (*it == "$currentVal")
std::cout << turs.currentVal() << " ";
else if (*it == "$nl")
std::cout << std::endl;
else
std::cout << *it << " ";
}
std::cout << std::endl;
}
}
else if (cmd == "clear")
turs.clear();
else if (cmd == "mode")
{
if (args.size() > 0)
{
if (args[0] == "tree")
turs.setMode(TURS_str::TREE_MODE);
else if(args[0] == "normal")
turs.setMode(TURS_str::NORMAL_MODE);
else
error(i, "Argument invalide pour la commande 'mode'");
}
else
error(i, "Argument manquant pour la commande 'mode'");
}
else if (cmd == "quit")
break;

args.clear();
i++;
}

return 0;
}
cptpingu Messages postés 3838 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 17 juin 2024 124
23 juin 2011 à 23:32
> - Gérer les sources suivant le modèle .cpp/.hh/.hpp (je n'ai pas bien compris comment faire et sur internet il n'y a pas beaucoup d'exemples clairs)

C'est pas dur:
- Dans le .hh tu mets la définition de la classe.
- Dans le .cpp le code
- Dans le .hpp (qui est inclus à la fin du .hh) tu mets le code inliné ou templaté.
En fait, c'est juste un souci de nomination que tu as, pas de principe.

> Modifier le makefile de l'exemple pour qu'il ne compile pas à chaque fois (idem, j'ai regardé ton makefile (pour le compilateur Pascal) mais comme c'est pour un gros projet, il est un peu complexe, il faudra que j'y regarde de plus près)
Attention, par convention, "Makefile" prend une majuscule.
Soit tu mets -O3 (avec une majuscule à O) qui veut dire optim maximale, soit tu mets -g (qui veut dire, pas d'optim, ajouter des informations de debug). Les deux ne sont pas compatibles (et sont carrément à l'opposé).

Un Makefile tout mini, adapté à ton projet:

# Liste des fichier cpp, ne jamais mettre *.cpp
SRC=main.cpp
# Liste des headers, ils devraient être en .hpp et .hxx
HEADER= ../../../TURS/src/TURS.cpp \
../../../TURS/src/TURS.hpp \
../../../TURS/src/TURSNode.cpp \
../../../TURS/src/TURSNode.hpp
BINDIR=.
ifdef SystemRoot
BINNAME=CLI.exe
else
BINNAME=CLI
endif

TARGET=$(BINDIR)/$(BINNAME)
OBJ=$(SRC:.cpp=.o)
CPP=g++
CXXFLAGS=-W -Wall -Wabi -O3

all: $(TARGET)

$(TARGET): $(OBJ) $(HEADER)
$(CPP) $(CXXFLAGS) $(OBJ) -o $(TARGET)

%.o: %.cpp
$(CPP) -o $@ -c $< $(CFLAGS)
clean:
rm -rf *.o

distclean: clean
rm -rf $(TARGET)
macsou01 Messages postés 45 Date d'inscription mardi 20 mars 2007 Statut Membre Dernière intervention 28 juillet 2011
23 juin 2011 à 20:12
J'ai modifié un peu mon code selon tes conseils. Cependant, il me reste les points suivants à effectuer :
- Gérer les sources suivant le modèle .cpp/.hh/.hpp (je n'ai pas bien compris comment faire et sur internet il n'y a pas beaucoup d'exemples clairs)
- Modifier le makefile de l'exemple pour qu'il ne compile pas à chaque fois (idem, j'ai regardé ton makefile (pour le compilateur Pascal) mais comme c'est pour un gros projet, il est un peu complexe, il faudra que j'y regarde de plus près)
- Vérifier les fuites mémoires (je verrai ça plus tard)
cptpingu Messages postés 3838 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 17 juin 2024 124
23 juin 2011 à 19:00
Pour le makefile, regarde dans mes sources. Tu trouveras un exemple de makefile + configure complet (avec gestion des dépendance via g++ -MM). C'est peut être un peu "overkill" pour ce que tu veux faire, mais ça peut toujours t'intéresser :p
macsou01 Messages postés 45 Date d'inscription mardi 20 mars 2007 Statut Membre Dernière intervention 28 juillet 2011
23 juin 2011 à 18:49
Merci pour toutes ces remarques intéressantes !

En effet par exemple pour la makefile j'avais bien remarqué que ça compilait même sans avoir modifié les fichiers, mais je ne savais pas d'où venait le problème.

Je vais essayer modifier ça rapidement.

Encore merci :)
cptpingu Messages postés 3838 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 17 juin 2024 124
23 juin 2011 à 18:30
C'est un code très intéressant et propre. C'est assez rare !
(Pas de using namespace, pas de mélange C et C++, utilisation de la STL, des templates, documentation, etc... Ça fait plaisir !).
C'est sympa aussi d'avoir une documentation. C'est encore plus sympa de l'avoir fait en Doxygen !

Au sujet du comportement du programme, il n'y a rien à redire, c'est pratique et aisément utilisable en tant que bibliothèque.

Y a des petits trucs qui me gênent un peu, mais ce n'est pas très grave:
- Inclusion de .cpp dans un .hpp. Pour séparer un code de sa définition, je conseille généralement le couple: .cc/.hh/.hxx ou .cpp/.hh/.hpp
- std::string TURSNode<T>::toString => Plutôt qu'un "std::string ret;" j'utiliserais un std::ostringstream (concaténation plus rapide qu'avec un std::string) avec un "return ret.str()".
- Dans les constructeurs, j'utiliserais la liste d'initialisation plutôt que d'affecter les valeurs dans le corps du constructeurs. Ça te permettrait d'avoir des attributs constants.
- Au lieu de addAction(T val), je mettrais plutôt: addAction(const T& val), sinon bonjour les copies en cas de gros objet ! C'est valable pour les autres aussi.
- J'éviterais les NULL au profit de 0, voir: http://0217021.free.fr/portfolio/axel.berardino/articles/null-en-cpp

Au niveau du binaire de démonstration:
- Le makefile est un faux makefile :p. Il ne tient pas compte de ce qui est déjà compilé.
- Tu compiles les *.cpp alors qu'ils sont inclus par les .hpp. Donc théoriquement, ta ligne de compil devrait être: "g++ main.cpp -o CLI"
- Tu ne mets pas de flags de sécurité: "g++ -W -Wall -Wabi -g main.cpp", tu verrais pas mal de warnings ! (Tu compares souvent des entiers signés avec des entiers non signés).
- En release un -o3 serait pas mal non plus :)
- D'après valgrind, tu as des fuites mémoires dans TURS.cpp, ligne 20 et ligne 43. Je t'invite à repasser du valgrind sur ton code.
- Au lieu de faire "char line[]", tu peux mettre un "std::string line", et faire un "while (std::getline(std::cin, line)"
- Ton binaire devrait facultativement prendre en argument un fichier. Pratique pour débugger des fuites mémoires, par exemple :p

J'ai sûrement oublié des choses. Je les ajouterais peut être plus tard.
Rejoignez-nous