Visualizzatore algoritmo di ordinamento in C ++ e SDL2 Migliorato
Post originale: Visualizzatore algoritmo di ordinamento in C ++ e SDL2
Ho seguito i consigli che mi hai dato e ho migliorato il mio codice. Ho anche aggiunto altri due algoritmi di ordinamento. Sto principalmente cercando consigli sulla leggibilità del codice (costruito su Windows con cmake e ninja)
Dimostrazione
puntelli a @Zeta per la dimostrazione
main.cpp
#include "Engine.h"
#undef main
int main()
{
try
{
// If the max number is higher than the window width it draws nothing other than a black screen :^)
// Default draw method is line
// Default sorting algorithm is bubble sort
SortVis::Engine visualization(
{ 1024, 768 },
1024,
SortVis::Engine::SortAlgorithm::insertionSort,
SortVis::Engine::DrawMethod::point
);
visualization.run();
}
catch (std::runtime_error& error)
{
std::cerr << error.what() << "\n";
}
}
Engine.h
#ifndef ENGINE_H
#define ENGINE_H
#include "Coord.h"
#include <SDL.h>
#include <vector>
#include <iostream>
namespace SortVis
{
class Engine
{
public:
enum class DrawMethod
{
line,
point
};
enum class SortAlgorithm
{
selectionSort,
insertionSort,
bubbleSort
};
// Random number generation
Engine() = delete;
Engine(Coord windowSize, int maxNumber);
Engine(Coord windowSize, int maxNumber, SortAlgorithm algorithm);
Engine(Coord windowSize, int maxNumber, SortAlgorithm algorithm, DrawMethod method);
Engine(Coord windowSize, int maxNumber, const char* windowTitle);
Engine(Coord windowSize, int maxNumber, const char* windowTitle, SortAlgorithm algorithm);
Engine(Coord windowSize, int maxNumber, const char* windowTitle, SortAlgorithm algorithm, DrawMethod method);
// Load from file
Engine(Coord windowSize, const char* pathToNumbersFile);
Engine(Coord windowSize, const char* pathToNumbersFile, SortAlgorithm algorithm);
Engine(Coord windowSize, const char* pathToNumbersFile, SortAlgorithm algorithm, DrawMethod method);
Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle);
Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle, SortAlgorithm algorithm);
Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle, SortAlgorithm algorithm, DrawMethod method);
~Engine();
void run();
private:
const Coord windowSize;
const SortAlgorithm selectedSortAlgorithm = SortAlgorithm::bubbleSort;
const DrawMethod selectedDrawMethod = DrawMethod::line;
SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
std::vector<int> numbers = { };
int columnWidth = 0;
int maxValue = 0;
bool running = true;
void initWindow(Coord windowSize, const char* windowTitle);
void initRenderer();
void calculateNumbers();
void loadFile(const char* pathToNumbersFile);
void handleEvents();
void draw();
void drawSelection();
void drawColumns();
void drawPoints();
void step();
void stepBubbleSort();
void stepInsertionSort();
void stepSelectionSort();
std::vector<int> generateRandom(int maxNumber);
};
}
#endif // ENGINE_H
Engine.cpp
#include "Engine.h"
#include <filesystem>
#include <fstream>
#include <random>
#include <utility>
#include <algorithm>
#include <numeric>
#include <string>
// --- CONSTRUCTORS --- (fml)
SortVis::Engine::Engine(Coord windowSize, int maxNumber)
: windowSize(windowSize), numbers(generateRandom(maxNumber))
{
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, "Sort visualizer");
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, int maxNumber, SortAlgorithm algorithm)
: windowSize(windowSize), numbers(generateRandom(maxNumber)), selectedSortAlgorithm(algorithm)
{
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, "Sort visualizer");
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, int maxNumber, SortAlgorithm algorithm, DrawMethod method)
: windowSize(windowSize), numbers(generateRandom(maxNumber)),
selectedSortAlgorithm(algorithm), selectedDrawMethod(method)
{
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, "Sort visualizer");
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, int maxNumber, const char* windowTitle)
: windowSize(windowSize), numbers(generateRandom(maxNumber))
{
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, windowTitle);
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, int maxNumber, const char* windowTitle, SortAlgorithm algorithm)
: windowSize(windowSize), numbers(generateRandom(maxNumber)), selectedSortAlgorithm(algorithm)
{
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, windowTitle);
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, int maxNumber, const char* windowTitle, SortAlgorithm algorithm, DrawMethod method)
: windowSize(windowSize), numbers(generateRandom(maxNumber)),
selectedSortAlgorithm(algorithm), selectedDrawMethod(method)
{
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, windowTitle);
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile)
: windowSize(windowSize)
{
if (!std::filesystem::exists(pathToNumbersFile))
{
throw std::runtime_error("That file does not exist. Make sure the path is correct.");
}
else
{
loadFile(pathToNumbersFile);
}
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, "Sort visualizer");
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile, SortAlgorithm algorithm)
: windowSize(windowSize), selectedSortAlgorithm(algorithm)
{
if (!std::filesystem::exists(pathToNumbersFile))
{
throw std::runtime_error("That file does not exist. Make sure the path is correct.");
}
else
{
loadFile(pathToNumbersFile);
}
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, "Sort visualizer");
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile, SortAlgorithm algorithm, DrawMethod method)
: windowSize(windowSize), selectedSortAlgorithm(algorithm), selectedDrawMethod(method)
{
if (!std::filesystem::exists(pathToNumbersFile))
{
throw std::runtime_error("That file does not exist. Make sure the path is correct.");
}
else
{
loadFile(pathToNumbersFile);
}
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, "Sort visualizer");
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle)
: windowSize(windowSize)
{
if (!std::filesystem::exists(pathToNumbersFile))
{
throw std::runtime_error("That file does not exist. Make sure the path is correct.");
}
else
{
loadFile(pathToNumbersFile);
}
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, windowTitle);
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle, SortAlgorithm algorithm)
: windowSize(windowSize), selectedSortAlgorithm(algorithm)
{
if (!std::filesystem::exists(pathToNumbersFile))
{
throw std::runtime_error("That file does not exist. Make sure the path is correct.");
}
else
{
loadFile(pathToNumbersFile);
}
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, windowTitle);
initRenderer();
}
SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle, SortAlgorithm algorithm, DrawMethod method)
: windowSize(windowSize), selectedSortAlgorithm(algorithm), selectedDrawMethod(method)
{
if (!std::filesystem::exists(pathToNumbersFile))
{
throw std::runtime_error("That file does not exist. Make sure the path is correct.");
}
else
{
loadFile(pathToNumbersFile);
}
calculateNumbers();
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Could not initialize SDL");
}
initWindow(windowSize, windowTitle);
initRenderer();
}
// --- END OF CONSTRUCTORS ---
SortVis::Engine::~Engine()
{
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
void SortVis::Engine::run()
{
// Sets render draw color to black
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
while (running)
{
handleEvents();
if (!std::is_sorted(numbers.begin(), numbers.end()))
{
step();
}
draw();
}
}
void SortVis::Engine::step()
{
switch (selectedSortAlgorithm)
{
case SortAlgorithm::bubbleSort:
stepBubbleSort();
break;
case SortAlgorithm::insertionSort:
stepInsertionSort();
break;
case SortAlgorithm::selectionSort:
stepSelectionSort();
break;
default:
break;
}
}
void SortVis::Engine::stepBubbleSort()
{
static int i = 0;
static int size = numbers.size();
for (int j = 0; j < size - i - 1; ++j)
{
if (numbers[j] > numbers[j + 1])
{
std::swap(numbers[j], numbers[j + 1]);
}
}
++i;
}
void SortVis::Engine::stepInsertionSort()
{
static int i = 1;
for (int j = i; j > 0 && numbers[j - 1] > numbers[j]; --j)
{
std::swap(numbers[j - 1], numbers[j]);
}
++i;
}
void SortVis::Engine::stepSelectionSort()
{
static int i = 0;
std::swap(numbers[i], numbers[std::min_element(numbers.begin() + i, numbers.end()) - numbers.begin()]);
++i;
}
void SortVis::Engine::draw()
{
SDL_RenderClear(renderer);
drawSelection();
SDL_RenderPresent(renderer);
}
void SortVis::Engine::drawSelection()
{
switch (selectedDrawMethod)
{
case DrawMethod::line:
drawColumns();
break;
case DrawMethod::point:
drawPoints();
break;
default:
break;
}
}
void SortVis::Engine::drawColumns()
{
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_Rect column;
for (int i = numbers.size(); i > 0; --i)
{
column.x = (i-1) * columnWidth;
column.w = columnWidth;
column.h = (numbers[i - 1] * windowSize.Y) / maxValue;
column.y = windowSize.Y - column.h;
SDL_RenderFillRect(renderer, &column);
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}
void SortVis::Engine::drawPoints()
{
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
// SDL_Point point;
for (int i = numbers.size(); i > 0; --i)
{
// point.x = i - 1;
// point.y = windowSize.Y - ((numbers[i - 1] * windowSize.Y) / maxValue);
SDL_RenderDrawPoint(renderer, i - 1, windowSize.Y - ((numbers[i - 1] * windowSize.Y) / maxValue));
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}
void SortVis::Engine::handleEvents()
{
SDL_Event Event;
while (SDL_PollEvent(&Event))
{
switch (Event.type)
{
case SDL_QUIT:
running = false;
break;
default:
break;
}
}
}
std::vector<int> SortVis::Engine::generateRandom(int maxNumber)
{
std::mt19937 seed(std::random_device{}());
std::vector<int> num(maxNumber);
std::iota(num.begin(), num.end(), 0);
std::shuffle(num.begin(), num.end(), seed);
std::cout << "Generated random number sequence.\n";
return num;
}
void SortVis::Engine::calculateNumbers()
{
columnWidth = windowSize.X / numbers.size();
maxValue = *std::max_element(numbers.begin(), numbers.end());
}
void SortVis::Engine::loadFile(const char* pathToNumbersFile)
{
std::ifstream NumbersFile(pathToNumbersFile);
if (NumbersFile.is_open())
{
std::string Number;
while (std::getline(NumbersFile, Number))
{
numbers.push_back(std::stoi(Number));
}
}
else
{
throw std::runtime_error("Couldn't open numbers file.");
}
if (numbers.empty())
{
throw std::runtime_error("Numbers file is empty.");
}
std::cout << "Loaded numbers file.\n";
}
void SortVis::Engine::initWindow(Coord windowSize, const char* windowTitle)
{
window = SDL_CreateWindow(
windowTitle,
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
windowSize.X,
windowSize.Y,
SDL_WINDOW_SHOWN
);
if (window == nullptr)
{
throw std::runtime_error("Could not initialize SDL window");
}
}
void SortVis::Engine::initRenderer()
{
renderer = SDL_CreateRenderer(
window,
-1,
SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED
);
if (renderer == nullptr)
{
throw std::runtime_error("Could not initialize SDL renderer");
}
}
Coord.h
#ifndef COORD_H
#define COORD_H
namespace SortVis
{
struct Coord
{
int X;
int Y;
};
}
#endif // COORD_H
Risposte
Il programma è decisamente migliorato rispetto alla versione precedente. Bel lavoro! Ecco alcune cose che potrebbero aiutarti a migliorarlo ulteriormente.
Non ripetere te stesso (DRY)
Ci sono undici ripetizioni del costruttore, il che mi sembra un po 'eccessivo, soprattutto perché il codice è quasi identico. Li ridurrei esattamente a uno:
Engine(
Coord windowSize,
std::vector<int>&& numbers,
SortAlgorithm algorithm = SortAlgorithm::bubbleSort,
DrawMethod method = DrawMethod::point,
const char* windowTitle = "Sort visualizer"
);
Fornendo un unico costruttore con parametri predefiniti, le ripetizioni vengono eliminate e la flessibilità è migliorata. Nota anche che l'array di numeri viene passato come argomento.
Riduci le dimensioni dell'interfaccia solo a ciò che è necessario
Queste due funzioni sono generiche e non devono essere membri della classe:
std::vector<int> SortVis::loadFile(std::istream& numberFile);
std::vector<int> SortVis::generateRandom(int maxNumber);
Riducendo le dimensioni dell'interfaccia al minimo necessario, la classe è più piccola e più facile da leggere, comprendere, testare, utilizzare, mantenere e adattare. Nota anche che il primo argomento accetta a std::istream&
invece di un nome di file. Ciò consente cose utili come il caricamento da un socket o da uno stringstream.
Preferisco fallire presto
Se la SDL_InitSubSystem
chiamata nel costruttore fallisce, non ha molto senso continuare. Per questo motivo, la calculateNumbers
chiamata che richiede più tempo dovrebbe probabilmente arrivare dopo piuttosto che prima.
Imposta il colore solo se vuoi disegnare qualcosa
Gli unici posti che SDL_SetRenderDrawColor
dovrebbero essere usati sono appena prima che qualcosa venga effettivamente disegnato sullo schermo. Per questo motivo, può apparire esattamente due volte in questo codice: una volta all'inizio drawSelection
e una volta all'inizio di draw
.
Spostare il lavoro al di fuori degli anelli, se possibile
Invece di ricalcolare tutte e quattro le porzioni di column
ogni volta attraverso il ciclo, è possibile cambiare semplicemente quelle che devono cambiare:
SDL_Rect column{ 0, 0, columnWidth, 0 };
for (const auto n : numbers)
{
column.h = n * windowSize.Y / maxValue;
column.y = windowSize.Y - column.h;
SDL_RenderFillRect(renderer, &column);
column.x += columnWidth;
}
Usa oggetti invece di switch
es
Il codice attualmente contiene questo:
void SortVis::Engine::step()
{
switch (selectedSortAlgorithm)
{
case SortAlgorithm::bubbleSort:
stepBubbleSort();
break;
case SortAlgorithm::insertionSort:
stepInsertionSort();
break;
case SortAlgorithm::selectionSort:
stepSelectionSort();
break;
default:
break;
}
}
Ciò richiede la valutazione di selectedSortAlgorithm
ogni iterazione. Il modo migliore per farlo è creare una classe base virtuale che dimostri l'interfaccia e quindi consentire all'utente di creare una classe derivata con qualsiasi nuovo tipo di ordinamento che preferisce. Ecco un modo per farlo:
using Collection = std::vector<int>;
struct Sorter {
std::string_view name;
virtual bool step(Collection &) = 0;
Sorter(Sorter&) = delete;
Sorter(std::string_view name)
: name{name}
{
}
virtual void reset(Collection& numbers) {
a = original_a = 0;
original_b = numbers.size();
}
virtual ~Sorter() = default;
std::size_t a;
std::size_t original_a;
std::size_t original_b;
};
struct BubbleSorter : public Sorter {
BubbleSorter() : Sorter{"Bubble Sort"} { }
bool step(Collection& numbers) {
auto lag{original_a};
for (auto it{lag + 1}; it < original_b; ++it, ++lag) {
if (numbers[lag] > numbers[it]) {
std::swap(numbers[lag], numbers[it]);
}
}
return ++a != original_b;
}
};
Ora per la massima flessibilità, puoi passare un tale oggetto al Engine
nel suo costruttore:
Engine visualization{
{ 1024, 768 },
generateRandom(1024),
std::make_unique<BubbleSorter>()
};
Usa un puntatore a una funzione membro
Una cosa simile può essere fatta per il metodo di disegno, se lo desideri, ma è un po 'più complicato poiché utilizza un puntatore a una funzione membro che ha una sintassi che può essere difficile da ottenere correttamente. Potremmo dichiarare la variabile all'interno in Engine
questo modo:
void (Engine::*drawSelection)();
Ho riproposto il tuo drawSelection
nome qui. All'interno del costruttore possiamo usare questo:
drawSelection{method == DrawMethod::point ? &Engine::drawPoints : &Engine::drawColumns}
Infine, per chiamarlo, dobbiamo usare l'operatore di accesso pointer-to-member. Eccolo nel contesto:
void SortVis::Engine::draw() {
// Sets render draw color to black
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
(this->*(drawSelection))();
SDL_RenderPresent(renderer);
}
Non fare lavori non necessari
In questo momento, il ciclo principale run
è questo:
while (running)
{
handleEvents();
if (!std::is_sorted(numbers.begin(), numbers.end()))
{
step();
}
draw();
}
Non ha molto senso per me fare una chiamata std::is_sorted
all'interno di un programma che dimostra l'ordinamento! Mi è sembrato di voler chiudere il programma una volta terminato l'ordinamento e ho modificato le routine di ordinamento per tornare bool
con il valore di false
solo quando ha finito di funzionare e il vettore è ordinato. Quindi, per questo, il ciclo si trasforma in questo:
while (running) {
handleEvents();
running &= sorter->step(numbers);
draw();
}
Considera l'aggiunta di funzionalità
Suggerirei che potrebbe essere bello aggiungere alcune funzionalità. Ecco cosa ho fatto:
void SortVis::Engine::handleEvents() {
SDL_Event Event;
while (SDL_PollEvent(&Event)) {
switch (Event.type) {
case SDL_QUIT:
running = false;
break;
case SDL_KEYDOWN:
switch (Event.key.keysym.sym) {
case SDLK_r:
numbers = generateRandom(maxValue);
sorter->reset(numbers);
break;
case SDLK_b:
numbers = generateRandom(maxValue);
sorter = std::move(std::make_unique<BubbleSorter>());
sorter->reset(numbers);
break;
case SDLK_i:
numbers = generateRandom(maxValue);
sorter = std::move(std::make_unique<InsertionSorter>());
sorter->reset(numbers);
break;
case SDLK_s:
numbers = generateRandom(maxValue);
sorter = std::move(std::make_unique<SelectionSorter>());
sorter->reset(numbers);
break;
case SDLK_l:
drawSelection = &Engine::drawColumns;
break;
case SDLK_p:
drawSelection = &Engine::drawPoints;
break;
case SDLK_q:
running = false;
break;
}
default:
break;
}
}
}
Usa namespace SortVis {...}
inEngine.cpp
Puoi evitare di ripetere lo spazio dei nomi in ciascuna definizione di funzione Engine.cpp
avvolgendo tutto il codice all'interno namespace SortVis
, proprio come hai fatto in Engine.h
. È anche comune non indentare il codice all'interno di un namespace {...}
blocco che copre l'intero file, per evitare che il codice scorra troppo spesso dalla mano destra dello schermo.
Troppi costruttori
Hai 12 costruttori diversi, questo è un po 'troppo. Immagina anche di voler aggiungere un altro parametro facoltativo in futuro, quindi raddoppi il numero di costruttori richiesti. Questo non è gestibile a lungo termine. Vedo due modi per ridurre il numero di costruttori richiesti:
Usa i valori degli argomenti predefiniti, in questo modo:
Engine(Coord windowSize, int maxNumber, const char *windowTitle = "Sort visualizer", SortAlgorithm algorithm = SortAlgorithm::bubbleSort, DrawMethod method = DrawMethod::line);
Con questo approccio, sono necessari solo due costruttori. Potrebbe essere leggermente più fastidioso da usare se vuoi solo specificarne un altro
DrawMethod
, ma è un piccolo prezzo per una manutenibilità molto migliorata.Non specificare i valori di input, l'algoritmo e il metodo di disegno nel costruttore, consenti a questi di essere impostati dalle funzioni membro, in questo modo:
Engine(Coord windowSize, const char *windowTitle = "Sort visualizer"); void generateNumbers(int maxNumber); void loadNumbers(const char *pathToNumbersFile); void setAlgorithm(SortAlgorithm algorithm); void setDrawMethod(DrawMethod method);
In generale, fai solo nel costruttore ciò che deve essere fatto veramente al momento della costruzione. L'inizializzazione di SDL e l'apertura di una finestra sono fondamentali per un motore di visualizzazione funzionante, quindi è bene farlo nel costruttore.
Considera l'idea di non generare / caricare numeri in formato class Engine
Invece di Engine
generare numeri casuali o caricarli da un file, puoi semplificarlo non farlo affatto, ma piuttosto consentirgli di utilizzare qualsiasi vettore che gli dai. Quindi per esempio:
void run(const std::vector<int> &input) {
numbers = input;
...
}
Puoi anche considerare di passarlo come non-const e run()
modificare il vettore di input specificato.
Considera l'idea di dividere la visualizzazione dall'ordinamento
Un grosso problema è che devi dividere i tuoi algoritmi di ordinamento in passaggi e avere una switch()
dichiarazione step()
per scegliere l'algoritmo giusto. È inoltre necessario enumerare i possibili algoritmi. Invece di farlo, considera la possibilità di Engine
visualizzare solo un vettore di numeri e invece di Engine
guidare i passaggi dell'algoritmo, fai in modo che un algoritmo Engine
mostri lo stato del vettore ad ogni passaggio. Puoi farlo cambiando Engine::draw()
per prendere un riferimento a un vettore di numeri:
void Engine::draw(const std::vector<int> &numbers) {
...
}
E un algoritmo di ordinamento può diventare una singola funzione:
void bubbleSort(std::vector<int> &numbers, Engine &visualization) {
for (size_t i = 0; i < numbers.size() - 1; ++i) {
for (size_t j = 0; j < numbers.size() - 1; ++j) {
if (numbers[j] > numbers[j + 1])) {
std::swap(numbers[j], numbers[j + 1]);
}
}
visualization.draw(numbers);
}
}
E poi main()
potresti sembrare così:
int main() {
std::vector<int> numbers = {...}; // generate some vector here
SortVis::Engine visualization({1024, 768});
SortVis::bubbleSort(numbers, visualization);
}
I vantaggi di questo approccio sono che separa le preoccupazioni. L' Engine
ora deve solo visualizzare un vettore (probabilmente dovrebbe essere rinominato in qualcosa di simile Visualizer
). Puoi aggiungere facilmente nuovi algoritmi di ordinamento senza doverli modificare Engine
.
Un problema con quanto sopra è che non gestisce più gli eventi SDL. Puoi farlo in draw()
e draw()
restituire un che bool
indica se l'algoritmo deve continuare o meno.
Verificare la presenza di errori dopo aver letto un file, non prima
In Engine::loadFile()
, controlli se il file è aperto correttamente, ma non controlli mai se si è verificato un errore durante la lettura. Un modo possibile è:
std::ifstream NumbersFile(pathToNumbersFile);
std::string Number;
while (std::getline(NumbersFile, Number) {
numbers.push_back(std::stoi(Number));
}
if (!NumbersFile.eof()) {
throw std::runtime_error("Error while reading numbers file.");
}
Qui usiamo il fatto che eofbit
viene impostato solo se ha raggiunto con successo la fine del file, non verrà impostato se il file non si è aperto o se si è verificato un errore prima di raggiungere la fine del file.