是否可以将 SDL2 与智能指针一起使用?
Is it possible to use SDL2 with smart pointers?
我有这行代码
//std::unique_ptr<SDL_Window> _window_; // this is somewhere else...
_window_ = std::make_unique<SDL_Window>(SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _WIDTH_, _HEIGHT_, SDL_WINDOW_SHOWN));
它产生以下编译器错误
In file included from /usr/include/c++/6/memory:81:0,
from /home/user/prj/src/main.cpp:4:
/usr/include/c++/6/bits/unique_ptr.h: In instantiation of ‘typename
std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = SDL_Window; _Args = {SDL_Window*}; typename
std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<SDL_Window>]’:
/home/user/prj/src/main.cpp:36:170: required from here
/usr/include/c++/6/bits/unique_ptr.h:791:30: error: invalid use of incomplete type ‘struct SDL_Window’
{ return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
为什么? (它在没有智能指针的情况下工作正常,所以我的猜测是我不理解语法,这很容易修复。将在下面添加源和 CMakeLists.txt
。)
CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
project(prj)
find_package(SDL2 REQUIRED)
include_directories(prj ${SDL2_INCLUDE_DIRS})
add_executable(prj main.cpp)
target_link_libraries(prj ${SDL2_LIBRARIES})
main.cpp
#include "SDL.h"
#include <memory>
#include <iostream>
#include <fstream>
#include <cstdint>
class Window
{
public:
Window()
: _window_{nullptr}
, _surface_{nullptr}
{
if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
std::cerr << SDL_GetError() << std::endl;
}
else
{
_window_ = std::make_unique<SDL_Window>(SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _WIDTH_, _HEIGHT_, SDL_WINDOW_SHOWN));
if(_window_ == nullptr)
{
std::cerr << SDL_GetError() << std::endl;
}
else
{
_surface_ = std::make_unique<SDL_Surface>(SDL_GetWindowSurface(_window_.get()));
SDL_FillRect(_surface_.get(), nullptr, SDL_MapRGB(_surface_->format, 0xFF, 0xFF, 0xFF));
SDL_UpdateWindowSurface(_window_.get());
SDL_Delay(1000);
}
}
}
~Window()
{
SDL_DestroyWindow(_window_.get());
SDL_Quit();
}
private:
const int32_t _WIDTH_{600};
const int32_t _HEIGHT_{400};
std::unique_ptr<SDL_Window> _window_;
std::unique_ptr<SDL_Surface> _surface_;
};
int main(int argc, char* argv[])
{
Window window;
return 0;
}
这些结构是 opaque data structures,您没有它们的完整定义。这意味着,除其他外,默认的 std::unique_ptr
删除器不知道如何 "delete" 结构。
您需要提供自己的自定义删除器。 SDL_Window
是 SDL_DestroyWindow
函数。
SDL Windows 使用 SDL_DestroyWindow 销毁,而不是像 unique_ptr 的默认删除器那样简单删除。您需要向 unique_ptr 提供自定义删除器,它将调用 SDL_DestroyWindow.
解决方案
经过反复试验终于找到了答案,所以将在这里解释解决方案。
这是正确的语法:
// first define the unique_ptr as member of class
std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> _window_;
// second, initialize in the member initialization list of class constructor
// probably don't need to do this if not embedding as member of class
class_name()
: _window_(nullptr, SDL_DestroyWindow)
{
// blaa blaa SDL code etc
_window_.reset(SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN));
}
// finally we need to be able to delete
// but this is handled automatically
说明
当我们添加 unique_ptr 作为数据成员时,我们需要同时提供类型 SDL_Window
和 "deleter function format / syntax",因为普通的 delete
调用不是正确的。我们使用 decltype
从删除函数中自动构造正确的删除格式。 (也许不是最准确的解释。)在某种程度上,decltype
有点像 auto...
std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> _window_;
必须初始化此对象。我们在构造函数中执行此操作。我们将指针设置为 nullptr
(因为我们不想在初始化 SDL2 之前对其进行初始化)并且我们还设置了 deleter 函数。
: _window_(nullptr, SDL_DestroyWindow)
初始化SDL后,我们接下来要创建一个window。这通过调用智能指针 reset()
函数最容易完成。我们向它传递一个由创建 window.
的函数返回的新指针
_window_.reset(SDL_CreateWindow(...));
完成。花了很长时间才弄清楚,但现在很有意义。参考文献
http://en.cppreference.com/w/cpp/memory/unique_ptr
Why does my unique_ptr think is has a null function pointer deleter?
我有这行代码
//std::unique_ptr<SDL_Window> _window_; // this is somewhere else...
_window_ = std::make_unique<SDL_Window>(SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _WIDTH_, _HEIGHT_, SDL_WINDOW_SHOWN));
它产生以下编译器错误
In file included from /usr/include/c++/6/memory:81:0,
from /home/user/prj/src/main.cpp:4:
/usr/include/c++/6/bits/unique_ptr.h: In instantiation of ‘typename
std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = SDL_Window; _Args = {SDL_Window*}; typename
std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<SDL_Window>]’:
/home/user/prj/src/main.cpp:36:170: required from here
/usr/include/c++/6/bits/unique_ptr.h:791:30: error: invalid use of incomplete type ‘struct SDL_Window’
{ return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
为什么? (它在没有智能指针的情况下工作正常,所以我的猜测是我不理解语法,这很容易修复。将在下面添加源和 CMakeLists.txt
。)
CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
project(prj)
find_package(SDL2 REQUIRED)
include_directories(prj ${SDL2_INCLUDE_DIRS})
add_executable(prj main.cpp)
target_link_libraries(prj ${SDL2_LIBRARIES})
main.cpp
#include "SDL.h"
#include <memory>
#include <iostream>
#include <fstream>
#include <cstdint>
class Window
{
public:
Window()
: _window_{nullptr}
, _surface_{nullptr}
{
if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
std::cerr << SDL_GetError() << std::endl;
}
else
{
_window_ = std::make_unique<SDL_Window>(SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _WIDTH_, _HEIGHT_, SDL_WINDOW_SHOWN));
if(_window_ == nullptr)
{
std::cerr << SDL_GetError() << std::endl;
}
else
{
_surface_ = std::make_unique<SDL_Surface>(SDL_GetWindowSurface(_window_.get()));
SDL_FillRect(_surface_.get(), nullptr, SDL_MapRGB(_surface_->format, 0xFF, 0xFF, 0xFF));
SDL_UpdateWindowSurface(_window_.get());
SDL_Delay(1000);
}
}
}
~Window()
{
SDL_DestroyWindow(_window_.get());
SDL_Quit();
}
private:
const int32_t _WIDTH_{600};
const int32_t _HEIGHT_{400};
std::unique_ptr<SDL_Window> _window_;
std::unique_ptr<SDL_Surface> _surface_;
};
int main(int argc, char* argv[])
{
Window window;
return 0;
}
这些结构是 opaque data structures,您没有它们的完整定义。这意味着,除其他外,默认的 std::unique_ptr
删除器不知道如何 "delete" 结构。
您需要提供自己的自定义删除器。 SDL_Window
是 SDL_DestroyWindow
函数。
SDL Windows 使用 SDL_DestroyWindow 销毁,而不是像 unique_ptr 的默认删除器那样简单删除。您需要向 unique_ptr 提供自定义删除器,它将调用 SDL_DestroyWindow.
解决方案
经过反复试验终于找到了答案,所以将在这里解释解决方案。
这是正确的语法:
// first define the unique_ptr as member of class
std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> _window_;
// second, initialize in the member initialization list of class constructor
// probably don't need to do this if not embedding as member of class
class_name()
: _window_(nullptr, SDL_DestroyWindow)
{
// blaa blaa SDL code etc
_window_.reset(SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN));
}
// finally we need to be able to delete
// but this is handled automatically
说明
当我们添加 unique_ptr 作为数据成员时,我们需要同时提供类型 SDL_Window
和 "deleter function format / syntax",因为普通的 delete
调用不是正确的。我们使用 decltype
从删除函数中自动构造正确的删除格式。 (也许不是最准确的解释。)在某种程度上,decltype
有点像 auto...
std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> _window_;
必须初始化此对象。我们在构造函数中执行此操作。我们将指针设置为 nullptr
(因为我们不想在初始化 SDL2 之前对其进行初始化)并且我们还设置了 deleter 函数。
: _window_(nullptr, SDL_DestroyWindow)
初始化SDL后,我们接下来要创建一个window。这通过调用智能指针 reset()
函数最容易完成。我们向它传递一个由创建 window.
_window_.reset(SDL_CreateWindow(...));
完成。花了很长时间才弄清楚,但现在很有意义。参考文献
http://en.cppreference.com/w/cpp/memory/unique_ptr
Why does my unique_ptr think is has a null function pointer deleter?