Search code examples
c++sdlsmart-pointerssdl-2

Smart pointer(unique_ptr) custom deleter error C2027 & C2338


I try to use smart pointers with SDL2 and I need a custom deleter. I use this code and get errors C2027 (using the undefined type SDL_Texture) & C2338 (can't delete an incomplete type)

ftexture = std::make_unique<SDL_Texture>(TTF_RenderText_Solid(font, fontData.c_str(), fontColor),
        [=](SDL_Texture* texture) {SDL_DestroyTexture(texture); });

This variable in my class looks like this:

std::unique_ptr <SDL_Texture> ftexture = nullptr;

Solution

  • First, TTF_RenderText_Solid() returns an SDL_Surface*, not an SDL_Texture*. SDL_Surface does not derive from SDL_Texture.

    Second, you can't specify a custom deleter with std::make_unique(). The 1st template argument is used as the T type of the resulting std::unique_ptr, and the remaining template arguments are used for the input parameters, which are all passed to T's constructor. In your example, T is SDL_Texture, and there is no constructor of SDL_Texture that takes an SDL_Surface* and a lambda as input.

    To use a custom deleter, you need to specify the deleter's type as a template argument of std::unique_ptr, which std::make_unique() does not allow you to do, so you have to use std::unique_ptr directly.

    The deleter should be a separate type rather than a lambda:

    struct SDL_Surface_Deleter
    {
        void operator()(SDL_Surface* surface) {
            SDL_FreeSurface(surface);
        } 
    };
    
    using SDL_Surface_ptr = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>;
    
    SDL_Surface_ptr fsurface;
    
    ...
    
    fsurface = SDL_Surface_ptr(
        TTF_RenderText_Solid(font, fontData.c_str(), fontColor)
    );
    

    But if you really want to use a lambda, you can do this instead:

    using SDL_Surface_Deleter = void (*)(SDL_Surface*);
    using SDL_Surface_ptr = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>;
    
    SDL_Surface_ptr fsurface;
    
    ...
    
    fsurface = SDL_Surface_ptr(
        TTF_RenderText_Solid(font, fontData.c_str(), fontColor),
        [](SDL_Surface* surface) { SDL_FreeSurface(surface); }
    );
    

    Or, you can just use SDL_FreeSurface() directly as the actual deleter:

    using SDL_Surface_ptr = std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)>;
    
    SDL_Surface_ptr fsurface;
    
    ...
    
    fsurface = SDL_Surface_ptr(
        TTF_RenderText_Solid(font, fontData.c_str(), fontColor),
        &SDL_FreeSurface
    );