Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
43517e33ec | |||
6d3861b718 | |||
60c6686ff6 | |||
0a11b3c4d3 | |||
a964d703fc | |||
477f73d4c4 |
22 changed files with 56 additions and 861 deletions
34
Makefile
34
Makefile
|
@ -3,16 +3,11 @@ CXXFLAGS = --std=c++11
|
|||
RM = rm
|
||||
TAR = tar -cf
|
||||
|
||||
SOURCES = $(wildcard src/*.cpp)
|
||||
OBJETS = $(patsubst %.cpp,%.cpp.o,$(notdir $(SOURCES)))
|
||||
SOURCES = $(wildcard src/*.cpp)
|
||||
OBJETS = $(patsubst %.cpp,%.cpp.o,$(notdir $(SOURCES)))
|
||||
|
||||
EXE = tp5
|
||||
EXE_EXT = out
|
||||
|
||||
PDF_LOCATION = report
|
||||
PDF_FILE = explications.pdf
|
||||
|
||||
ARCHIVE = $(EXE).tar
|
||||
EXE = tp5
|
||||
EXE_EXT = out
|
||||
|
||||
%.cpp.o: src/%.cpp
|
||||
$(CXX) -c -o $@ $< $(CXXFLAGS) $(DEVFLAGS)
|
||||
|
@ -30,19 +25,10 @@ compilation: $(OBJETS)
|
|||
all:
|
||||
main
|
||||
|
||||
pdf-make:
|
||||
cd report && \
|
||||
$(MAKE)
|
||||
clean:
|
||||
$(RM) $(OBJETS) $(EXE).$(EXE_EXT)
|
||||
|
||||
pdf-clean:
|
||||
cd report && \
|
||||
$(MAKE) clean
|
||||
|
||||
clean: pdf-clean
|
||||
-$(RM) $(OBJETS) "$(EXE).$(EXE_EXT)" "$(ARCHIVE)"
|
||||
|
||||
archive: pdf-make
|
||||
cp "$(PDF_LOCATION)/$(PDF_FILE)" .
|
||||
$(TAR) "$(ARCHIVE)" $(SOURCES) $(wildcard includes/*.hpp) Makefile \
|
||||
binome.txt "$(PDF_FILE)" diagramme_uml.png
|
||||
$(RM) "$(PDF_FILE)"
|
||||
archive:
|
||||
$(TAR) "$(EXE).tar" $(SOURCES) $(wildcard includes/*.hpp) Makefile \
|
||||
binome.txt
|
||||
# diagramme_uml.* explications.pdf
|
||||
|
|
108
TODO.md
108
TODO.md
|
@ -2,107 +2,7 @@
|
|||
|
||||
TODO avant rendu :
|
||||
|
||||
- [x] Page rédigée montrant que l'on a tenu compte des problématiques de copies et sécurité (private/public) en expliquant en français non technique comment vous les avez traitées (`x` à pas accès à `y`)
|
||||
- [x] Écrire des tests pour montrer que ça fonctionne
|
||||
|
||||
## Notes
|
||||
|
||||
- [x] Toute classe doit être surchargée avec `<<`
|
||||
- [x] Gestion satisfaisante des copies, affectation, destruction
|
||||
|
||||
### Projet
|
||||
|
||||
- [x] Ne dois **pas** être instanciée directement
|
||||
- [x] Se décompose en tâches (vecteur de tâches)
|
||||
- [x] Durée totale dépend du degré de parallélisme que le gestionnaire décide
|
||||
- [x] Deux natures de projets dépendantent de `Projet`
|
||||
- En cours d'élaboration (`ProtoProjet`)
|
||||
- En cours d'exécution (`RunProjet`)
|
||||
- [x] Peut être vu comme un graphe acyclique (utilisation de `vector`) des tâches
|
||||
- Les sommets sont des tâches
|
||||
- Une tâche `fin` servira de source au graphe
|
||||
- Garder un ordre topologique (triée par dépendances)
|
||||
- [x] Mère des deux classes `ProtoProjet` et `RunProjet`
|
||||
- [x] Méthodes utiles qu'à ses sous-classes
|
||||
- Méthodes (cf. le PDF du prof)
|
||||
- [x] `pick_two_random_tasks()`
|
||||
- [x] `contains()`
|
||||
- [x] Surcharge de `<<` : afficher toutes les tâches
|
||||
- [x] `consult_tasks()`
|
||||
- [x] `topologicalSort()`
|
||||
|
||||
#### ProtoProjet
|
||||
|
||||
- Permet de pouvoir ajouter de nouvelles tâches
|
||||
- Ne peut pas progresser (a.k.a ne peut pas lancer les tâches)
|
||||
- Champs
|
||||
- [x] Tâche `début`
|
||||
- [x] Tâche `fin`
|
||||
- Méthodes (cf. le PDF du prof) **⇒ Tout ça avec l'ordre topologique**
|
||||
|
||||
- Pas de méthode d'ajout d'un objet `Tache`
|
||||
- [x] `bool ajoute(nom, durée)` : sélectionne au hasard 2 tâches déjà
|
||||
enregistrer et **ajoute** la nouvelle tâche entres-elles
|
||||
- [x] `bool ajoute(nom, durée, id)` : **ajoute** une tâche qui doit se réaliser
|
||||
**après** la tâche qui à l'`id` correspondant (et avant la tâche finale)
|
||||
- [x] `bool ajoute(nom, durée, id1, id2)` : **ajoute** une tâche entre les 2 tâches
|
||||
qui ont l'identifiant `id1` et `id2`
|
||||
- [x] Surcharge de `<<`
|
||||
|
||||
#### RunProjet
|
||||
|
||||
- [x] Construit uniquement via un `ProtoProjet`
|
||||
- [x] Avance vers sa conclusion en prenant en compte des tâches ponctuelles
|
||||
- [x] Vide le contenu d'un `ProtoProjet` pour se construire, rendre les tâches "read-only"
|
||||
- Méthodes (cf. le PDF du prof)
|
||||
- [x] `run(id)` : Lance une tâche
|
||||
- [x] `run(sequence_taches)` : Exécute une liste de tâches dans l'ordre donnée
|
||||
- [x] Surcharge de `<<`
|
||||
|
||||
### Concepteur
|
||||
|
||||
L'utilisateur final en somme
|
||||
|
||||
- [x] Interagis avec un `ProtoProjet` pour y mettre des tâches
|
||||
|
||||
### Gestionnaire
|
||||
|
||||
- [x] Étudie/Analyse des `RunProjet`
|
||||
|
||||
- [x] Recommande des ordonnancements pour la suite des exécutions à venir
|
||||
(ordre d'exécutions des tâches)
|
||||
- [x] Calcule la durée totale restante d'un projet
|
||||
- [x] Demande un salaire pour travailler
|
||||
- Méthodes
|
||||
- [x] Surcharge de `<<` : Affiche une facturation
|
||||
- [x] `pair<vector<int>, int> avis(const RunProjet &)` : renvoie l'ordonnancement
|
||||
et la durée totale restante
|
||||
|
||||
#### Consultant
|
||||
|
||||
- [x] Calcule **sans** parallélisation des tâches
|
||||
|
||||
#### Expert
|
||||
|
||||
- [x] Calcule **avec** parallélisation des tâches
|
||||
|
||||
### Tâches
|
||||
|
||||
- [x] Dépendantes, entre-elles
|
||||
- [x] Durée exacte, fixe, propre à elle-même
|
||||
- [x] Ne peut être lancé que si toutes les dépendances sont réalisées
|
||||
- [x] Dépendances = autres tâches
|
||||
- [x] Élément atomique (final)
|
||||
- Champs
|
||||
- [x] Nom
|
||||
- [x] Numéro unique
|
||||
- [x] État (réalisée/en attente)
|
||||
- Réalisée → En attente : **interdit**
|
||||
- En attente → Réalisée : **autorisé**
|
||||
- [x] Vision locale des dépendances (`vector`)
|
||||
- Méthodes (cf. le PDF du prof)
|
||||
- [x] `bool realise()`
|
||||
- [x] `bool depends_from(Tache & x)`
|
||||
- [x] `bool ajouteDependance(Tache & x)`
|
||||
- [x] `int dureeParal()`
|
||||
- [x] Surcharge de `<<`
|
||||
- [ ] "Vous remettrez en plus de votre code une page rédigée montrant que vous
|
||||
avez tenu compte de certaines problématiques de copies/sécurité etc en
|
||||
expliquant en français non technique comment vous les avez traitées"
|
||||
- [ ] Écrire un "écrirez un petit jeu de test, clair"
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
<!-- https://mermaid.live -->
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'neutral'}}%%
|
||||
|
||||
classDiagram
|
||||
%% Classes
|
||||
class Tache {
|
||||
-static_counter_id
|
||||
-nom
|
||||
-duree_total
|
||||
-etat
|
||||
-dependances
|
||||
+unique_id
|
||||
+visite
|
||||
|
||||
+realise()
|
||||
+depends_from(tache)
|
||||
+ajouteDependance(tache)
|
||||
+dureeParal()
|
||||
+PP_postfixe(liste_tache)
|
||||
}
|
||||
|
||||
class Projet {
|
||||
#tache_fin
|
||||
#tache_debut
|
||||
#taches
|
||||
-cleanMarks()*
|
||||
#pick_two_random_tasks()
|
||||
#contains(id ou name)
|
||||
#topologicalSort()
|
||||
+consult_tasks()
|
||||
}
|
||||
class ProtoProjet {
|
||||
-cleanMarks()
|
||||
+ajoute(nom, duree)
|
||||
+ajoute(nom, duree, id)
|
||||
+ajoute(nom, duree, id1, id2)
|
||||
}
|
||||
class RunProjet {
|
||||
+run(id ou liste_tache)
|
||||
}
|
||||
|
||||
class Gestionnaire {
|
||||
-salaire
|
||||
+payer(argent)
|
||||
+avis(RunProjet)*
|
||||
}
|
||||
class Consultant {
|
||||
+avis(RunProjet)
|
||||
}
|
||||
class Expert {
|
||||
+avis(RunProjet)
|
||||
}
|
||||
|
||||
%% Héritage
|
||||
Projet <|-- ProtoProjet
|
||||
ProtoProjet <|-- RunProjet
|
||||
|
||||
Gestionnaire <|-- Consultant
|
||||
Gestionnaire <|-- Expert
|
||||
|
||||
%% Friends (Dependency)
|
||||
%% pour : consult_tasks()
|
||||
Projet ..> Consultant
|
||||
Projet ..> Expert
|
||||
|
||||
%% Composition
|
||||
Tache --* Projet
|
||||
```
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
|
@ -1,9 +1,9 @@
|
|||
#ifndef TP5_CONSULTANT_HPP
|
||||
#define TP5_CONSULTANT_HPP 1
|
||||
|
||||
#include "Gestionnaire.hpp"
|
||||
#include <iostream>
|
||||
|
||||
class Consultant final : public Gestionnaire {
|
||||
class Consultant {
|
||||
friend std::ostream &operator<<(std::ostream &, const Consultant &);
|
||||
|
||||
public:
|
||||
|
@ -12,8 +12,6 @@ public:
|
|||
|
||||
Consultant(const Consultant &); // copy constructor
|
||||
const Consultant &operator=(const Consultant &); // copy assignement
|
||||
|
||||
std::pair<std::vector<int>, int> avis(const RunProjet &projet) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#ifndef TP5_EXPERT_HPP
|
||||
#define TP5_EXPERT_HPP 1
|
||||
|
||||
#include "Gestionnaire.hpp"
|
||||
#include <iostream>
|
||||
|
||||
class Expert final : public Gestionnaire {
|
||||
class Expert {
|
||||
friend std::ostream &operator<<(std::ostream &, const Expert &);
|
||||
|
||||
public:
|
||||
|
@ -12,8 +12,6 @@ public:
|
|||
|
||||
Expert(const Expert &); // copy constructor
|
||||
const Expert &operator=(const Expert &); // copy assignement
|
||||
|
||||
std::pair<std::vector<int>, int> avis(const RunProjet &projet) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,37 +1,17 @@
|
|||
#ifndef TP5_GESTIONNAIRE_HPP
|
||||
#define TP5_GESTIONNAIRE_HPP 1
|
||||
|
||||
#include "RunProjet.hpp"
|
||||
#include <iostream>
|
||||
|
||||
class Gestionnaire {
|
||||
int salaire_attendu;
|
||||
int salaire_recu;
|
||||
|
||||
// Renvoie le reste à payer pour donner une expertise
|
||||
int reste_a_payer() const;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &, const Gestionnaire &);
|
||||
|
||||
protected:
|
||||
// Auxiliaire pour simplifier l'affichage d'un projet
|
||||
std::ostream &print(std::ostream &) const;
|
||||
|
||||
// Vrai si Gestionnaire à été payé.e
|
||||
bool payer() const;
|
||||
|
||||
public:
|
||||
Gestionnaire(); // constructor
|
||||
virtual ~Gestionnaire(); // destructor
|
||||
|
||||
Gestionnaire(const Gestionnaire &); // copy constructor
|
||||
const Gestionnaire &operator=(const Gestionnaire &); // copy assignement
|
||||
|
||||
// Paye Gestionnaire, renvoie le montant restant à payer
|
||||
int payer(const int argent);
|
||||
|
||||
// Renvoie l'ordonnancement et la durée totale restante
|
||||
// Renvoie une liste vide et une durée totale -1 si pas payé.e
|
||||
virtual std::pair<std::vector<int>, int> avis(const RunProjet &) const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,58 +1,17 @@
|
|||
#ifndef TP5_PROJET_HPP
|
||||
#define TP5_PROJET_HPP 1
|
||||
|
||||
#include "Tache.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
struct RunProjet;
|
||||
#include <iostream>
|
||||
|
||||
class Projet {
|
||||
// Auxilliaire pour simplifier les copies
|
||||
void _copy(const Projet &);
|
||||
|
||||
// Auxiliiaire pour simplifier la libération de mémoire des tâches
|
||||
void free_taches();
|
||||
|
||||
// Remet tous les marquages à leur valeur initiale
|
||||
virtual void cleanMarks() const = 0;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &, const Projet &);
|
||||
friend RunProjet;
|
||||
|
||||
protected:
|
||||
Tache
|
||||
// Source du graphe aka la dernière à être exécutée
|
||||
*fin,
|
||||
|
||||
// Première tâche qui sera exécutée
|
||||
*debut;
|
||||
|
||||
public:
|
||||
Projet(); // constructor
|
||||
virtual ~Projet(); // destructor
|
||||
|
||||
Projet(const Projet &); // copy constructor
|
||||
const Projet &operator=(const Projet &); // copy assignement
|
||||
|
||||
// Graphe acyclique
|
||||
std::vector<Tache *> taches;
|
||||
|
||||
// Auxiliaire pour simplifier l'affichage d'un projet
|
||||
std::ostream &print(std::ostream &) const;
|
||||
|
||||
// Retourne une paire d'indentifiants de tâches au hasard
|
||||
const std::pair<const int, const int> pick_two_random_tasks() const;
|
||||
|
||||
// Indique pour une tâche si elle fait partie du projet
|
||||
Tache *contains(const int id) const;
|
||||
Tache *contains(const std::string name) const;
|
||||
|
||||
// Corrige les éventuelles anomalies du vector de tâches
|
||||
void topologicalSort();
|
||||
|
||||
public:
|
||||
// Donne une version du vecteur de tâches non modifiable
|
||||
const std::vector<const Tache *> consult_tasks() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
#include "Projet.hpp"
|
||||
|
||||
class ProtoProjet : public Projet {
|
||||
void cleanMarks() const;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &, const ProtoProjet &);
|
||||
|
||||
public:
|
||||
|
@ -14,18 +12,6 @@ public:
|
|||
|
||||
ProtoProjet(const ProtoProjet &); // copy constructor
|
||||
const ProtoProjet &operator=(const ProtoProjet &); // copy assignement
|
||||
|
||||
// Insère une nouvelle tâche en la plaçant entre deux tâches au hasard
|
||||
bool ajoute(const std::string nom, const int duree);
|
||||
|
||||
// Insère une nouvelle tâche en la plaçant après la tâche avec l'ID
|
||||
// renseignée et avant la tâche finale, en créant une dépendance
|
||||
bool ajoute(const std::string nom, const int duree, const int id);
|
||||
|
||||
// Insère une nouvelle tâche en la plaçant entre les deux tâches renseignée
|
||||
// via ID
|
||||
bool ajoute(const std::string nom, const int duree, const int id1,
|
||||
const int id2);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,29 +3,15 @@
|
|||
|
||||
#include "ProtoProjet.hpp"
|
||||
|
||||
struct Consultant;
|
||||
struct Expert;
|
||||
|
||||
class RunProjet final : protected ProtoProjet {
|
||||
class RunProjet : public ProtoProjet {
|
||||
friend std::ostream &operator<<(std::ostream &, const RunProjet &);
|
||||
|
||||
// Ami pour pouvoir consulter les tâches
|
||||
friend Consultant;
|
||||
friend Expert;
|
||||
|
||||
public:
|
||||
RunProjet() = delete; // remove default constructor
|
||||
RunProjet(ProtoProjet &); // constructor
|
||||
virtual ~RunProjet(); // destructor
|
||||
RunProjet(); // constructor
|
||||
virtual ~RunProjet(); // destructor
|
||||
|
||||
RunProjet(const RunProjet &); // copy constructor
|
||||
const RunProjet &operator=(const RunProjet &); // copy assignement
|
||||
|
||||
// Lance une tâche
|
||||
bool run(const int id) const;
|
||||
|
||||
// Exécute une liste de tâches dans l'ordre donnée
|
||||
void run(const std::vector<const Tache *> sequence_taches) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
class Tache final {
|
||||
class Tache {
|
||||
// Compteur global pour les IDs
|
||||
static int counter_id;
|
||||
|
||||
|
@ -12,8 +12,9 @@ class Tache final {
|
|||
enum Etat { EnAttente, Realisee };
|
||||
friend std::ostream &operator<<(std::ostream &, const Etat &);
|
||||
|
||||
// Nom de la tâche
|
||||
std::string name;
|
||||
// ID unique de la tâche
|
||||
const int unique_id;
|
||||
|
||||
// Durée totale pour faire la tâche
|
||||
int duree_total;
|
||||
// Etat actuelle de la tâche
|
||||
|
@ -27,13 +28,8 @@ class Tache final {
|
|||
void _copy(const Tache &);
|
||||
|
||||
public:
|
||||
// ID unique de la tâche
|
||||
const int unique_id;
|
||||
// Vrai si la tâche à été visitée par le parcours en profondeur
|
||||
bool visite;
|
||||
|
||||
Tache(const std::string, const int); // constructor
|
||||
virtual ~Tache(); // destructor
|
||||
Tache(const int); // constructor
|
||||
virtual ~Tache(); // destructor
|
||||
|
||||
Tache(const Tache &); // copy constructor
|
||||
const Tache &operator=(const Tache &); // copy assignement
|
||||
|
@ -49,15 +45,6 @@ public:
|
|||
|
||||
// Calcule la durée totale max de réalisation d'une tâche et des dépendances
|
||||
int dureeParal() const;
|
||||
|
||||
// Parcours en profondeur
|
||||
void PP_postfixe(std::vector<Tache *> &);
|
||||
|
||||
// Récupère la durée totale
|
||||
int get_duree_totale() const;
|
||||
|
||||
// Récupère le nom
|
||||
std::string get_name() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
5
report/.gitignore
vendored
5
report/.gitignore
vendored
|
@ -1,5 +0,0 @@
|
|||
*
|
||||
|
||||
!.gitignore
|
||||
!Makefile
|
||||
!*.tex
|
|
@ -1,16 +0,0 @@
|
|||
NAME = explications
|
||||
|
||||
TEX = $(NAME).tex
|
||||
SRC = $(TEX)
|
||||
PDF = $(TEX:.tex=.pdf)
|
||||
|
||||
TEXMK = latexmk -shell-escape -lualatex
|
||||
|
||||
all: $(PDF)
|
||||
|
||||
$(PDF): %.pdf: %.tex
|
||||
$(TEXMK) $<
|
||||
|
||||
EXTS = aux fdb_latexmk fls log nav out snm synctex.gz toc
|
||||
clean:
|
||||
rm -rf $(PDF) _minted-$(NAME)/ $(foreach ext,$(EXTS),$(NAME).$(ext))
|
|
@ -1,49 +0,0 @@
|
|||
\documentclass{article}
|
||||
|
||||
\usepackage[T1]{fontenc} % encoding
|
||||
\renewcommand{\familydefault}{\sfdefault} % sans-serif font
|
||||
|
||||
\usepackage[french]{babel} % langages
|
||||
\frenchsetup{SmallCapsFigTabCaptions=false}
|
||||
|
||||
\usepackage[hidelinks]{hyperref} % clickable links in table of contents
|
||||
|
||||
\usepackage{graphicx} % images
|
||||
|
||||
\newcommand{\emma}{Emma Botti}
|
||||
\newcommand{\anri}{Anri Kennel}
|
||||
|
||||
\newcommand{\pp}{\texttt{ProtoProjet}}
|
||||
\newcommand{\rp}{\texttt{RunProjet}}
|
||||
|
||||
\title{Explications TP5}
|
||||
\author{\emma\thanks{\emma : 00000000} et \anri\thanks{\anri : 22302653} \\
|
||||
Langages à objet avancés $\cdot$ Université Paris Cité}
|
||||
\date{Année universitaire 2023-2024}
|
||||
|
||||
\begin{document}
|
||||
\maketitle
|
||||
\tableofcontents
|
||||
\clearpage
|
||||
|
||||
\section{Explications}
|
||||
\subsection{Problématique de copies}
|
||||
La liste des tâches est créée lors du \pp, et vider de la mémoire
|
||||
lorsque \rp~disparaît. Lors du passage des tâches entre \pp~et \rp, il n'y a pas de copie.
|
||||
|
||||
\subsection{Problématique de sécurité}
|
||||
Les variables sont toutes encapsulées dans des classes qui leur correspondent.
|
||||
Les variables, quand elles n'ont pas à être exposé, sont soit cachés,
|
||||
soit immuable.
|
||||
|
||||
\section{Diagramme UML}
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\includegraphics[width=1.3\textwidth]{../diagramme_uml.png}
|
||||
\caption{Diagramme UML}
|
||||
\end{figure}
|
||||
|
||||
|
||||
|
||||
\end{document}
|
|
@ -1,10 +1,10 @@
|
|||
#include "../includes/Consultant.hpp"
|
||||
|
||||
Consultant::Consultant() {}
|
||||
Consultant::Consultant() { std::cout << "Hello, Consultant!\n"; }
|
||||
|
||||
Consultant::~Consultant() {}
|
||||
|
||||
Consultant::Consultant(const Consultant &) : Gestionnaire() {}
|
||||
Consultant::Consultant(const Consultant &) {}
|
||||
|
||||
const Consultant &Consultant::operator=(const Consultant &src) {
|
||||
if (this == &src) {
|
||||
|
@ -13,27 +13,3 @@ const Consultant &Consultant::operator=(const Consultant &src) {
|
|||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Consultant &data) {
|
||||
return data.print(out);
|
||||
}
|
||||
|
||||
std::pair<std::vector<int>, int>
|
||||
Consultant::avis(const RunProjet &projet) const {
|
||||
std::vector<int> ordonnancement;
|
||||
if (!payer()) {
|
||||
return std::make_pair(ordonnancement, -1);
|
||||
}
|
||||
|
||||
const std::vector<const Tache *> taches = projet.consult_tasks();
|
||||
int duree_totale = 0;
|
||||
for (const Tache *it : taches) {
|
||||
duree_totale += it->get_duree_totale();
|
||||
}
|
||||
|
||||
for (const Tache *it : taches) {
|
||||
ordonnancement.push_back(it->unique_id);
|
||||
}
|
||||
|
||||
return std::make_pair(ordonnancement, duree_totale);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#include "../includes/Expert.hpp"
|
||||
|
||||
Expert::Expert() {}
|
||||
Expert::Expert() { std::cout << "Hello, Expert!\n"; }
|
||||
|
||||
Expert::~Expert() {}
|
||||
|
||||
Expert::Expert(const Expert &) : Gestionnaire() {}
|
||||
Expert::Expert(const Expert &) {}
|
||||
|
||||
const Expert &Expert::operator=(const Expert &src) {
|
||||
if (this == &src) {
|
||||
|
@ -13,23 +13,3 @@ const Expert &Expert::operator=(const Expert &src) {
|
|||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Expert &data) {
|
||||
return data.print(out);
|
||||
}
|
||||
|
||||
std::pair<std::vector<int>, int> Expert::avis(const RunProjet &projet) const {
|
||||
std::vector<int> ordonnancement;
|
||||
if (!payer()) {
|
||||
return std::make_pair(ordonnancement, -1);
|
||||
}
|
||||
|
||||
const std::vector<const Tache *> taches = projet.consult_tasks();
|
||||
const int duree_totale = taches.front()->dureeParal();
|
||||
|
||||
for (const Tache *it : taches) {
|
||||
ordonnancement.push_back(it->unique_id);
|
||||
}
|
||||
|
||||
return std::make_pair(ordonnancement, duree_totale);
|
||||
}
|
||||
|
|
|
@ -1,59 +1,15 @@
|
|||
#include "../includes/Gestionnaire.hpp"
|
||||
|
||||
Gestionnaire::Gestionnaire() {
|
||||
salaire_attendu = rand() % 500 + 400; // Entre 400 et 899
|
||||
salaire_recu = 0;
|
||||
}
|
||||
Gestionnaire::Gestionnaire() { std::cout << "Hello, Gestionnaire!\n"; }
|
||||
|
||||
Gestionnaire::~Gestionnaire() {}
|
||||
|
||||
Gestionnaire::Gestionnaire(const Gestionnaire &src)
|
||||
: salaire_attendu(src.salaire_attendu), salaire_recu(0) {}
|
||||
Gestionnaire::Gestionnaire(const Gestionnaire &) {}
|
||||
|
||||
const Gestionnaire &Gestionnaire::operator=(const Gestionnaire &src) {
|
||||
if (this == &src) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
salaire_attendu = src.salaire_attendu;
|
||||
salaire_recu = src.salaire_recu;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::ostream &Gestionnaire::print(std::ostream &out) const {
|
||||
out << "La facture s'élève à " << salaire_attendu << "€, il reste "
|
||||
<< reste_a_payer() << "€ à payer";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Gestionnaire &data) {
|
||||
return data.print(out);
|
||||
}
|
||||
|
||||
int Gestionnaire::reste_a_payer() const {
|
||||
return salaire_attendu - salaire_recu;
|
||||
}
|
||||
|
||||
bool Gestionnaire::payer() const {
|
||||
bool return_val = salaire_recu >= salaire_attendu;
|
||||
|
||||
if (!return_val) {
|
||||
std::cerr << "Je n'ai pas été payé. " << *this << ".\n";
|
||||
}
|
||||
|
||||
return return_val;
|
||||
}
|
||||
|
||||
int Gestionnaire::payer(const int argent) {
|
||||
if (reste_a_payer() <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
salaire_recu += argent;
|
||||
if (reste_a_payer() <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return reste_a_payer();
|
||||
}
|
||||
|
|
110
src/Projet.cpp
110
src/Projet.cpp
|
@ -1,117 +1,15 @@
|
|||
#include "../includes/Projet.hpp"
|
||||
|
||||
Projet::Projet() : fin(new Tache("Fin", 0)), debut(new Tache("Début", 0)) {
|
||||
fin->ajouteDependance(*debut);
|
||||
Projet::Projet() { std::cout << "Hello, project!\n"; }
|
||||
|
||||
taches.push_back(fin);
|
||||
taches.push_back(debut);
|
||||
}
|
||||
Projet::~Projet() {}
|
||||
|
||||
Projet::~Projet() { free_taches(); }
|
||||
|
||||
void Projet::_copy(const Projet &src) {
|
||||
// Copie des tâches
|
||||
taches.reserve(src.taches.size());
|
||||
for (Tache *t : src.taches) {
|
||||
taches.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
void Projet::free_taches() { taches.clear(); }
|
||||
|
||||
Projet::Projet(const Projet &src) : fin(src.fin), debut(src.debut) {
|
||||
_copy(src);
|
||||
}
|
||||
Projet::Projet(const Projet &) {}
|
||||
|
||||
const Projet &Projet::operator=(const Projet &src) {
|
||||
if (this == &src) {
|
||||
return *this;
|
||||
}
|
||||
fin = src.fin;
|
||||
debut = src.debut;
|
||||
free_taches();
|
||||
_copy(src);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::ostream &Projet::print(std::ostream &out) const {
|
||||
// Liste vide
|
||||
if (taches.empty()) {
|
||||
out << "[]";
|
||||
return out;
|
||||
}
|
||||
|
||||
out << "[\n";
|
||||
for (Tache *t : taches) {
|
||||
out << " " << *t << ",\n";
|
||||
}
|
||||
out << ']';
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Projet &data) {
|
||||
return data.print(out);
|
||||
}
|
||||
|
||||
const std::pair<const int, const int> Projet::pick_two_random_tasks() const {
|
||||
// Choix aléatoire d'une tâche
|
||||
size_t size = this->taches.size();
|
||||
size_t rand1 = static_cast<size_t>(rand()) % size;
|
||||
size_t rand2 = static_cast<size_t>(rand()) % size;
|
||||
|
||||
Tache *tache1 = this->taches[rand1];
|
||||
Tache *tache2 = this->taches[rand2];
|
||||
|
||||
// tache2 ne doit pas dépendre transitivement de tache1
|
||||
if (tache2->depends_from(*tache1)) {
|
||||
return pick_two_random_tasks();
|
||||
}
|
||||
|
||||
int id1 = tache1->unique_id;
|
||||
int id2 = tache2->unique_id;
|
||||
return std::make_pair(id1, id2);
|
||||
}
|
||||
|
||||
Tache *Projet::contains(const int id) const {
|
||||
for (Tache *t : this->taches) {
|
||||
if (id == t->unique_id) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Tache *Projet::contains(const std::string name) const {
|
||||
for (Tache *t : this->taches) {
|
||||
if (name == t->get_name()) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<const Tache *> Projet::consult_tasks() const {
|
||||
return std::vector<const Tache *>(taches.begin(), taches.end());
|
||||
}
|
||||
|
||||
void Projet::topologicalSort() {
|
||||
// Construction de la pile
|
||||
std::vector<Tache *> pile;
|
||||
for (Tache *it : taches) {
|
||||
if (!it->visite) {
|
||||
it->PP_postfixe(pile);
|
||||
}
|
||||
}
|
||||
|
||||
// Nettoyage des marques
|
||||
cleanMarks();
|
||||
|
||||
// Reverse de la pile
|
||||
std::reverse(pile.begin(), pile.end());
|
||||
|
||||
// Modifie la liste des tâches topologiquement triée
|
||||
taches = pile;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#include "../includes/ProtoProjet.hpp"
|
||||
|
||||
ProtoProjet::ProtoProjet() {}
|
||||
ProtoProjet::ProtoProjet() { std::cout << "Hello, protoProject!\n"; }
|
||||
|
||||
ProtoProjet::~ProtoProjet() {}
|
||||
|
||||
ProtoProjet::ProtoProjet(const ProtoProjet &src) : Projet(src) {}
|
||||
ProtoProjet::ProtoProjet(const ProtoProjet &) : Projet() {}
|
||||
|
||||
const ProtoProjet &ProtoProjet::operator=(const ProtoProjet &src) {
|
||||
if (this == &src) {
|
||||
|
@ -13,50 +13,3 @@ const ProtoProjet &ProtoProjet::operator=(const ProtoProjet &src) {
|
|||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ProtoProjet &data) {
|
||||
return data.print(out);
|
||||
}
|
||||
|
||||
void ProtoProjet::cleanMarks() const {
|
||||
for (auto t : taches) {
|
||||
t->visite = false;
|
||||
}
|
||||
};
|
||||
|
||||
bool ProtoProjet::ajoute(const std::string nom, const int duree) {
|
||||
// Sélection de 2 tâches au hasard
|
||||
auto tasks = pick_two_random_tasks();
|
||||
|
||||
// Ajout des tâches
|
||||
return ajoute(nom, duree, tasks.first, tasks.second);
|
||||
}
|
||||
|
||||
bool ProtoProjet::ajoute(const std::string nom, const int duree, const int id) {
|
||||
// Ajout de la tâche entre l'ID voulu et la tâche finale
|
||||
return ajoute(nom, duree, id, fin->unique_id);
|
||||
}
|
||||
|
||||
bool ProtoProjet::ajoute(const std::string nom, const int duree, const int id1,
|
||||
const int id2) {
|
||||
Tache *tache1 = contains(id1);
|
||||
Tache *tache2 = contains(id2);
|
||||
|
||||
// Vérifie que les tâches avec les id1 et id2 existent bien dans la liste
|
||||
if (!tache1 || !tache2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Création des dépendances
|
||||
Tache *tache = new Tache(nom, duree);
|
||||
if (!tache1->ajouteDependance(*tache) && !tache->ajouteDependance(*tache2)) {
|
||||
// Problème de dépendance
|
||||
delete tache;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mise à jour du vecteur avec tri topologique
|
||||
taches.push_back(tache);
|
||||
topologicalSort();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
#include "../includes/RunProjet.hpp"
|
||||
|
||||
RunProjet::RunProjet(ProtoProjet &src) : ProtoProjet(src) { src.free_taches(); }
|
||||
RunProjet::RunProjet() { std::cout << "Hello, runProject!\n"; }
|
||||
|
||||
RunProjet::~RunProjet() {
|
||||
// Ici on veut supprimes les tâches
|
||||
for (Tache *it : taches) {
|
||||
delete it;
|
||||
}
|
||||
}
|
||||
RunProjet::~RunProjet() {}
|
||||
|
||||
RunProjet::RunProjet(const RunProjet &src) : ProtoProjet(src) {}
|
||||
RunProjet::RunProjet(const RunProjet &) : ProtoProjet() {}
|
||||
|
||||
const RunProjet &RunProjet::operator=(const RunProjet &src) {
|
||||
if (this == &src) {
|
||||
|
@ -18,24 +13,3 @@ const RunProjet &RunProjet::operator=(const RunProjet &src) {
|
|||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const RunProjet &data) {
|
||||
return data.print(out);
|
||||
}
|
||||
|
||||
bool RunProjet::run(const int id) const {
|
||||
Tache *tache = contains(id);
|
||||
// La tâche n'existe pas
|
||||
if (!tache) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Renvoie si la tâche s'est réalisée
|
||||
return tache->realise();
|
||||
}
|
||||
|
||||
void RunProjet::run(const std::vector<const Tache *> sequence_taches) const {
|
||||
for (const Tache *it : sequence_taches) {
|
||||
run(it->unique_id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
int Tache::counter_id = 0;
|
||||
|
||||
Tache::Tache(const std::string n, const int duree)
|
||||
: name(n), duree_total(duree), etat(EnAttente),
|
||||
dependances(std::vector<Tache *>()), unique_id(counter_id++),
|
||||
visite(false) {}
|
||||
Tache::Tache(const int duree)
|
||||
: unique_id(++counter_id), duree_total(duree), etat(EnAttente),
|
||||
dependances(std::vector<Tache *>()) {}
|
||||
|
||||
Tache::~Tache() {}
|
||||
|
||||
|
@ -18,8 +17,7 @@ void Tache::_copy(const Tache &src) {
|
|||
}
|
||||
|
||||
Tache::Tache(const Tache &src)
|
||||
: name(src.name), duree_total(src.duree_total), etat(src.etat),
|
||||
unique_id(counter_id++), visite(src.visite) {
|
||||
: unique_id(++counter_id), duree_total(src.duree_total), etat(src.etat) {
|
||||
_copy(src);
|
||||
}
|
||||
|
||||
|
@ -27,10 +25,8 @@ const Tache &Tache::operator=(const Tache &src) {
|
|||
if (this == &src) {
|
||||
return *this;
|
||||
}
|
||||
name = src.name;
|
||||
duree_total = src.duree_total;
|
||||
etat = src.etat;
|
||||
visite = src.visite;
|
||||
_copy(src);
|
||||
return *this;
|
||||
}
|
||||
|
@ -38,7 +34,7 @@ const Tache &Tache::operator=(const Tache &src) {
|
|||
std::ostream &operator<<(std::ostream &out, const Tache::Etat &data) {
|
||||
switch (data) {
|
||||
case Tache::EnAttente:
|
||||
out << "Attend";
|
||||
out << "En Attente";
|
||||
break;
|
||||
|
||||
case Tache::Realisee:
|
||||
|
@ -49,8 +45,8 @@ std::ostream &operator<<(std::ostream &out, const Tache::Etat &data) {
|
|||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Tache &data) {
|
||||
out << "Tâche(#" << data.unique_id << ", \"" << data.name << "\", "
|
||||
<< data.etat << ")\t=> " << data.dependances.size() << " dépendances";
|
||||
out << "Tâche #" << data.unique_id << " " << data.etat << " avec "
|
||||
<< data.dependances.size() << " dépendances";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -63,11 +59,9 @@ bool Tache::realise() {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Faire une vérification si le temps est écoulée ?
|
||||
// Dans ce cas là faut stocker le temps d'exécution actuelle de la tâche ?
|
||||
|
||||
// TODO: Ca veux dire quoi déclencher une tâche ?
|
||||
etat = Realisee;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Tache::depends_from(const Tache &x) const {
|
||||
|
@ -88,7 +82,7 @@ bool Tache::depends_from(const Tache &x) const {
|
|||
bool Tache::ajouteDependance(Tache &x) {
|
||||
// On ne créer pas de dépendances cyclique aka 2 tâches
|
||||
// qui dépendent l'une de l'autre
|
||||
if (x.depends_from(*this)) {
|
||||
if (depends_from(x)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -115,19 +109,3 @@ int Tache::dureeParal() const {
|
|||
// On ajoute la valeur max à notre durée totale
|
||||
return ret_value + max_duree;
|
||||
}
|
||||
|
||||
void Tache::PP_postfixe(std::vector<Tache *> &pile) {
|
||||
visite = true;
|
||||
for (Tache *it : dependances) {
|
||||
if (!it->visite) {
|
||||
it->PP_postfixe(pile);
|
||||
}
|
||||
}
|
||||
|
||||
// Moment post-fix du parcours
|
||||
pile.push_back(this);
|
||||
}
|
||||
|
||||
int Tache::get_duree_totale() const { return duree_total; }
|
||||
|
||||
std::string Tache::get_name() const { return name; }
|
||||
|
|
162
src/main.cpp
162
src/main.cpp
|
@ -1,161 +1 @@
|
|||
#include "../includes/Consultant.hpp"
|
||||
#include "../includes/Expert.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Fait des tests pour vérifier/montrer que ça fonctionne
|
||||
void test();
|
||||
|
||||
// Transforme un vecteur<int> en string
|
||||
std::string vecint_tostring(std::vector<int> list);
|
||||
|
||||
int main() {
|
||||
/* test();
|
||||
return EXIT_SUCCESS; */
|
||||
|
||||
ProtoProjet pp;
|
||||
|
||||
pp.ajoute("a", 10); // probablement numero 2
|
||||
cout << pp; // avec ses 3 taches
|
||||
cout << "\n----------" << endl;
|
||||
|
||||
pp.ajoute("b", 15, 0, 1); // en supposant que 0: fin et 1: début
|
||||
cout << pp;
|
||||
cout << "\n----------" << endl;
|
||||
|
||||
RunProjet rp{pp};
|
||||
cout << "----- verification : ProtoProjet vide " << endl;
|
||||
cout << pp << endl;
|
||||
|
||||
Consultant c;
|
||||
c.payer(1000);
|
||||
cout << c.avis(rp).second << endl; // dira 25
|
||||
|
||||
Expert e;
|
||||
e.payer(1000);
|
||||
cout << e.avis(rp).second << endl; // dira 15
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void test() {
|
||||
/********************************** Projet **********************************/
|
||||
// Projet p; // impossible car classe abstraite
|
||||
|
||||
/******************************* ProtoProjet *******************************/
|
||||
ProtoProjet pp;
|
||||
cout << "pp: " << pp << "\n";
|
||||
/* pp: [
|
||||
* Tâche(#0, "Fin", Attend) => 1 dépendances,
|
||||
* Tâche(#1, "Début", Attend) => 0 dépendances,
|
||||
* ] */
|
||||
|
||||
pp.ajoute("a", 10); // ajoute une dépendance à #1 le "début"
|
||||
pp.ajoute("b", 15, 0, 1); // ajoute une dépendance à #0 (id1)
|
||||
cout << "pp: " << pp << "\n";
|
||||
/* pp: [
|
||||
* Tâche(#0, "Fin", Attend) => 2 dépendances,
|
||||
* Tâche(#3, "b", Attend) => 0 dépendances,
|
||||
* Tâche(#1, "Début", Attend) => 1 dépendances,
|
||||
* Tâche(#2, "a", Attend) => 0 dépendances,
|
||||
* ]
|
||||
*
|
||||
* => L'ordre pour les dépendances est respecté */
|
||||
|
||||
/******************************** RunProjet ********************************/
|
||||
// RunProjet rp; // impossible car le constructeur vide n'existe pas
|
||||
RunProjet rp(pp);
|
||||
cout << "pp: " << pp << "\n";
|
||||
/* pp: []
|
||||
*
|
||||
* => La liste est bien vidée*/
|
||||
|
||||
cout << "rp: " << rp << "\n";
|
||||
/* rp: [
|
||||
* Tâche(#0, "Fin", Attend) => 2 dépendances,
|
||||
* Tâche(#3, "b", Attend) => 0 dépendances,
|
||||
* Tâche(#1, "Début", Attend) => 1 dépendances,
|
||||
* Tâche(#2, "a", Attend) => 0 dépendances,
|
||||
* ]
|
||||
*
|
||||
* => Les mêmes tâches ont étés transférés à RunProjet (pas de copie vérifiés
|
||||
* par les ID identiques) */
|
||||
|
||||
/******************************** Consultant *******************************/
|
||||
Consultant c;
|
||||
cout << "c: " << c << "\n";
|
||||
/* c: La facture s'élève à 693€, il reste 693€ à payer
|
||||
*
|
||||
* => Affiche la facture */
|
||||
|
||||
auto avis = c.avis(rp);
|
||||
cout << "c(avis): (" << vecint_tostring(avis.first) << ", " << avis.second
|
||||
<< ")\n";
|
||||
/* Je n'ai pas été payé. La facture s'élève à 693€, il reste 693€ à payer.
|
||||
* c(avis): ([], -1)
|
||||
*
|
||||
* => Iel ne donne pas d'avis car pas payé. */
|
||||
|
||||
cout << "c(payer 1000): " << c.payer(1000) << "\n";
|
||||
/* c(payer 1000): 0
|
||||
*
|
||||
* On donne 1000 pour être sûr qu'iel soit content
|
||||
* Il renvoie 0 car iel a plus besoin d'être payé */
|
||||
|
||||
auto avis2 = c.avis(rp);
|
||||
cout << "c(avis): (" << vecint_tostring(avis2.first) << ", " << avis2.second
|
||||
<< ")\n";
|
||||
/* c(avis): ([0, 3, 1, 2], 25)
|
||||
*
|
||||
* => Iel donne la liste d'exécution
|
||||
* et le temps d'exécution sans parallélisation */
|
||||
|
||||
/********************************** Expert *********************************/
|
||||
Expert e;
|
||||
cout << "e: " << e << "\n";
|
||||
/* e: La facture s'élève à x€, il reste x€ à payer
|
||||
*
|
||||
* => Affiche la facture */
|
||||
|
||||
auto avis3 = e.avis(rp);
|
||||
cout << "e(avis): (" << vecint_tostring(avis3.first) << ", " << avis3.second
|
||||
<< ")\n";
|
||||
/* Je n'ai pas été payé. La facture s'élève à y€, il reste y€ à payer.
|
||||
* e(avis): ([], -1)
|
||||
*
|
||||
* => Iel ne donne pas d'avis car pas payé. */
|
||||
|
||||
cout << "e(payer 1000): " << e.payer(1000) << "\n";
|
||||
/* e(payer 1000): 0
|
||||
*
|
||||
* On donne 1000 pour être sûr qu'iel soit content
|
||||
* Il renvoie 0 car iel a plus besoin d'être payé */
|
||||
|
||||
auto avis4 = e.avis(rp);
|
||||
cout << "e(avis): (" << vecint_tostring(avis4.first) << ", " << avis4.second
|
||||
<< ")\n";
|
||||
/* e(avis): ([0, 3, 1, 2], 15)
|
||||
*
|
||||
* => Iel donne la liste d'exécution
|
||||
* et le temps d'exécution avec parallélisation */
|
||||
|
||||
/* A la fin, la mémoire est correctement libérée, testé via Valgrind-3.21.0 :
|
||||
* HEAP SUMMARY:
|
||||
* in use at exit: 0 bytes in 0 blocks
|
||||
* total heap usage: 33 allocs, 33 frees, 75,550 bytes allocated
|
||||
* All heap blocks were freed -- no leaks are possible
|
||||
* ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) */
|
||||
}
|
||||
|
||||
std::string vecint_tostring(std::vector<int> list) {
|
||||
std::string return_value = "[";
|
||||
if (list.empty()) {
|
||||
return return_value + "]";
|
||||
}
|
||||
for (auto it : list) {
|
||||
return_value += std::to_string(it) + ", ";
|
||||
}
|
||||
|
||||
// \b\b pour retirer la dernière virgule
|
||||
return return_value + "\b\b]";
|
||||
}
|
||||
int main(int argc, char const *argv[]) { return 0; }
|
||||
|
|
Reference in a new issue