[Tools.h++]12.3 ユーザ定義関数
汎用コレクションクラスのメンバ関数の中には、ユーザ定義関数へのポインタを必要とするものがあります。次の 2 節で説明するように、これらのユーザ定義関数には 2 つの種類があります。
12.3.1 テスタ関数
1 つ目のユーザ定義関数は、テスタ関数です。. これは、次の形式を持ちます。
RWBoolean tester(const type* ty, const void* a)
tester は関数名を、type はコレクションクラスのメンバの型をそれぞれ表わし、RWBoolean は 唯一可能な値が TRUE または FALSE であるような int に typedef されています。テスタ関数の役目は、コレクションのあるメンバが同定されたときに合図することです。どのようにしてこれが決定されるか、またはオブジェクトを同定することはどういう意味を持つかは、ユーザに任されています。アドレスの比較 (2 つのオブジェクトの「同一性」についてのテスト)、またはオブジェクト内で特定の値を検索 (isEqual のテスト) を選択できます。最初の変数 ty はコレクションのメンバを指しており、候補者として考えることができます。2 番目の変数 a はクライアントデータとしてみなすことができ、と一致するかどうかをテストすることができます。
次の例では、前の例を拡張したもので、問題は isEqual のテストです。いくつかの値をスタックにプッシュして、ある値がスタックに存在するかどうかを調べます。クラス RWGStack(type) のメンバ関数 contains() は、次のプロトタイプを持ちます。
RWBoolean contains(RWBoolean (*t)(const type*, const void*),
const void* a) const;
最初の引数は、RWBoolean (*t)(const type*, const void*) です。これは、テスタ関数へのポインタで、適切な定義を与える必要があります。
#include <rw/gstack.h>
#include <rw/rstream.h>
declare(RWGStack, int)
RWBoolean myTesterFunction(const int* jp, const void* a) //1
{ return *jp == *(const int*)a; //2
}
main(){
RWGStack(int) gs; //3
gs.push(new int(1)); //4
gs.push(new int(2)); //5
gs.push(new int(3)); //6
gs.push(new int(4)); //7
int aValue = 2; //8
if ( gs.contains(myTesterFunction, &aValue) ) //9
cout << "Yup.\n";
else
cout << "Nope.\n";
while(!gs.isEmpty())
delete gs.pop();
return 0;
}
出力:
Yup.
次に、プログラムを行ごとに説明します。
- //1
- これはテスタ関数です。最初の引数は、コレクションのオブジェクトの型へのポインタであることに注意してください。この場合は、int です。2 番目の引数は、任意の型のオブジェクトを指すことができます。この例では、int です。両方の引数が const ポインタとして宣言されていることに注意してください。一般に、テスタ関数は指されているオブジェクトの値を変更すべきではありません。
- //2
- 2 番目の引数は、型 const void* から型 const int* に変換され、間接参照されます。結果の型は const int になります。この const int は、間接参照された最初の引数 const int と比較されます。結果として、このテスタ関数は 2 つの int が同じ値を持つ (等値である) ときに一致が発生したとみなします。特別の整数であるかどうかで識別することもできたことに注意してください (例えばアイデンティティ (実体) の検査)。
- //3-//7
- これらの行は 12.1 節の例と同じです。整数 (へのポインタ) の汎用スタックが宣言、定義され、4 個の値がスタックにプッシュされます。
- //8
- これがスタックで探す値 (例えば2) です。
- //9
- ここで、テスタ関数を用いてメンバ関数 contains() が呼び出されます。contains() の 2 番目の引数は変数 aValue へのポインタで、テスタ関数の 2 番目の引数として現れます。関数 contains() は、スタック全体を走査して、順番に各項目ごとにテスタ関数を呼び出し、テスタ関数が一致の合図を出すのを待ちます。一致がある場合は contains() が TRUE を返し、そうでない場合は FALSE を返します。
上記の例では、テスタ関数の 2 番目の引数はコレクションのメンバと同じ型ですが、必ずしも同じ型である必要はないことに注意してください。次の例では、引数とコレクションのメンバが異なる型です。
#include <rw/gstack.h>
#include <rw/rstream.h>
class Foo {
public:
int data;
Foo(int i) {data = i;}
};
declare(RWGStack, Foo) // Foo へのポインタのスタック
RWBoolean anotherTesterFunction(const Foo* fp, const void* a)
{ return fp->data == *(const int*)a;
}
main(){
RWGStack(Foo) gs;
gs.push(new Foo(1));
gs.push(new Foo(2));
gs.push(new Foo(3));
gs.push(new Foo(4));
int aValue = 2;
if ( gs.contains(anotherTesterFunction, &aValue) )
cout << "Yup.\n";
else
cout << "Nope.\n";
while(!gs.isEmpty())
delete gs.pop();
return 0;
}
この例では、テスタ関数の 2 番目の引数として渡される変数が依然 const int* であるときに、Foo へのポインタのスタックが宣言され使用されます。テスタ関数は、異なる型を計算に入れる必要があります。
12.3.2 適用関数
2 番目の種類のユーザ定義関数は、適用関数です。. これは、次のような形式をとります。
void yourApplyFunction(type* ty, void* a)
yourApplyFunction は関数名を、type はコレクションメンバの型を表わします。適用関数により、印刷したり画面上に表示するなど、コレクションの各メンバで同じ操作を実行することができます。2 番目の引数は、オブジェクトが描かれるウィンドウのハンドルなど、関数によって使用されるクライアントデータを保持するように設計されています。
次の例では、適用関数 printAFoo を使用して、汎用二重リンクリストである RWGDlist(type) の各メンバの値を印刷します。
#include <rw/gdlist.h>
#include <rw/rstream.h>
class Foo {
public:
int val;
Foo(int i) {val = i;}
};
declare(RWGDlist, Foo)
void printAFoo(Foo* ty, void* sp){
ostream* s = (ostream*)sp;
(*s) << ty->val << "\n";
}
main(){
RWGDlist(Foo) gd;
gd.append(new Foo(1));
gd.append(new Foo(2));
gd.append(new Foo(3));
gd.append(new Foo(4));
gd.apply(printAFoo, &cout);
while(!gd.isEmpty())
delete gd.get();
return 0;
}
出力:
1 2 3 4
項目がリストの最後に付け加えられます。各項目ごとに、関数 apply() が最初の引数として項目のアドレスを、2 番目の引数として ostream (出力ストリーム) のアドレスを持つユーザ定義関数 printAFoo() を呼び出します。printAFoo() の役目は、メンバデータ val を出力することです。apply() はリストの先頭から終結まで走査するため、項目は挿入されたのと同じ順序で出力されます。RWGDlist(type) については、『Tools.h++ Class Reference』を参照してください。
注意深く適用関数を使うことによって、コレクションのオブジェクトを変更することができます。例えば、上記の例では、適用関数を使ってメンバデータ val の値を変更できます。ただし、apply() を使ってコレクション内の項目数や項目の場所を変更することはできません。特に、適用関数でコレクションから項目を削除したり、あるいはコレクションに項目を追加したりしてはいけません。