/*	
 *	clipintf.h
 *
 *	インターフェイス
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2015-2016 Naoyuki Sawa
 *
 *	* Tue Jan 06 21:26:41 JST 2015 Naoyuki Sawa
 *	- 新規作成。
 *	- インターフェイス関連の宣言を、clipmisc.hからclipintf.hへ分離しました。
 *	  インターフェイス関連の実装は既に、clipmisc.cからclipintf.cへ分離してあったので、今回分離したのは宣言のみです。
 *	  宣言も分離することにした理由は、宣言と実装のファイルの対応付けを、分り易くするためです。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'～'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 *	* Sat Apr 30 21:45:42 JST 2016 Naoyuki Sawa
 *	- 変数名を'this'⇒'This'に変更しました。
 *	  VC++6.0ではCソースのコンパイルでも変数名にthisは使えないようだからです。(GCCならばokなのですが…)
 *	  この変更により、当モジュールもVC++6.0でコンパイル出来るようになり、libclipに含めました。
 *	  詳細は、/clip/libclip/extclip.h の同日のコメントを参照して下さい。
 *	- C++ソースからインクルードした時に_safe_detach()で警告が出るのを、明示的なキャストを行うようにして修正しました。
 */
#ifndef __CLIP_INTF_H__
#define __CLIP_INTF_H__

#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus

/*****************************************************************************
 *	ユーティリティ：簡略版COM (Component Object Model)
 *****************************************************************************/

/* 参照カウントの増減ルール (暫定版。※TODO:あとで詳しく書く。)
 *
 *	以下、呼び出す側(A)、呼び出される側(B、C)とする。
 *
 *	[ルール1-1]
 *	Bの戻り値としてインターフェイスを返す場合、Aのために、参照カウントを+1しておく。
 *	Aは、インターフェイスの使用が終わったら、参照カウントを-1する。
 *
 *		INTERFACE** B() {
 *			safe_addref(g_intf);	// refcnt+1
 *			return g_intf;
 *		}
 *
 *		void A() {
 *			INTERFACE** intf = B();
 *			...
 *			safe_release(intf);	// refcnt-1
 *		}
 *
 *	[ルール1-2]
 *	Bがオブジェクト生成関数である場合も、[ルール1-1]に従う。
 *	つまり、Bは参照カウント1のオブジェクトを作成し、そのインターフェイスをAに返す。
 *	Aは、返されたインターフェイスが、新規作成されたものか、既存のものか、判断する必要が無い。
 *
 *		INTERFACE** B() {
 *			OBJECT** obj = calloc(1, OBJECT);
 *			obj->refcnt = 1;		// refcnt=1
 *			obj->interface = &interface;	// VTBL設定
 *			...
 *			return &obj->interface;
 *		}
 *
 *		void A() {
 *			INTERFACE** intf = B();
 *			...
 *			safe_release(intf);		// refcnt-1
 *		}
 *
 *	[ルール2-1]
 *	AがBへの入力引数としてインターフェイスを渡す場合、Aは参照カウントを増減しない。
 *	Bは、インターフェイスの使用が関数内のみならば、参照カウントを増減しない。
 *
 *		void A() {
 *			B(g_intf);
 *		}
 *
 *		void B(INTERFACE** intf) {
 *			...
 *		}
 *
 *	[ルール2-2]
 *	Bが、グローバル変数等にインターフェイスを保持するならば、参照カウントを+1する。
 *	グローバル変数等への保持が終了したら、参照カウントを-1する。
 *
 *		void A() {
 *			B(g_intf);
 *			...
 *			C();
 *		}
 *
 *		void B(INTERFACE** intf) {
 *			g_intf = intf;
 *			safe_addref(g_intf);		// refcnt+1
 *		}
 *
 *		void C(INTERFACE** intf) {
 *			safe_release(g_intf);		// refcnt-1
 *		}
 */

/*---------------------------------------------------------------------------*/

/* インターフェイスを定義するためのマクロです。
 * * 簡易UUIDとして、万国標準時1970/1/1 00:00:00からの経過秒数を利用します。
 *   clip/tool/timestmp.exeコマンドを使って、生成できます。
 */
