Use unique_ptr to manage SDL pointers

This commit is contained in:
Lexi / Zoe 2025-11-21 01:09:05 +01:00
parent 5c3e0e3a86
commit 4277f4c818
Signed by: binaryDiv
GPG Key ID: F8D4956E224DA232
8 changed files with 128 additions and 129 deletions

View File

@ -22,16 +22,6 @@ export
public: public:
App() = default; App() = default;
// No copy operations
App(const App&) = delete;
App& operator=(const App&) = delete;
// Default move operations
App(App&&) = default;
App& operator=(App&&) = default;
~App() = default;
SDL_AppResult initialize() SDL_AppResult initialize()
{ {
try { try {

View File

@ -31,13 +31,11 @@ export namespace core
instantiated_ = true; instantiated_ = true;
} }
// No copy operations // No copy or move operations
Engine(const Engine&) = delete; Engine(const Engine&) = delete;
Engine& operator=(const Engine&) = delete; Engine& operator=(const Engine&) = delete;
Engine(Engine&&) = delete;
// Default move operations Engine& operator=(Engine&&) = delete;
Engine(Engine&&) = default;
Engine& operator=(Engine&&) = default;
~Engine() ~Engine()
{ {

View File

@ -1,15 +1,20 @@
module; module;
#include <memory>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
export module core.renderer; export module core.renderer;
import utils.memory;
export namespace core export namespace core
{ {
class Renderer class Renderer
{ {
// TODO: Use unique_ptr with custom deleters std::unique_ptr<
SDL_Renderer* sdl_renderer_ = nullptr; SDL_Renderer,
utils::FuncDeleter<SDL_DestroyRenderer>
> sdl_renderer_ = nullptr;
public: public:
Renderer() = default; Renderer() = default;
@ -18,47 +23,21 @@ export namespace core
: sdl_renderer_(sdl_renderer) : sdl_renderer_(sdl_renderer)
{} {}
// No copy operations
Renderer(const Renderer&) = delete;
Renderer& operator=(const Renderer&) = delete;
// Move constructor
Renderer(Renderer&& other) noexcept
: sdl_renderer_(other.sdl_renderer_)
{
other.sdl_renderer_ = nullptr;
}
// Move assignment
Renderer& operator=(Renderer&& other) noexcept
{
sdl_renderer_ = other.sdl_renderer_;
other.sdl_renderer_ = nullptr;
return *this;
}
~Renderer()
{
if (sdl_renderer_ != nullptr) {
SDL_DestroyRenderer(sdl_renderer_);
}
}
// TODO: Remove this when not needed anymore // TODO: Remove this when not needed anymore
SDL_Renderer* get_sdl_renderer() const constexpr SDL_Renderer* get_sdl_renderer() const
{ {
return sdl_renderer_; return sdl_renderer_.get();
} }
// TODO: Rename clear/present to start_render/finish_render or similar? // TODO: Rename clear/present to start_render/finish_render or similar?
void clear() const void clear() const
{ {
SDL_RenderClear(sdl_renderer_); SDL_RenderClear(sdl_renderer_.get());
} }
void present() const void present() const
{ {
SDL_RenderPresent(sdl_renderer_); SDL_RenderPresent(sdl_renderer_.get());
} }
// TODO: Replace SDL_Texture pointer with Texture class // TODO: Replace SDL_Texture pointer with Texture class
@ -66,7 +45,7 @@ export namespace core
// to just type-alias it?) // to just type-alias it?)
void render_texture(SDL_Texture* texture, const SDL_FRect* src_rect, const SDL_FRect* dest_rect) const void render_texture(SDL_Texture* texture, const SDL_FRect* src_rect, const SDL_FRect* dest_rect) const
{ {
SDL_RenderTexture(sdl_renderer_, texture, src_rect, dest_rect); SDL_RenderTexture(sdl_renderer_.get(), texture, src_rect, dest_rect);
} }
}; };
} }

View File

@ -1,5 +1,6 @@
module; module;
#include <memory>
#include <string> #include <string>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
@ -7,13 +8,16 @@ export module core.window;
import core.exceptions; import core.exceptions;
import core.renderer; import core.renderer;
import utils.memory;
export namespace core export namespace core
{ {
class Window class Window
{ {
// TODO: Use unique_ptr with custom deleters std::unique_ptr<
SDL_Window* sdl_window_ = nullptr; SDL_Window,
utils::FuncDeleter<SDL_DestroyWindow>
> sdl_window_ = nullptr;
public: public:
Window() = default; Window() = default;
@ -22,32 +26,6 @@ export namespace core
: sdl_window_(sdl_window) : sdl_window_(sdl_window)
{} {}
// No copy operations
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
// Move constructor
Window(Window&& other) noexcept
: sdl_window_(other.sdl_window_)
{
other.sdl_window_ = nullptr;
}
// Move assignment
Window& operator=(Window&& other) noexcept
{
sdl_window_ = other.sdl_window_;
other.sdl_window_ = nullptr;
return *this;
}
~Window()
{
if (sdl_window_ != nullptr) {
SDL_DestroyWindow(sdl_window_);
}
}
static std::pair<Window, Renderer> create_window_and_renderer( static std::pair<Window, Renderer> create_window_and_renderer(
const std::string& title, const std::string& title,
const int width, const int width,

View File

@ -8,6 +8,7 @@ export module game.game;
import core.engine; import core.engine;
import core.renderer; import core.renderer;
import game.sprite; import game.sprite;
import resources.texture;
export namespace game export namespace game
{ {
@ -25,11 +26,9 @@ export namespace game
: engine_(engine) : engine_(engine)
{} {}
// No copy operations // No copy or move operations because we have a reference to the engine
Game(const Game&) = delete; Game(const Game&) = delete;
Game& operator=(const Game&) = delete; Game& operator=(const Game&) = delete;
// No move operations - TODO?
Game(Game&&) = delete; Game(Game&&) = delete;
Game& operator=(Game&&) = delete; Game& operator=(Game&&) = delete;
@ -37,7 +36,12 @@ export namespace game
void initialize() void initialize()
{ {
sprite_ = std::make_unique<Sprite>(engine_.get_renderer(), "assets/neocat.png", 100, 100); auto texture = resources::Texture::load_from_file(
engine_.get_renderer().get_sdl_renderer(),
"assets/neocat.png"
);
sprite_ = std::make_unique<Sprite>(std::move(texture), 100, 100);
} }
// Handles an SDL event. Returns true if the event has been handled. // Handles an SDL event. Returns true if the event has been handled.

View File

@ -2,87 +2,53 @@ module;
#include <string> #include <string>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
export module game.sprite; export module game.sprite;
import core.exceptions; import core.exceptions;
import core.renderer; import core.renderer;
import resources.texture;
// TODO: Move this to a different namespace (core, drawing, ...?) // TODO: Move this to a different namespace (core, drawing, ...?)
export namespace game export namespace game
{ {
class Sprite class Sprite
{ {
// TODO: Move texture to separate class // TODO: Texture should be a reference/pointer to an object managed by a ResourceManager or similar.
SDL_Texture* sdl_texture; resources::Texture texture_;
SDL_FRect dest_rect{0, 0, 0, 0}; SDL_FRect dest_rect_{0, 0, 0, 0};
public: public:
explicit Sprite( explicit Sprite(
core::Renderer& renderer, resources::Texture&& texture,
const std::string& filename,
const int width, const int width,
const int height const int height
) )
: texture_{std::move(texture)}
{ {
SDL_Surface* texture_surface = IMG_Load(filename.c_str()); dest_rect_.w = static_cast<float>(width);
if (texture_surface == nullptr) { dest_rect_.h = static_cast<float>(height);
throw core::SDLException("IMG_Load");
}
sdl_texture = SDL_CreateTextureFromSurface(renderer.get_sdl_renderer(), texture_surface);
SDL_DestroySurface(texture_surface);
if (sdl_texture == nullptr) {
throw core::SDLException("SDL_CreateTextureFromSurface");
}
dest_rect.w = static_cast<float>(width);
dest_rect.h = static_cast<float>(height);
} }
// Don't allow copy operations // Don't allow copy operations
Sprite(const Sprite&) = delete; Sprite(const Sprite&) = delete;
Sprite& operator=(const Sprite&) = delete; Sprite& operator=(const Sprite&) = delete;
// Move constructor // Default move operations
Sprite(Sprite&& other) noexcept Sprite(Sprite&& other) = default;
: sdl_texture(other.sdl_texture), Sprite& operator=(Sprite&& other) = default;
dest_rect(other.dest_rect)
{
other.sdl_texture = nullptr;
}
// Move assignment ~Sprite() = default;
Sprite& operator=(Sprite&& other) noexcept
{
// Move inner resources from other
sdl_texture = other.sdl_texture;
dest_rect = other.dest_rect;
// Reset other to make it safe for deletion
other.sdl_texture = nullptr;
return *this;
}
~Sprite()
{
if (sdl_texture != nullptr) {
SDL_DestroyTexture(sdl_texture);
}
}
void move(const float x, const float y) void move(const float x, const float y)
{ {
dest_rect.x = x; dest_rect_.x = x;
dest_rect.y = y; dest_rect_.y = y;
} }
void draw(const core::Renderer& renderer) const void draw(const core::Renderer& renderer) const
{ {
renderer.render_texture(sdl_texture, nullptr, &dest_rect); renderer.render_texture(texture_.get_sdl_texture(), nullptr, &dest_rect_);
} }
}; };
} }

View File

@ -0,0 +1,63 @@
module;
#include <memory>
#include <string>
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
export module resources.texture;
import core.exceptions;
import utils.memory;
export namespace resources
{
class Texture
{
std::unique_ptr<
SDL_Texture,
utils::FuncDeleter<SDL_DestroyTexture>
> sdl_texture_ = nullptr;
public:
Texture() = default;
explicit Texture(SDL_Texture* sdl_texture)
: sdl_texture_(sdl_texture)
{}
static Texture create_from_surface(
SDL_Renderer* sdl_renderer,
SDL_Surface* sdl_surface
)
{
SDL_Texture* sdl_texture = SDL_CreateTextureFromSurface(sdl_renderer, sdl_surface);
if (sdl_texture == nullptr) {
throw core::SDLException("SDL_CreateTextureFromSurface");
}
return Texture{sdl_texture};
}
static Texture load_from_file(
SDL_Renderer* sdl_renderer,
const std::string& filename
)
{
SDL_Texture* sdl_texture = IMG_LoadTexture(sdl_renderer, filename.c_str());
if (sdl_texture == nullptr) {
throw core::SDLException("IMG_LoadTexture");
}
return Texture{sdl_texture};
}
// TODO: Do we need this?
constexpr SDL_Texture* get_sdl_texture() const
{
return sdl_texture_.get();
}
};
}

21
src/utils/memory.cppm Normal file
View File

@ -0,0 +1,21 @@
module;
export module utils.memory;
export namespace utils
{
/**
* Template to generate deleters for `std::unique_ptr` from functions, e.g. to free SDL resources.
*
* @tparam delete_func Function that takes a pointer to a resource and deletes the resource.
*/
template <auto delete_func>
struct FuncDeleter
{
template <typename T>
constexpr void operator()(T* ptr) const noexcept
{
delete_func(ptr);
}
};
}