Visualizador de algoritmo de classificação em C ++ e SDL2 aprimorado
Post original: Visualizador de algoritmos de classificação em C ++ e SDL2
Segui o conselho que você me deu e melhorei meu código. Também adicionei mais dois algoritmos de classificação. Estou procurando principalmente conselhos de legibilidade de código (integrado ao Windows com cmake e ninja)
Demonstração
adereços para @Zeta para a demonstração
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";
}
}
Motor.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
Respostas
O programa foi definitivamente melhorado em relação à versão anterior. Bom trabalho! Aqui estão algumas coisas que podem ajudá-lo a melhorá-lo ainda mais.
Não se repita (DRY)
Existem onze repetições do construtor, o que me parece um pouco excessivo, principalmente porque o código é quase idêntico. Eu os reduziria a exatamente um:
Engine(
Coord windowSize,
std::vector<int>&& numbers,
SortAlgorithm algorithm = SortAlgorithm::bubbleSort,
DrawMethod method = DrawMethod::point,
const char* windowTitle = "Sort visualizer"
);
Ao fornecer um único construtor com parâmetros padrão, as repetições são eliminadas e a flexibilidade é aprimorada. Observe também que a matriz de números é passada como um argumento.
Reduza o tamanho da interface para apenas o necessário
Essas duas funções são genéricas e não precisam ser membros da classe:
std::vector<int> SortVis::loadFile(std::istream& numberFile);
std::vector<int> SortVis::generateRandom(int maxNumber);
Ao reduzir o tamanho da interface ao mínimo necessário, a classe fica menor e mais fácil de ler, entender, testar, usar, manter e adaptar. Observe também que o primeiro argumento recebe um em std::istream&
vez de um nome de arquivo. Isso permite coisas úteis como carregar de um socket ou stringstream.
Prefiro falhar cedo
Se a SDL_InitSubSystem
chamada no construtor falhar, não há muito sentido em continuar. Por esse motivo, a calculateNumbers
chamada mais demorada provavelmente deve vir depois, e não antes.
Só defina a cor se for desenhar algo
Os únicos lugares que SDL_SetRenderDrawColor
devem ser usados são imediatamente antes de algo ser realmente desenhado na tela. Por esse motivo, ele pode aparecer exatamente duas vezes neste código: uma vez no topo drawSelection
e uma vez no topo draw
.
Mova o trabalho para fora dos loops onde for prático
Em vez de recalcular todas as quatro partes de column
cada vez por meio do loop, é possível simplesmente alterar aquelas que precisam ser alteradas:
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;
}
Use objetos em vez de switch
es
O código atualmente contém isto:
void SortVis::Engine::step()
{
switch (selectedSortAlgorithm)
{
case SortAlgorithm::bubbleSort:
stepBubbleSort();
break;
case SortAlgorithm::insertionSort:
stepInsertionSort();
break;
case SortAlgorithm::selectionSort:
stepSelectionSort();
break;
default:
break;
}
}
Isso requer a avaliação de selectedSortAlgorithm
cada iteração. A melhor maneira de fazer isso é criar uma classe base virtual que demonstra a interface e, em seguida, permitir que o usuário crie uma classe derivada com qualquer novo tipo de tipo que desejar. Esta é uma maneira de fazer isso:
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;
}
};
Agora, para obter o máximo de flexibilidade, você pode passar esse objeto para o Engine
em seu construtor:
Engine visualization{
{ 1024, 768 },
generateRandom(1024),
std::make_unique<BubbleSorter>()
};
Use um ponteiro para uma função-membro
Uma coisa semelhante pode ser feita para o método de desenho, se quiser, mas é um pouco mais complicado, pois usa um ponteiro para uma função de membro que tem uma sintaxe que pode ser difícil de acertar. Podemos declarar a variável dentro Engine
desta forma:
void (Engine::*drawSelection)();
Eu reaprovei seu drawSelection
nome aqui. Dentro do construtor, podemos usar isto:
drawSelection{method == DrawMethod::point ? &Engine::drawPoints : &Engine::drawColumns}
E, finalmente, para chamá-lo, precisamos usar o operador de acesso de ponteiro para membro. Aqui está no contexto:
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);
}
Não faça trabalho que não seja necessário
No momento, o loop principal run
é este:
while (running)
{
handleEvents();
if (!std::is_sorted(numbers.begin(), numbers.end()))
{
step();
}
draw();
}
Não faz muito sentido para mim fazer uma chamada para std::is_sorted
dentro de um programa que demonstra classificação! Pareceu-me que gostaria de fechar o programa quando ele terminasse a classificação e modifiquei as rotinas de classificação para retornar bool
com o valor false
apenas quando a execução for concluída e o vetor for classificado. Então, para isso, o loop se transforma em:
while (running) {
handleEvents();
running &= sorter->step(numbers);
draw();
}
Considere adicionar recursos
Eu sugeriria que seria bom adicionar alguns recursos. Aqui está o que eu fiz:
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;
}
}
}
Use namespace SortVis {...}
emEngine.cpp
Você pode evitar a repetição do namespace em cada definição de função Engine.cpp
envolvendo todo o código dentro dele namespace SortVis
, assim como fez em Engine.h
. Também é comum não recuar o código dentro de um namespace {...}
bloco que cobre todo o arquivo, para evitar que o código saia do lado direito da tela com muita frequência.
Muitos construtores
Você tem 12 construtores diferentes, isso é um pouco demais. Imagine também que você possa querer adicionar outro parâmetro opcional no futuro e, em seguida, dobrar o número de construtores necessários. Isso não é sustentável a longo prazo. Vejo duas maneiras de reduzir o número de construtores necessários:
Use valores de argumento padrão, como:
Engine(Coord windowSize, int maxNumber, const char *windowTitle = "Sort visualizer", SortAlgorithm algorithm = SortAlgorithm::bubbleSort, DrawMethod method = DrawMethod::line);
Com essa abordagem, você só precisa de dois construtores. Pode ser um pouco mais incômodo de usar se você quiser apenas especificar outro
DrawMethod
, mas é um preço pequeno para uma capacidade de manutenção muito melhor.Não especifique os valores de entrada, algoritmo e método de desenho no construtor, permita que eles sejam definidos por funções-membro, como:
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);
Em geral, faça no construtor apenas o que realmente precisa ser feito na hora da construção. Inicializar o SDL e abrir uma janela é crucial para um mecanismo de visualização em funcionamento, portanto, é bom fazer isso no construtor.
Considere não gerar / carregar números em class Engine
Em vez de ter de Engine
gerar números aleatórios ou carregá-los de um arquivo, você pode simplificar não fazendo nada, mas apenas permitindo que ele use qualquer vetor que você fornecer. Então, por exemplo:
void run(const std::vector<int> &input) {
numbers = input;
...
}
Você pode até considerar transmiti-lo como não constante e run()
modificar o vetor de entrada fornecido.
Considere dividir a visualização da classificação
Um grande problema é que você precisa dividir seus algoritmos de classificação em etapas e ter uma switch()
instrução step()
para escolher o algoritmo certo. Você também precisa enumerar os algoritmos possíveis. Em vez de fazer isso, considere fazer Engine
apenas visualizar um vetor de números e, em vez de Engine
conduzir as etapas do algoritmo, tenha uma unidade de algoritmo Engine
para mostrar o estado do vetor em cada etapa. Você pode fazer isso alterando Engine::draw()
para tomar uma referência a um vetor de números:
void Engine::draw(const std::vector<int> &numbers) {
...
}
E um algoritmo de classificação pode se tornar uma única função:
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 então você main()
pode ficar assim:
int main() {
std::vector<int> numbers = {...}; // generate some vector here
SortVis::Engine visualization({1024, 768});
SortVis::bubbleSort(numbers, visualization);
}
Os benefícios dessa abordagem são que você separa as preocupações. O Engine
agora só precisa visualizar um vetor (provavelmente deve ser renomeado para algo parecido Visualizer
). Você pode adicionar facilmente novos algoritmos de classificação sem ter que mudar Engine
.
Um problema com o acima é que ele não lida mais com eventos SDL. Você poderia fazer isso em draw()
e draw()
retornar um bool
indicando se o algoritmo deve continuar ou não.
Verifique se há erros depois de ler um arquivo, não antes
Em Engine::loadFile()
, você verifica se o arquivo está aberto corretamente, mas nunca verifica se ocorreu um erro durante a leitura. Uma maneira possível é:
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.");
}
Aqui usamos o fato de que o eofbit
só é definido se atingir o final do arquivo com sucesso, não será definido se o arquivo falhar ao abrir ou se ocorrer um erro antes de chegar ao final do arquivo.