#define DEFINE_INTERFACE(intfname, timestmp, method_list)		\
	enum { intfname##_ID = timestmp };				\
	typedef struct _##intfname intfname;				\
	struct _##intfname {						\
		void* (*query_interface)(intfname** intf, int id);	\
		void  (*addref)(intfname** intf);			\
		void  (*release)(intfname** intf);			\
		method_list						\
	};

/* 基底インターフェイス(UNKNOWN_INTERFACE)を定義します。
 * * method_listが空であるために、DEFINE_INTERFACEマクロが利用できません。
 *   止むを得ず、UNKNOWN_INTERFACEだけは、マクロを使わずに定義しています。
 */
enum { UNKNOWN_INTERFACE_ID = 0/*1970/01/01 09:00:00*/ };
typedef struct _UNKNOWN_INTERFACE UNKNOWN_INTERFACE;
struct _UNKNOWN_INTERFACE {
	void* (*query_interface)(UNKNOWN_INTERFACE** intf, int id);
	void  (*addref)(UNKNOWN_INTERFACE** intf);
	void  (*release)(UNKNOWN_INTERFACE** intf);
};

//----------------------------------------------------------------------------
//				実装例
//----------------------------------------------------------------------------
//	
//	/*----- *.hファイルに記述し、公開する部分 -----*/
//	
//	DEFINE_INTERFACE(SAMPLE_INTERFACE, 1133444760/*2005/12/01 22:46:00*/,
//		void (*put)(SAMPLE_INTERFACE** intf, int value);
//		int  (*get)(SAMPLE_INTERFACE** intf);
//	);
//	
//	SAMPLE_INTERFACE** sample_create(int initial_value);
//	
//	/*----- *.cファイルに記述し、非公開の部分 -----*/
//	
//	static void* sample_query_interface(SAMPLE_INTERFACE** intf, int id);
//	static void sample_addref(SAMPLE_INTERFACE** intf);
//	static void sample_release(SAMPLE_INTERFACE** intf);
//	static void sample_put(SAMPLE_INTERFACE** intf, int value);
//	static int sample_get(SAMPLE_INTERFACE** intf);
//	
//	static SAMPLE_INTERFACE sample_interface = {
//		sample_query_interface,
//		sample_addref,
//		sample_release,
//		sample_put,
//		sample_get,
//	};
//	
//	typedef struct _SAMPLE {
//		SAMPLE_INTERFACE* sample_interface;
//		int refcnt;
//		int value;
//	} SAMPLE;
//	
//	SAMPLE_INTERFACE**
//	sample_create(int initial_value)
//	{
//		SAMPLE* This = calloc(1, sizeof(SAMPLE));
//		if(!This) {
//			DIE();
//		}
//		This->sample_interface = &sample_interface;
//		This->refcnt = 1; /* 作成直後の参照カウントは1 */
//		This->value = initial_value;
//		return &This->sample_interface;
//	}
//	
//	static void
//	sample_delete(SAMPLE* This)
//	{
//		free(This);
//		printf("Delete!\n");
//	}
//	
//	static void*
//	sample_query_interface(SAMPLE_INTERFACE** intf, int id)
//	{
//		SAMPLE* This = CONTAINING_RECORD(intf, SAMPLE, sample_interface);
//		UNKNOWN_INTERFACE** unknown;
//		switch(id) {
//		case UNKNOWN_INTERFACE_ID:
//		case SAMPLE_INTERFACE_ID:
//			unknown = (void*)&This->sample_interface;
//			This->refcnt++; /* query_interface()が成功したら参照カウントを+1 */
//			break;
//		default:
//			unknown = NULL;
//			break;
//		}
//		return unknown;
//	}
//	
//	static void
//	sample_addref(SAMPLE_INTERFACE** intf)
//	{
//		SAMPLE* This = CONTAINING_RECORD(intf, SAMPLE, sample_interface);
//		This->refcnt++;
//	}
//	
//	static void
//	sample_release(SAMPLE_INTERFACE** intf)
//	{
//		SAMPLE* This = CONTAINING_RECORD(intf, SAMPLE, sample_interface);
//		if(!--This->refcnt) {
//			sample_delete(This);
//		}
//	}
//	
//	static void
//	sample_put(SAMPLE_INTERFACE** intf, int value)
//	{
//		SAMPLE* This = CONTAINING_RECORD(intf, SAMPLE, sample_interface);
//		This->value = value;
//	}
//	
//	static int
//	sample_get(SAMPLE_INTERFACE** intf)
//	{
//		SAMPLE* This = CONTAINING_RECORD(intf, SAMPLE, sample_interface);
//		return This->value;
//	}
//	
//	/*----- テストプログラム -----*/
//	
//	void
//	test_main()
//	{
//		SAMPLE_INTERFACE** sample = sample_create(123);
//		printf("%d\n", (*sample)->get(sample));	/* "123"と表示されます */
//		(*sample)->put(sample, 456);
//		printf("%d\n", (*sample)->get(sample));	/* "456"と表示されます */
//		(*sample)->release(sample);		/* "Delete!"と表示されます */
//	}
//	
//----------------------------------------------------------------------------

