I'm making some SDL2 wrappers in C++. Like this:
/* header file */
#include <SDL_mixer.h>
#include <memory>
class SDL2_Music {
public:
~SDL2_Music() { free(); }
bool loadMusic(const std::string& path);
bool play(int loops = -1);
// more methods
private:
void free();
Mix_Music* music_ = nullptr;
};
/* cpp file */
void SDL2_Music::free() {
if (music_ != nullptr) {
Mix_FreeMusic(music_);
music_ = nullptr;
}
}
bool SDL2_Music::loadMusic(const std::string& path) {
free();
music_ = Mix_LoadMUS(path.c_str()); // this returns a Mix_Music*
if (music_ == nullptr) {
ktp::logSDLError("Mix_LoadMUS");
return false;
}
return true;
}
// more stuff
This works fine, but I want to get rid of the raw pointer, so I can also get rid of the free()
method and the dtor
invoking it (yes, I'm reading about the rule of 0). So I made the following changes:
/* header file */
#include <SDL_mixer.h>
#include <memory>
class SDL2_Music {
public:
bool loadMusic(const std::string& path);
bool play(int loops = -1);
// more methods
private:
std::unique_ptr<Mix_Music> music_ = nullptr;
};
/* cpp file */
bool SDL2_Music::loadMusic(const std::string& path) {
music_ = std::make_unique<Mix_Music>(Mix_LoadMUS(path.c_str()));
if (music_ == nullptr) {
ktp::logSDLError("Mix_LoadMUS");
return false;
}
return true;
}
// more stuff
When I try to compile (GCC) I get:
"C:\Users\not_bjarne\CodeBlocks\MinGW\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\unique_ptr.h|831|error: invalid use of incomplete type 'struct _Mix_Music'"
And codeblocks points me to unique_ptr.h
, which I obviously didn't tried to "fix".
It seems that Mix_Music
is an incomplete type and the correct way to free a Mix_Music
object is to call the Mix_FreeMusic
function. You cannot dispose of it the way you would a C++ object, namely by using delete
. delete
would attempt to call the destructor (which cannot be done in this context, since the type is incomplete here) and would assume that the object was allocated by new
and return the memory to the same pool where new
got it from. The way SDL allocates the object is an implementation detail, so you must let SDL deallocate the object itself as well, to ensure it is done properly.
std::unique_ptr
can be used for this purpose, but requires a custom deleter. The default deleter will call delete
, which should not be done here. The error you are seeing is because delete p;
is ill-formed (where p
has type Mix_Music*
) because of the incompleteness. You must ensure that the custom deleter calls Mix_FreeMusic
. You can see how to use custom deleters here: How do I use a custom deleter with a std::unique_ptr member?