0

以下のコードでは、メンバー関数を汎用ルートファインダーに渡す方法を理解できません。

#include <stdio.h>

double OneDimBisector(double (*fun)(float), float a, float b, float tol){
 double val;                                                                                                                                                                           
 val = (*fun)(0.5*(b-a));    // actually: do proper bisection                                                                                                                          
 return val;                                                                                                                                                                           
}                                                                                                                                                                                      

class EOS {                                                                                                                                                                            
 public:                                                                                                                                                                               
  double S_array[10][10];    // actually: filled by constructor                                                                                                                        
  double S(double T, double P);                                                                                                                                                        

  double T_PS(double P, double S);                                                                                                                                                     
  double functForT_PS(double T);                                                                                                                                                       
  double (EOS::*pfunctForT_PS)(double);                                                                                                                                                
  double Sseek, Pseek;                                                                                                                                                                 
};                                                                                                                                                                                     


double EOS::S(double T, double P){                                                                                                                                                     
  double val = T+P;          // actually: interpolate in S_array                                                                                                                       
  return val;                                                                                                                                                                          
}                                                                                                                                                                                      

double EOS::functForT_PS(double T){                                                                                                                                                    
 return S(T,Pseek)-Sseek;                                                                                                                                                              
}                                                                                                                                                                                      

// Find T from P and S (T is invertible), assuming the intervals are ok
double EOS::T_PS(double P, double S0){
  double Tmin = 2., Tmax = 7., T1, tol=1e-8;
  pfunctForT_PS = &EOS::functForT_PS;
  Sseek = S0;
  Pseek = P;

  printf("\n %f\n", (*this.*pfunctForT_PS)(4.));         // no problem
  T1 = OneDimBisector(pfunctForT_PS, Tmin, Tmax, tol);   // wrong type for pfunctForT_PS

  return T1;
}

int main() {
  double P=3., S=8;
  EOS myEOS;

  printf("\n %f %f %f\n",P,S,myEOS.T_PS(P,S));
}

ルートファインダーはこのクラスに固有のものではないため、メンバーにしたくありません。すべてを作成するソリューションはstatic非常にエレガントではないようです。誰かがアイデアを持っていますか?これはよくある状況ですが、私にも理解できる関連する投稿は見つかりませんでした。

ありがとう!

編集:Pseek実際、私はまた尋ねることを意味しました:私がしたこと以外に変数を設定する適切でスレッドセーフな方法はありますか?明確にするために、私は2次元関数で1次元の求根アルゴリズムを実行していますが、2つの引数の1つを修正しています。

4

3 に答える 3

3

1つの方法は、ルートファインダーの署名を変更することです(追加#include <functional>)。

double OneDimBisector(std::function<double(float)> f, float a, float b, float tol);

次に、次のコマンドで呼び出しますbind

T1 = OneDimBisector(std::bind(pfunctForT_PS, this, std::placeholders::_1),
                    Tmin, Tmax, tol);

これには一定のオーバーヘッドが伴います。重複するコードがたくさんあることを気にしない場合は、関数をテンプレートにすることができます。

template <typename Func>
double OneDimBisector(Func f, float a, float b, float tol);

同じ方法で呼び出しますが、新しい関数型を使用するたびに、テンプレートの新しいインスタンスがコンパイルで作成されます。

「従来の」解決策は、追加のインスタンス引数を受け入れる無料の(または静的な)関数を持つことです。


更新:「従来のソリューション」:

double OneDimBisector(double(*f)(float, void *), void * data, ...);

double EOSBisect(float f, void * data)
{
    EOS * e = static_cast<EOS *>(data); // very "traditional"
    return e->functorForT_PS(f);
}

使用法:T1 = OneDimBisector(EOSBisect, this, Tmin, Tmax, tol);

于 2012-01-13T15:05:55.613 に答える
1

thisメンバー関数ポインターを関数ポインターとして渡すことはできません。後者には、メンバー関数ポインターを適切に呼び出すためのコンテキストポインター( )がないためです。

これを解決する一般的な方法(標準C ++ライブラリの場合と同様)は、テンプレートを使用することです。

template <typename F>
double OneDimBisector(F fun, float a, float b, float tol){
   double val;
   val = fun(0.5*(b-a));
   return val;                                                        
}

関数オブジェクトを渡します

struct Evaluator
{
   EOS* this_;

   Evaluator(EOS* this_) : this_(this_) {}  // constructor

   double operator()(double value) const    // call the function
   {
       return this_->functForT_PS(value);
   }
};

T1 = OneDimBisector(Evaluator(this), Tmin, Tmax, tol);

を使用することもできますがstd::bind1st(std::mem_fun(&EOS::functForT_PS), this)、その機能は上記の構造とまったく同じです。(ところで、std::bind1stとstd::mem_funの両方が非推奨になりました。)

テンプレートが気に入らない場合は、代わりにポリモーフィック関数を受け入れることができます(たとえば、C++11のBoost.Functionまたはstd::functionを使用)が、速度は遅くなります。

double OneDimBisector(const boost::function<double(double)>& fun,
                      float a, float b, float tol)
{
    return fun(0.5 * (b-a));
}

最後に、C ++ 11を使用できる場合は、OneDimBisectorの呼び出しでラムダ関数を使用できます。

T1 = OneDimBisector([=](double value){ return functForT_PS(value); },
                    Tmin, Tmax, tol);
于 2012-01-13T15:15:10.357 に答える
0

あなたが直面している問題は、関数ポインタがメンバー関数ポインタとは異なるものであるということです。

問題を回避するための一般的な(Java World)アプローチは、戦略パターンを使用することです(Bisectorの楽しみは、戦略の実装です)。

一般的なC++アプローチは、ファンクター/バインディングを使用することです。たとえば、ブーストを使用します。

typedef boost::function<double (double)> MyFun;
double OneDimBisector(const MyFun & fun, float a, float b, float tol){
    double val;                                                                                                                                                                           
    val = fun(0.5*(b-a));    // actually: do proper bisection                                                                                                                          
    return val;                                                                                                                                                                           
}

// Calling
T1 = OneDimBisector (boost::bind (&EOS::functForT_PS, *this), Tmin, Tmax, tol));                                                                                                                                                                                      
于 2012-01-13T15:06:41.497 に答える