/* * オブジェクト初期化と、query_interface()の実装を簡単にする、ヘルパ関数のための構造体です。
 *   インターフェイスを実装するクラス毎に、あらかじめINTERFACE_MAPの配列を定義しておけば、
 *   オブジェクト初期化とquery_interface()の処理の大部分を、ヘルパ関数に任せられます。
 * * インターフェイスマップは、INTERFACE_MAP.id=0の要素で終端します。
 *   インターフェイスID=0は、実際にはUNKNOWN_INTERFACE_IDに相当するのですが、
 *   INTERFACE_MAPの配列においては、UNKNOWN_INTERFACE_IDでなく、単なる終端マークと見なします。
 * * インターフェイスマップには、UNKNOWN_INTERFACEに相当する要素は含めません。
 *   その代りに、先頭のインターフェイスが「暗黙のUNKNOWN_INTERFACE」として扱われます。
 *   UNKNOWN_INTERFACE_IDを指定して、query_interface_map()を呼び出した場合、
 *   query_interface_map()は、インターフェイスマップの先頭の要素のインターフェイスを、
 *   「暗黙のUNKNOWN_INTERFACE」として、呼び出し元に返します。
 * * 上述のように、先頭のインターフェイスを固定的に「暗黙のUNKNOWN_INTERFACE」とするために、
 *   インターフェイスマップには、少なくとも一つ以上の有効な要素が必要です。
 *   すなわち、インターフェイスマップの先頭の要素が、id=0であってはいけません。
 *   言い換えると、INTERFACE_MAPを利用してインターフェイスを管理するオブジェクトは、
 *   UNKNOWN_INTERFACE以外に、一つ以上のインターフェイスを持っている必要があります。
 */
typedef struct _INTERFACE_MAP {
	int id;		/* インターフェイスID (id=0にて終端。終端要素のoffset,vtblは無意味) */
	int offset;	/* オブジェクトの先頭から、インターフェイスまでのオフセットバイト数 */
	void* vtbl;	/* インターフェイスVTBLのポインタ (apply_interface_map()だけが参照) */
} INTERFACE_MAP;

/* オブジェクト作成時に、インターフェイスを格納する、ヘルパ関数です。
 * [in]
 *	This		オブジェクトの先頭アドレス。
 *	map		インターフェイスマップ。
 *			INTERFACE_MAP.id=0の要素で、明示的に配列を終端してください。
 * [note]
 *	* query_interface_map()のコメントと、後述の利用例を参照してください。
 */
void apply_interface_map(void* This, const INTERFACE_MAP* map);

