Этот вопрос не относится к glfw, но он хорошо описывает то, что я имею в виду. В glfw, чтобы начать использовать любые функции, нам нужно вызвать glfwInit (), и когда нам больше не нужно их использовать, мы вызываем glfwTerminate (), я пытался придумать способ обернуть это вокруг класса RAII и я нашел два полезных способа сделать это, но я не уверен в плюсах и минусах каждого из них. Во всех этих случаях я опускаю проверку ошибок и тому подобное, поскольку они не слишком изменят примеры.
1: использование класса блокировки
Моей первой идеей было создать класс блокировки, который вызывал бы glfwInit () в начале его жизни и glfwTerminate () и в конце, что-то вроде этого:
struct GLFWLock
{
GLFWLock() { glfwInit(); }
~GLFWLock() { glfwTerminate(); }
}
Я понял, что если два из этих классов будут созданы, то glfwInit и glfwTerminate будут вызываться дважды, поэтому я добавил счетчик ссылок, и я чувствовал, что это было довольно полное решение, за исключением того, что оно было поточно-ориентированным и, возможно, другими , но по сути это было бы то же самое:
struct GLFWLock
{
static size_t ref_count; /* = 0 in .cpp */
GLFWLock() { if ( ref_count == 0 ) { glfwInit(); } ref_count++; }
~GLFWLock() { ref_count--; if ( ref_count == 0 ) { glfwTerminate(); } }
}
2: Использование Mutex-подобного класса
Поработав с предыдущей моделью немного, я понял, что это то же самое, что и std :: lock_guard с мьютексом, поэтому я подумал, что могу создать класс мьютекса и заставить пользователя всякий раз делать lock_guard им нужно было использовать glfw вместо того, чтобы предлагать просто блокировку.
В итоге я придумал это, что в некоторой степени соответствует концепции мьютекса в соответствии со стандартом, игнорируя некоторые формальные требования и сосредоточившись на том, что на самом деле будет использовать std :: lock_guard:
struct GLFWMutex
{
static size_t ref_count; /* = 0 in the .cpp */
bool locked = false;
~GLFWMutex() { unlock(); }
void lock()
{
if ( !locked )
{
if ( ref_count == 0 ) { glfwInit(); }
ref_count++;
locked = true;
}
}
void unlock()
{
if ( locked )
{
locked = false;
ref_count--;
if ( ref_count == 0 ) { glfwTerminate(); }
}
}
};
А затем используйте его с std :: lock_guard, когда это необходимо, как обычный мьютекс.
Я вижу, что использование подобного замку класса требует меньше ввода, поскольку вам не нужно объявлять мьютекс и охрану, но будет ли мьютексоподобный класс более полезным? Возможно, после добавления дополнительных функций-членов, таких как try_lock (), owns_lock () и других? Или есть лучшее решение для инкапсуляции этого поведения?
Редактировать 1:
Синтаксис использования, который я хотел бы для этого общего состояния, будет выглядеть примерно так:
struct glfw_shared_state
{
static size_t ref_count; /* = 0 in .cpp */
glfw_shared_state() { if ( ref_count == 0 ) { glfwInit(); } ref_count++; }
~glfw_shared_state() { ref_count--; if ( ref_count == 0 ) { glfwTerminate(); } }
};
struct Game
{
/// While this Game object is alive, I want to share the state of glfw, so it isn't terminated
glfw_shared_state state;
(...)
};
Где каждый экземпляр игры будет увеличивать ref_count на единицу, что заставит glfw остаться в живых на протяжении всей жизни игры, в основном shared_ptr, но для функций вместо объекта
Редактировать 2:
Что касается std :: lock_guard, я имел в виду что-то вроде следующего:
/// This has an internal ref counter for how many threads are currently locking it
/// When it first starts with 0 threads and someone locks, it calls glfwInit()
/// Then everytime some other thread locks, it just ups the ref counter
/// After every thread using it has unlocked it and it's ref counter is 0, it calls glfwTerminate()
/// So this isn't locking anyway, it's just sharing a state
glfw_mutex global_glfw_mutex;
void draw()
{
/// Make sure glfw is alive during this draw function
std::lock_guard lock(global_glfw_mutex);
}
Это немного запутанно, но по сути это то, что я имел в виду со вторым примером в оригинальном посте. Я думаю, что мьютекс и блокировка не подходят для этого, но я думаю, что комментарий передает значение, которое я хочу, чтобы код есть, блокировка - это просто shared_ptr