#プログラム大喜利 内容不定Job実行系

Twitter. It's what's happening.

実はオブジェクト指向問題だったりする。
JavaC++, Ruby などのオブジェクト指向言語であれば非常にすっきりと実装できたりする。

実はCでもできん事は無いのだけれども、スコープをファイル単位で分けねばならなかったりで結構しちめんどくさいw

下記回答は当初C++のくせにまとめて書いていたが、やっぱり読みにくいというか、ヘッダはヘッダとしてあったほうがクラスの全容が把握しやすいと思われるため、分けることにした。
内容は分割前と同じ。

CJobMgr.h

#ifndef CJobMgr_h
#define CJobMgr_h

#include <iostream>
#include <list>

class CJobMgr;

//! Job基底クラス
/*!
各Jobは、このクラスを派生させて実装する。
*/
class CJob
{
  protected:
	CJob();

  public:
	virtual ~CJob();

	virtual bool watch() = 0;	// 監視メソッド: 監視条件の判定を記述する
	virtual bool exec() = 0;	// 実行メソッド: 実行する処理を記述する
	virtual void destroy() = 0;	// 破棄メソッド: 破棄処理を記述する
	
  protected:
	//! Job登録メソッド
	bool regist();
	
	//! 破棄予約メソッド
	bool remove();
};

class CJobMgr
{
	friend class CJob;
  private:
	CJobMgr();
	virtual ~CJobMgr();
  public:
	//! インスタンス取得
	static CJobMgr& getInstance();
	
	//! 登録 Job 実行
	void exec_job();
	
  private:
	//! 指定Jobの登録
	bool regist(CJob * pJob);
	
	//! 指定Jobの削除予約
	bool remove(CJob * pJob);
private:
	std::list<CJob *>		m_lstJob;		//!< Jobリスト
	std::list<CJob *>		m_lstRemove;	//!< 削除予約リスト
};

#endif // CJobMgr_h

CJobMgr.cpp

#include "CJobMgr.h"


CJob::CJob() {}
CJob::~CJob() {}

/*!
\return Job登録が正常終了すればtrue, 異常終了すれば false

Jobの初期化条件がJobごとに異なることが考えられるので、初期化メソッドは実装しないが、
初期化の最後にこのメソッドを呼び、自身をマネージャに登録するという仕様。
*/
bool
CJob::regist()
{
	return CJobMgr::getInstance().regist(this);
}
	
/*!
\return 破棄予約に成功すれば true, 失敗すれば false

Jobを破棄予約する。破棄予約されたJobはメインループの最後にまとめて破棄される。
各Jobインスタンスに対し destroy() を呼んだ後、delete される。
*/
bool
CJob::remove()
{
	return CJobMgr::getInstance().remove(this);
}




CJobMgr::CJobMgr() {}
CJobMgr::~CJobMgr() {}

/*!
\return インスタンスへの参照

ジョブマネージャは複数存在してはいけないので、singleton として実装する。
*/
CJobMgr&
CJobMgr::getInstance()
{
	static CJobMgr instance;
	return instance;
}

/*!
メインループの中で呼び出すべきメソッド。
	for(;;) {
	    CJobMgr::getInstance().exec_job();
	}
のように呼べば良い。

登録されている全てのJobに対し watch() を呼び、
true を返したものについては exec() を呼び出す。
その過程で自身を削除予約した者については、最後にまとめて破棄処理を行う。
*/
void
CJobMgr::exec_job()
{
	std::list<CJob *>::iterator it;

	// 削除予約リストをクリアする。
	m_lstRemove.clear();

	// 登録されているJobを処理する
	for(it = m_lstJob.begin(); it != m_lstJob.end(); it++) {
		CJob * pJob = *it;
		if(pJob->watch()) pJob->exec();
	}
		
	// 削除予約されたものを削除する
	for(it = m_lstRemove.begin(); it != m_lstRemove.end(); it++) {
		CJob * pJob = *it;
		pJob->destroy();
		m_lstJob.remove(pJob);
		delete pJob;	// インスタンスそのものを破棄
	}
}

/*!
\param pJob  登録対象となるJobインスタンスのポインタ
\return 登録が正常終了すればtrue, 異常終了の場合はfalse

指定されたポインタのJobをマネージャに登録する。CJob::regist()内部から呼ばれる。
privateメソッドだが、friend class である CJob からは呼べるため、
Jobの実装者はマネージャの存在を意識する必要がなくなる。

このようにする理由は、singleton の public メソッドはプログラム中の
何処からでも呼べてしまうため。
不正な呼び出しを防ぐ目的で「CJob以外から呼ぶことができない」状態を作り出している。
*/
bool
CJobMgr::regist(CJob * pJob)
{
	try {
		m_lstJob.push_back(pJob);
		return true;
	} catch(std::bad_alloc& ex) {
		return false;
	}
}

/*!
\param pJob  削除予約対象となるJobインスタンスのポインタ
\return 予約が正常終了すればtrue, 異常終了の場合はfalse

指定されたポインタのJobを、マネージャに対し削除予約する。CJob::remove()内部から呼ばれる。
スコープに関しては regist(CJob*) と同様。
*/
bool
CJobMgr::remove(CJob * pJob)
{
	try {
		m_lstRemove.push_back(pJob);
		return true;
	} catch(std::bad_alloc& ex) {
		return false;
	}
}