/* インターフェイスマップを検索する、ヘルパ関数です。
 * [in]
 *	This		オブジェクトの先頭アドレス。
 *	id		検索するインターフェイスID。
 *	map		インターフェイスマップ。
 *			INTERFACE_MAP.id=0の要素で、明示的に配列を終端してください。
 * [out]
 *	戻り値		成功ならば、参照カウントを+1し、インターフェイスを返します。
 *			失敗ならば、参照カウントを変更せず、NULLを返します。
 * [note]
 *	* インターフェイスマップに、UNKNOWN_INTERFACEに対応する要素を含めてはいけません。
 *	  id=UNKNOWN_INTERFACE_IDが指定された場合、最初の要素のインターフェイスを返します。
 *	  すなわち、インターフェイスマップの最初の要素が「代表UNKNOWN_INTERFACE」になります。
 */
void* query_interface_map(void* This, int id, const INTERFACE_MAP* map);

//----------------------------------------------------------------------------
//				利用例
//----------------------------------------------------------------------------
//	
//	typedef struct _SAMPLE {
//		int refcnt;
//		SAMPLE1_INTERFACE* sample1_interface;
//		SAMPLE2_INTERFACE* sample2_interface;
//		SAMPLE3_INTERFACE* sample3_interface;
//		int value;
//	} SAMPLE;
//	
//	SAMPLE1_INTERFACE sample1_interface = { ... };
//	SAMPLE2_INTERFACE sample2_interface = { ... };
//	SAMPLE3_INTERFACE sample3_interface = { ... };
//	
//	static const INTERFACE_MAP sample_interface_map[] = {
//		{ SAMPLE1_INTERFACE_ID, FIELD_OFFSET(SAMPLE, sample1_interface), &sample1_interface },
//		{ SAMPLE2_INTERFACE_ID, FIELD_OFFSET(SAMPLE, sample2_interface), &sample2_interface },
//		{ SAMPLE3_INTERFACE_ID, FIELD_OFFSET(SAMPLE, sample3_interface), &sample3_interface },
//		{ 0 } /* インターフェイスマップの終端。忘れないで!! */
//	};
//	
//	SAMPLE*
//	sample_create()
//	{
//		SAMPLE* This = calloc(1, sizeof(SAMPLE));
//		if(!This) {
//			DIE();
//		}
//		This->refcnt = 1;
//		apply_interface_map(This, sample_interface_map);
//		return This;
//	}
//	
//	static void*
//	sample1_query_interface(SAMPLE1_INTERFACE** intf, int id)
//	{
//		SAMPLE* This = CONTAINING_RECORD(intf, SAMPLE, sample1_interface);
//		return query_interface_map(This, id, sample_interface_map);
//	}
//	
//	static void*
//	sample2_query_interface(SAMPLE2_INTERFACE** intf, int id)
//	{
//		SAMPLE* This = CONTAINING_RECORD(intf, SAMPLE, sample2_interface);
//		return query_interface_map(This, id, sample_interface_map);
//	}
//	
//	static void*
//	sample3_query_interface(SAMPLE3_INTERFACE** intf, int id)
//	{
//		SAMPLE* This = CONTAINING_RECORD(intf, SAMPLE, sample3_interface);
//		return query_interface_map(This, id, sample_interface_map);
//	}
//	
//----------------------------------------------------------------------------

/* インターフェイスポインタの参照カウントを、増加します。
 * インターフェイスポインタがNULLだったら、何もしません。
 */
//#define safe_addref(ptr) do {		\
//	if(ptr) {			\
//		(*(ptr))->addref(ptr);	\
//	}				\
//} while(0)
//↓2005/12/10 safe_addref(ptr)が、ptrを返すように変更しました。
//↓このような仕様とした理由は、safe_addref(ptr)の後、直ちにptrを返す関数のためです。
//↓上述の、参照カウントの増減ルール[ルール1-1]を参照してください。
//↓これまでは、
//↓	safe_addref(ptr);
//↓	return ptr;
//↓と記述していた処理を、今後は、
//↓	return safe_addref(ptr);
//↓と記述することができるようになりました。
#define safe_addref(ptr) ((ptr) ? ((*(ptr))->addref(ptr), (ptr)) : (ptr))

/* インターフェイスポインタの参照カウントを減少後、NULLにします。
 * インターフェイスポインタがもともとNULLだったら、何もしません。
 */
