車輪の再発明 : Windows専用(?) たぶん便利なThreadクラスを自作した

# 1.はじめに
MFCでスレッドに関するクラスというと、以下があります。

https://learn.microsoft.com/ja-jp/cpp/mfc/reference/cwinthread-class?view=msvc-170

「でも、MessagePump なんて大それたことしないんだけどな～。大げさだな～」って思いました。
次に候補に挙がるのは Win32 API の CreateThread です。

https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread

ここにはさらっととんでもないことか書いてあります。

>C ランタイム ライブラリ (CRT) を呼び出す実行可能ファイル内のスレッドは、CreateThread と ExitThreadを するのではなく、スレッド管理に _beginthreadex 関数と _endthreadex 関数 使用する必要があります。これには、マルチスレッド バージョンの CRT を使用する必要があります。 CreateThread を使用して作成されたスレッドが CRT を呼び出すと、CRT はメモリ不足の状態でプロセスを終了する可能性があります。

原文でも以下ですから誤翻訳でもなさそうです。

>A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multithreaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.

一方、std::thread だと以下の点が物足りないと思います
* スレッドが動作中なのかどうかがわからない
* (使うかどうかはともかく)実行したスレッドの戻り値がわからない

考えるのが面倒になってきました。

# 2. と、いうことで __beginthreadex を元に スレッドクラスを作りました。
まずはヘッダ。

```c++:threadbase.h

// ThreadBase.h
#pragma once

#include	<windows.h>

class CThreadBase
{
public:
	CThreadBase();
	virtual ~CThreadBase();

	HANDLE	CreateThread(unsigned int (__stdcall* start_address)(void*),
		void* pParam = nullptr, void* pSecurity = NULL,
		unsigned int stack_size = 0, unsigned int initflag = 0, unsigned int* thrdaddr = NULL);

	void	join();
	BOOL	isThreadRunning();
	BOOL	GetExitCodeThread(LPDWORD  pExitCode);

private:
	static unsigned int __stdcall ThreadBaseRun(void* pParam);

	unsigned int (__stdcall* m_pFunc)(void*);
	void*						m_pParam;
	HANDLE						m_hThread;
};

```

引数の順番は好みの問題です。セキュリティ記述子などほとんど使ったことがないもの。
そんなのデフォルト引数 にして NULLを入れておけばいいのではないか。で、cpp。

``` C++:ThreadBase.cpp

// ThreadBase.cpp
#include	"ThreadBase.h"

CThreadBase::CThreadBase()
	: m_pFunc(NULL)
	, m_pParam(NULL)
	, m_hThread(INVALID_HANDLE_VALUE)
{
	// no more!
}

CThreadBase::~CThreadBase()
{
	join();
}

void	CThreadBase::join()
{
	if (m_hThread != INVALID_HANDLE_VALUE) {
		WaitForSingleObject(m_hThread, INFINITE);
		CloseHandle(m_hThread);
		m_hThread = INVALID_HANDLE_VALUE;
	}
}

BOOL	CThreadBase::isThreadRunning()
{
	if (m_hThread == INVALID_HANDLE_VALUE) {
		return FALSE;
	}
	return (WaitForSingleObject(m_hThread, 0) == WAIT_TIMEOUT);
}


BOOL	CThreadBase::GetExitCodeThread(LPDWORD  pExitCode)
{
	if (m_hThread == INVALID_HANDLE_VALUE) {
		return FALSE;
	}
	return (::GetExitCodeThread(m_hThread, pExitCode));
}

HANDLE	CThreadBase::CreateThread(unsigned int (__stdcall* start_address)(void*),
	void* pParam, void* pSecurity, unsigned int stack_size, unsigned int initflag, unsigned int * thrdaddr)
{
	// スレッド起動中の再起動を防ぐ
	if ((nullptr == start_address) || (m_hThread != INVALID_HANDLE_VALUE))
	{
		return INVALID_HANDLE_VALUE;
	}

	HANDLE hRet = (HANDLE)(_beginthreadex(pSecurity, stack_size, ThreadBaseRun,
		this, initflag, thrdaddr));

	m_hThread = hRet;
	m_pFunc = start_address;
	m_pParam = pParam;

	return hRet;
}

unsigned int __stdcall CThreadBase::ThreadBaseRun(void* pParam)
{
	CThreadBase* pThis = static_cast<CThreadBase*>(pParam);
	
	unsigned int (__stdcall * pFunc)(void*) = pThis->m_pFunc;

	unsigned int uRet = pFunc(pThis->m_pParam);
	_endthreadex(uRet);

	// この文は実行されない
	return uRet;
}

```
# 3. あとはよろしく
CThreadBase を継承して使ってください。