Задача: заменить std::thread
немного схожим по логике (собственно написанным) классом-оберткой WinAPI Thread.
Основная проблема связана с конструктором класса Thread: он должен быть схож по логике с конструктором класса std::thread. (см. функцию main(), чтобы увидеть аналогию)
/* ??? */
означает, что я не знаю, что туда нужно написать, чтобы работало.
#include <iostream>
#include <thread>
#include <functional>
#include <stdexcept>
#include <Windows.h>
// Класс-обертка "Поток"
class Thread {
public:
// Конструктор
template < typename _Callable, typename..._Args >
Thread(_Callable && callable, _Args && ...args) {
/* ??? */
thread_ = CreateThread(NULL, 0, /* ??? */, /* ??? */, 0, NULL);
if (thread_ == 0) {
throw std::system_error(errno, std::generic_category());
}
}
// Деструктор
~Thread() {
if (this->joinable()) {
TerminateThread(thread_, 0);
CloseHandle(thread_);
}
}
// Отделение потока выполнения от объекта потока, позволяя продолжить выполнение независимо
void detach() {
if (this->joinable()) {
if (!CloseHandle(thread_)) {
throw std::system_error();
}
thread_ = INVALID_HANDLE;
} else throw std::invalid_argument("joinable() == false!");
}
// Блокировка текущего потока до тех пор, пока поток, обозначенный *this, не завершит свое выполнение
void join() {
if (this->joinable()) {
if (WaitForSingleObject(thread_, INFINITE) != WAIT_OBJECT_0) {
throw std::system_error();
}
thread_ = INVALID_HANDLE;
} else throw std::invalid_argument("joinable() == false!");
}
// Проверяет, идентифицирует ли объект std :: thread активный поток выполнения
bool joinable() {
return thread_ != INVALID_HANDLE;
}
private:
// Идентификатор дескриптора потока
HANDLE thread_;
// [Константа] Неверный идентификатор дескриптора потока
static constexpr HANDLE INVALID_HANDLE = nullptr;
// Конструктор копирования запрещен
Thread(const Thread & ) = delete;
// Оператор копирования запрещен
Thread & operator = (const Thread & ) = delete;
};
// Некоторый класс SomeClass с некоторым методом someMethod,
// который нужно выполнить в отдельном потоке
class SomeClass {
public:
SomeClass(int a, int b): a_(a), b_(b) {}
void someMethod(int c, int d) {
// Некоторая работа...
std::cout << "Hello, world! " << (a_ += c) << ", " << (b_ += d) << '\n';
Sleep(1000);
}
private:
int a_, b_;
};
// Точка входа
int main() {
SomeClass some_object(1, 2);
std::thread std_thread( & SomeClass::someMethod, some_object, 1, 1);
Thread win_thread( & SomeClass::someMethod, some_object, 1, 1);
std_thread.join();
win_thread.join();
}
По итогу получилось следующее.
invoke.h
// from https://github.com/meganz/mingw-std-threads
#ifndef MINGW_INVOKE_H_
#define MINGW_INVOKE_H_
#include <type_traits> // For std::result_of, etc.
#include <utility> // For std::forward
#include <functional> // For std::reference_wrapper
namespace mingw_stdthread {
namespace detail {
// For compatibility, implement std::invoke for C++11 and C++14
#if __cplusplus < 201703L
template<bool PMemFunc, bool PMemData>
struct Invoker {
template<class F, class... Args>
inline static typename std::result_of<F(Args...)>::type invoke (F&& f, Args&&... args) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
};
template<bool>
struct InvokerHelper;
template<>
struct InvokerHelper<false> {
template<class T1>
inline static auto get (T1&& t1) -> decltype(*std::forward<T1>(t1)) {
return *std::forward<T1>(t1);
}
template<class T1>
inline static auto get (const std::reference_wrapper<T1>& t1) -> decltype(t1.get()) {
return t1.get();
}
};
template<>
struct InvokerHelper<true> {
template<class T1>
inline static auto get (T1&& t1) -> decltype(std::forward<T1>(t1)) {
return std::forward<T1>(t1);
}
};
template<>
struct Invoker<true, false> {
template<class T, class F, class T1, class... Args>
inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\
decltype((InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...)) {
return (InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);
}
};
template<>
struct Invoker<false, true> {
template<class T, class F, class T1, class... Args>
inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\
decltype(InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f) {
return InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f;
}
};
template<class F, class... Args>
struct InvokeResult {
typedef Invoker<std::is_member_function_pointer<typename std::remove_reference<F>::type>::value,
std::is_member_object_pointer<typename std::remove_reference<F>::type>::value &&
(sizeof...(Args) == 1)> invoker;
inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...)) {
return invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
};
template<class F, class...Args>
auto invoke (F&& f, Args&&... args) -> decltype(InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...)) {
return InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
#else
using std::invoke;
#endif
} // Namespace "detail"
} // Namespace "mingw_stdthread"
#endif
main.cpp
#include "invoke.h"
#include <iostream>
#include <thread>
#include <functional>
#include <stdexcept>
#include <Windows.h>
// Класс-обертка "Поток"
class Thread {
public:
// Конструктор
template < typename _Callable, typename..._Args >
Thread(_Callable && callable, _Args && ...args) {
mThreadArgument = [&callable, &args...] () {
mingw_stdthread::detail::invoke(callable, args...);
};
mHandle = CreateThread(NULL, 0, run<decltype(mThreadArgument)>, &mThreadArgument, 0, NULL);
if (mHandle == 0) {
throw std::system_error(GetLastError(), std::generic_category());
}
}
// Деструктор
~Thread() {
if (this->joinable()) {
TerminateThread(mHandle, 0);
CloseHandle(mHandle);
}
}
// Отделение потока выполнения от объекта потока, позволяя продолжить выполнение независимо
void detach() {
if (this->joinable()) {
if (!CloseHandle(mHandle)) {
throw std::system_error(GetLastError(), std::generic_category());
}
mHandle = INVALID_HANDLE;
} else throw std::invalid_argument("joinable() == false!");
}
// Блокировка текущего потока до тех пор, пока поток, обозначенный *this, не завершит свое выполнение
void join() {
if (this->joinable()) {
if (WaitForSingleObject(mHandle, INFINITE) != WAIT_OBJECT_0) {
throw std::system_error(GetLastError(), std::generic_category());
}
mHandle = INVALID_HANDLE;
} else throw std::invalid_argument("joinable() == false!");
}
// Проверяет, идентифицирует ли объект std :: thread активный поток выполнения
bool joinable() {
return mHandle != INVALID_HANDLE;
}
private:
// Идентификатор дескриптора потока
HANDLE mHandle;
// [Константа] Неверный идентификатор дескриптора потока
static constexpr HANDLE INVALID_HANDLE = nullptr;
// Функция-обертка для аргумента потока
std::function<void()> mThreadArgument;
// Функция потока
template < class T >
static DWORD WINAPI run(void * threadArgument) {
(*(T*) threadArgument)();
return 0;
}
// Конструктор копирования запрещен
Thread(const Thread & ) = delete;
// Оператор копирования запрещен
Thread & operator = (const Thread & ) = delete;
};
// Некоторый класс SomeClass с некоторым методом someMethod,
// который нужно выполнить в отдельном потоке
class SomeClass {
public:
SomeClass(int a, int b): a_(a), b_(b) {}
void someMethod(int c, int d) {
// Некоторая работа...
std::cout << "Hello, world! " << (a_ += c) << ", " << (b_ += d) << '\n';
Sleep(1000);
}
private:
int a_, b_;
};
// Точка входа
int main() {
SomeClass someObject(1, 2);
std::thread stdThread( & SomeClass::someMethod, someObject, 1, 1);
Thread winThread( & SomeClass::someMethod, someObject, 1, 1);
stdThread.join();
winThread.join();
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
сегодня я решил попробовать написать шифрование строк и у меня даже получилосьОднако в выводе расшифрованной строки присутствуют лишние...
Я написал алгоритм сортировки слиянием, когда я передаю стандартный компоратор от меньшего к большему, все работает нормально, но если я пытаюсь...
есть двусвязный список, необходимо сделать для него шаблон, чтобы можно было взаимодействовать с разными типами данных, подскажите как правильно...
Есть код, в котором генерируются последовательности и помещаются в inputtxt