#define safe_release(ptr) do {		\
	if(ptr) {			\
		(*(ptr))->release(ptr);	\
		(ptr) = NULL;		\
	}				\
} while(0)

/* インターフェイスポインタに、新しいインターフェイスをアサインします。
 * - dstとsrcが、同じオブジェクトを指している可能性があるので、
 *   新しくアサインするインターフェイスの参照カウントを増加した後、
 *   既存インターフェイスの参照カウントを減少することに注意してください。
 *   この手順を逆順にすると、途中でオブジェクトが削除されてしまいます。
 * - <使用例>
 *	SAMPLE_INTERFACE** sample1 = sample_create(123); // sample(123)'s refcnt=1
 *	SAMPLE_INTERFACE** sample2 = sample_create(456); // sample(456)'s refcnt=1
 *	safe_assign(sample1, sample2); // sample(123)'s refcnt=0, sample(456)'s refcnt=2
 */
#define safe_assign(dst, src) do {			\
	UNKNOWN_INTERFACE** __tmp__ = (void*)(dst);	\
	(dst) = (src);					\
	safe_addref(dst);				\
	safe_release(__tmp__);				\
} while(0)

/* インターフェイスポインタに、新しいインターフェイスをアタッチします。
 * - 新しくアサインするインターフェイスを得る式(src)の中に、(dst)が含まれている可能性があるので、
 *   式(src)を評価した後に、(dst)の参照カウントを減少することに注意してください。
 *   この手順を逆順にすると、途中でオブジェクトが削除されてしまいます。
 *	<例> safe_attach(sample, (*sample)->clone(sample));
 * - safe_assign()との違いは、新しいインターフェイスの参照カウントを増加しないことです。
 * - <使用例>
 *	SAMPLE_INTERFACE** sample = sample_create(123); // sample(123)'s refcnt=1
 *	safe_attach(sample, sample_create(234)); // sample(123)'s refcnt=0, sample(456)'s refcnt=1
 */
#define safe_attach(dst, src) do {			\
	UNKNOWN_INTERFACE** __tmp__ = (void*)(dst);	\
	(dst) = (src);					\
	safe_release(__tmp__);				\
} while(0)

/* インターフェイスを取り出し、インターフェイスポインタをクリアします。
 * - 参照カウントの増加、減少は伴いません。
 * - P/ECEでは、GCC拡張機能を利用して、マクロのみで定義しています。
 *   Win32では、GCC拡張機能が利用できないため、マクロとインライン関数で定義しています。
 *   Win32では、やむを得ず(void*)へのキャストを行うため、型チェックが少し甘くなります。
 * - <使用例>
 *	SAMPLE_INTERFACE** sample = sample_create(123); // sample(123)'s refcnt=1
 *	return safe_detach(sample); // sample(123)'s refcnt=1
 *   上述の使用例は、safe_detach()を使わず、単純に、
 *	return sample_create(123);
 *   と変更する方法でも、同じ結果が得られます。
 */
#ifdef PIECE
#define safe_detach(ptr) ({			\
	typeof(ptr) __tmp__ = (ptr);		\
	(ptr) = NULL;				\
	__tmp__;				\
})
#else /*PIECE*/
#define safe_detach(ptr) _safe_detach(&(ptr))
static __inline void* _safe_detach(void* _pptr) {
	UNKNOWN_INTERFACE***       pptr = (UNKNOWN_INTERFACE***)_pptr;	//C++ソースからインクルードした時に_safe_detach()で警告が出るのを、明示的なキャストを行うようにして修正しました。
	UNKNOWN_INTERFACE** ptr = *pptr;
	*pptr = NULL;
	return ptr;
}
#endif /*PIECE*/

