関数へのポインターが必要です。
double(*)(double*)
あなたが持っているのは、メンバー関数へのポインタです:
double (MyClass::*)(double*)
これらは同じものではありません。または、すべてが似ています。最初のものは直接呼び出すことができます。->*
2 つ目は、 or.*
演算子を使用して、オブジェクトを介してのみ呼び出すことができます。
これは、コンパイラが型にうるさいというだけではありません。メンバー関数へのポインターは、カバーの下のポインター (または少なくとも単なるポインターではない) ではありません。動的バインディングを処理するには、より多くの情報が必要です。
詳細については、 http://www.parashift.com/c++-faq/pointers-to-members.htmlを参照してください。
C スタイルの API でメンバ関数へのポインタを使用する「古典的な」方法は、別のMyClass*
場所から取得するラッパー関数を作成することです (通常は、関数ポインタとともに渡される「userinfo」、「thunk」など)。 ) と呼び出しますmyobj->*pmf(args)
。次に、単純な関数であるため、そのラッパーへのポインターを渡すことができます。それを行うためのよりスマートな最新の方法にはbind
、ラムダが含まれます。
最初に現代的な方法で対処しましょう。これははるかに単純であり、可能であれば行うべきことだからです。まず、C++11 を使用している場合はstd::bind
、 、 またはを使用するかstd::mem_fn
、単にラムダを記述できます。
auto pt2apply = [&](double *parameter) { return my_solver->pt2apply(parameter); }
さて、このものの型は ではありませんdouble(*)(double*)
。実際、それは特定されていないものであり、おそらく言葉では言い表せないほど醜いものです. ここではauto
、それを処理することを避けるために使用しましたが、 に渡す必要がset_pt2func
あり、そのメソッドはそれをメンバー変数に格納する必要があるため、入力できる何らかの種類の型が必要です。答えはstd::function<double(double *)>
です。double *
これは、 で呼び出すことができ、を返すあらゆるもののコピーを格納できるオブジェクトですdouble
。そう:
void MySolver::set_pt2func(std::function<double(double*)> pt2function)
{
pt2Function = pt2function;
}
以上です。
現在、これには C++11 が必要です。C++98 を使用している場合はboost::lambda
、C++11 ラムダの代わりに使用できます (かなり醜いですが、基本的な考え方は同じです)。または、( Boost ライブラリに基づいたboost::bind
C++11 とほぼ同じ)、または同様の機能を持つ他の多くのライブラリのいずれかを使用できます。std::bind
そしてファンクタを格納しますboost::function
(これも C++11 とほぼ同じですがstd::function
、効率が悪い場合があります)。(TR1 を備えた C++03 を持っている場合、std
またはstd::tr1
に C++11 の機能の一部が含まれている可能性がありますが、C++11 がほぼ完成するまで、さまざまなコンパイラ メーカーがすべての詳細を整理したわけではありません。それを使用する最も簡単な方法はboost::tr1
.
なんらかの理由で C++98 に行き詰まり、Boost がない場合はstd::mem_fn
、 を使用して、結果をstd::binary_function
. これはかなり醜いので、ここでは詳細を説明しません。
それでは、古典的な解決策を見てみましょう。これは、コールバックなどに関数ポインタを使用する既存の API があり、それを変更できない場合にのみ行う価値があります。のような C 関数を使用するサンプル コードを探すことをお勧めしますqsort_r
。これは、考え方がほぼ同じであるためです。関数ポインタを「サンク」と一緒に渡し、2 つの関数は常に一緒に移動します。(最新のソリューションはすべて、基本的に、サンクを追跡する必要なく呼び出すことができる単一のオブジェクトに 2 つのものをバインドすることであり、コードを読み書きしやすくし、コンパイラがエラーをキャッチしやすくします。 )
ここで重要なのは、関数ポインターを受け取ることができる単純な C スタイルの関数であるラッパー関数が必要であるということです。サンクは、MyClass
オブジェクトへのポインターになります。私の例では、サンクを として渡しvoid *
ます。これは明らかに劣っていますが (記述するコードが多くなり、コンパイラがエラーをキャッチする可能性が低くなります)、対処する必要がある汎用 C API。
したがって、すべてをまとめると、次のようになります。
void MySolver::set_pt2func(double(*pt2function)(double*, void *), void *userinfo)
{
pt2Function = pt2function;
userInfo = userinfo;
}
double wrapMyClassPt2Apply(double *parameter, void *userinfo)
{
return ((MyClass*)userinfo)->apply_func(parameter);
}
my_solver->set_pt2func(wrapMyClassPt2Apply, (void *)my_solver);
メンバー関数へのポインターを使用していた場合は、ラッパーpt2apply
でそれを呼び出すことに注意してください。->*
しかし、もう必要ありpt2apply
ません。ラッパーは でapply_func
メンバーを呼び出すことができ->
ます。
次に、MySolver が関数を呼び出したい場合、次のようにします。
pt2Function(parameter, userInfo);
最後に 1 つ: wrapMyClassPt2Apply をメンバー関数にすることはできません。それへのポインターは、ポインターではなくメンバー関数へのポインターになります。したがって、次の 2 つの選択肢があります。
無料の機能にしてください。これは完全に合理的なことです。フリー関数は、クラスのインターフェースの一部にすることができます。または、プライベート メソッドよりも隠しておくことができます (.cpp ファイルに配置し、.h ファイルではまったく言及しないことにより)。
静的メソッドにします。これを好む人もいますが、実際に有益なのは、 MyClass.cpp の外部の誰かがset_pt2func
呼び出しを実行しようとしていて、すでに のフレンド (またはサブクラス) にMyClass
なっている場合だけです。保護されています)。