Firefox ExtensionでC++プログラムを動かしてみるテスト 〜その2 XPCOMモジュールの実装〜

Firefox ExtensionでC++プログラムを動かしてみるテスト 〜その1 IDLでAPIを書いてみる〜 - かーねる・う゛いえむにっきからの続き。

IDLに対応したクラスの定義とモジュール情報の定義を行う。
まずクラスの定義から。

testKVS.hというファイルを作成する。
ここでは、xpidlが生成したtestIKVS.hで宣言されてるtestIKVSクラスを継承したtestKVSImplクラスを作ってみる。
クラスメンバの定義はNS_DECL_ISUPPORTS(nsISupportsクラスのメンバ)、NS_DECL_TESTIKVS(testIKVSクラスのメンバ)の二つのマクロを利用し、コンストラクタ・デストラクタのみ付け加えてみる。

#ifndef __TESTKVS_H__
#define __TESTKVS_H__

#include "testIKVS.h"

class testKVSImpl : public testIKVS
{
public:
	NS_DECL_ISUPPORTS
	NS_DECL_TESTIKVS

	testKVSImpl();

private:
	~testKVSImpl();
};

#endif

次に、testKVSImplクラスを実装する。ファイル名はtestKVS.cpp。

#include <stdlib.h>
#include <stdint.h>
#include "testKVS.h"

NS_IMPL_ISUPPORTS1(testKVSImpl, testIKVS)

#define SIZE_OF_STORE 1024

static uint32_t *store;

testKVSImpl::testKVSImpl()
{
	store = (uint32_t *)calloc(SIZE_OF_STORE, sizeof(uint32_t));
}

testKVSImpl::~testKVSImpl()
{
	free(store);
}

/* void open (); */
NS_IMETHODIMP testKVSImpl::Open()
{
    return NS_OK;
}

/* long get (in long key); */
NS_IMETHODIMP testKVSImpl::Get(PRInt32 key, PRInt32 *_retval NS_OUTPARAM)
{
	if (key >= 0 && key < SIZE_OF_STORE) {
		*_retval = store[key];
		return NS_OK;
	}else
		return NS_ERROR_ILLEGAL_VALUE;
}

/* void put (in long key, in long value); */
NS_IMETHODIMP testKVSImpl::Put(PRInt32 key, PRInt32 value)
{
	if (key >= 0 && key < SIZE_OF_STORE) {
		store[key] = value;
		return NS_OK;
	}else
		return NS_ERROR_ILLEGAL_VALUE;
}

/* void close (); */
NS_IMETHODIMP testKVSImpl::Close()
{
    return NS_OK;
}

/* End of implementation class template. */

Key value storeというのだから本当は永続的にデータが保存出来たり、場合によっては分散出来たりするものをイメージするが、今回はスタブコードとして単なるメモリ上の配列とした。
#一度動くものが出来てから後で拡張すれば良いし。

testKVSImplクラスのインスタンス作成時にSIZE_OF_STORE分のメモリをstoreにcalloc()している。
今回はopen/closeするものが何も無いので両メソッドはダミー。

Get(key)でstore[key]をreturn, Put(key, value)でvalueをstore[key]へ代入。
それだけ。
配列外参照のチェックだけしておく。

各メソッドのスタブコードはtestIKVS.hにあるので、コピーしてきて実装する。
IDLで定義した返り値は引数からの参照渡しで、実際の引数はNS_*という名前のエラーコードになる。
このエラーコードは$SDK_DIR/dist/include/xpcom/nsError.hに定義されているのでこれを参照しながら決める。

最後にモジュール情報の定義を行う。
testKVSModule.cppというファイルを作成して、こんな内容を書く。

#include "nsIGenericFactory.h"
#include "nsIClassInfoImpl.h"
#include "xpcom-config.h"

#include "testKVS.h"

NS_GENERIC_FACTORY_CONSTRUCTOR(testKVSImpl)
NS_DECL_CLASSINFO(testKVSImpl)

static nsModuleComponentInfo components[] =
{
	{
		"TestKVS",
		{ 0x11E00341, 0xAC12, 0x434B, { 0x9F, 0x3A, 0x80, 0x29, 0xB1, 0x48, 0x0A, 0xBB } },
		"@dokukino.com/testKVS;1",
		testKVSImplConstructor,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		&NS_CLASSINFO_NAME(testKVSImpl)
	}
};

NS_IMPL_NSGETMODULE(testKVSModule, components)

これはワケ分からんよね。
マクロの部分はとにかくこのように書くと思っておくのが良さそう。
nsModuleComponentInfoの方だけど、このあたりを見ると各項目が何に当たるのかが書いてある。

つまりこういう対応になってる訳だね。

member description value
mDescription Class Name of given object "TestKVS"
mCID CID of given object { 0x11E00341, 0xAC12, 0x434B, { 0x9F, 0x3A, 0x80, 0x29, 0xB1, 0x48, 0x0A, 0xBB } }
mContractID Contract ID of given object "@dokukino.com/testKVS;1"
mConstructor Constructor of given object testKVSImplConstructor
mRegisterSelfProc (optional) Registration Callback NULL
mUnregisterSelfProc (optional) Unregistration Callback NULL
mFactoryDestructor (optional) Destruction Callback NULL
mGetInterfacesProc (optional) Interfaces Callback NULL
mGetLanguageHelperProc (optional) Language Helper Callback NULL
mClassInfoGlobal (optional) Global Class Info of given object &NS_CLASSINFO_NAME(testKVSImpl)
mFlags (optional) Class Info Flags 0

mDescriptionは単なる説明。
mCIDはモジュール実体のUUIDらしい。IDLのはインタフェースに対するUUIDっぽくて、別の値となる。
mContractIDはXPCOMオブジェクトとして呼び出すときの名前。
mConstructorはコンストラクタ関数?どっかのマクロで作ってると思う。
mなんとかProcの類はコールバック関数。必須じゃない。
mFactoryDestructorはきっとコンストラクタと対になってるんだろう。
mClassInfoGlobalはクラス情報?
mFlagsは指定してない。必須ではない。

一応、これだけ書けば最低限のXPCOMモジュール実装はおしまい。
次回はコンパイル編。