/* 安全なquery_interface()呼び出しを行います。
 * - インターフェイスポインタがNULLでなければ、query_interface()を呼び出し、戻り値を適切にキャストします。
 *   インターフェイスポインタがNULLならば、query_interface()を呼び出さずに、NULLを返します。
 * - P/ECE版は、GCC拡張機能を利用し、引数ptrを複数回評価しない、安全な実装となっています。
 *   Win32版は、GCC拡張機能が利用できないため、やむを得ず、引数ptrを複数回評価しています。
 *   引数ptrに、関数呼び出しを含む式や、副作用のある式を指定しないよう、注意してください。
 * - <使用例>
 *	SAMPLE_INTERFACE** sample = safe_query_interface(unknown, SAMPLE_INTERFACE);
 */
#ifdef PIECE
#define safe_query_interface(ptr, intfname) ((intfname**)({			\
	typeof(ptr) __tmp__ = (ptr);						\
	(__tmp__) ? (*(__tmp__))->query_interface((__tmp__), intfname##_ID)	\
	      : NULL;								\
}))
#else /*PIECE*/
#define safe_query_interface(ptr, intfname) ((intfname**)(	\
	(ptr) ? (*(ptr))->query_interface((ptr), intfname##_ID)	\
	      : NULL						\
))
#endif /*PIECE*/

/* safe_query_interface()とsafe_attach()は、同時に使うケースが多いので、簡略表記を用意しました。
 * 以下のコード:
 *	SAMPLE1_INTERFACE** sample1 = ...;
 *	SAMPLE2_INTERFACE** sample2 = ...;
 *	safe_attach(sample2, safe_query_interface(sample1, SAMPLE2_INTERFACE));
 * を、次のように記述できます。
 *	SAMPLE1_INTERFACE** sample1 = ...;
 *	SAMPLE2_INTERFACE** sample2 = ...;
 *	safe_assign_interface(sample2, sample1, SAMPLE2);
 * safe_attach_interface()ではなく、safe_assign_interface()というマクロ名にした理由は、
 * オブジェクトの参照カウント数の合計が、新たに1増えることを強調するためです。
 */
#define safe_assign_interface(dst_ptr, src_ptr, dst_intfname)			\
	safe_attach((dst_ptr), safe_query_interface((src_ptr), dst_intfname))

/* 二つのインターフェイスポインタが、同じオブジェクトに属するものか否か、調べます。
 * 同じオブジェクトに属するものならば1、さもなくば0を返します。
 * - 以下のいずれか条件に一致する場合、同じオブジェクトに属すると判断します。
 *	①intf1とintf2が、いずれもNULLの場合。
 *	②intf1とintf2に対し、明示的にUNKNOWN_INTERFACEを要求し、それらが一致した場合。
 * - P/ECEでは、GCC拡張機能を利用して、マクロのみで定義しています。
 *   Win32では、GCC拡張機能が利用できないため、マクロとインライン関数で定義しています。
 *   Win32では、やむを得ず(void*)へのキャストを行うため、型チェックが少し甘くなります。
 * - .NET Frameworkの、Object.ReferenceEqualsメソッドから、名前を借りました。
 */
#ifdef PIECE
#define reference_equals(intf1, intf2) ({						\
	UNKNOWN_INTERFACE** unk1 = safe_query_interface((intf1), UNKNOWN_INTERFACE);	\
	UNKNOWN_INTERFACE** unk2 = safe_query_interface((intf2), UNKNOWN_INTERFACE);	\
	if(unk1) (*unk1)->release(unk1);						\
	if(unk2) (*unk2)->release(unk2);						\
	unk1 == unk2;									\
})
#else /*PIECE*/
#define reference_equals(intf1, intf2) _reference_equals((void*)(intf1), (void*)(intf2))
static __inline int _reference_equals(UNKNOWN_INTERFACE** intf1, UNKNOWN_INTERFACE** intf2) {
	UNKNOWN_INTERFACE** unk1 = safe_query_interface((intf1), UNKNOWN_INTERFACE);
	UNKNOWN_INTERFACE** unk2 = safe_query_interface((intf2), UNKNOWN_INTERFACE);
	if(unk1) (*unk1)->release(unk1);
	if(unk2) (*unk2)->release(unk2);
	return unk1 == unk2;
}
#endif /*PIECE*/

#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus

#endif//__CLIP_INTF_H__
