2012-01-13 3 views
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 кажется очень безвкусным. У кого-нибудь есть идея? Это, должно быть, обычная ситуация, но я не нашел подходящего сообщения, которое мне было понятно.

Спасибо!

Edit: На самом деле, я хотел спросить: Есть ли собственно, потокобезопасный способ настройки Pseek переменных, кроме того, что я сделал? Просто, чтобы дать понять: я делаю одномерный поиск корня на двумерной функции, но фиксируя один из двух аргументов.

+1

Сделать это шаблон? – elmo

+0

Вы не можете передать указатель на функцию-член как указатель на регулярную функцию. Можете ли вы изменить OneDimBisector, чтобы принимать разные аргументы? –

+0

@VaughCato Я могу изменить его, но он должен оставаться общим. Я не хочу, чтобы в коде использовалось много копий корневого искателя, так как он используется в разных классах! – Labbedudl

ответ

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) , но то, что он делает, так же, как и выше. (кстати, как станд :: bind1st и станд :: mem_fun устарели.)

Если вы не любите шаблоны, вы могли бы принять полиморфную функцию вместо (например, с помощью Boost.Function или зОго :: функции в C++ 11), но это будет медленнее:

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); 
+0

Не могли бы вы усилить: привязать 'this' к функции-члену в качестве дополнительной альтернативы, которая не требует написания функтора или C++ 11? –

+0

@MarkB: Да, и другие уже ответили на это. – kennytm

+0

@KennyTM Спасибо за подробный и полезный ответ! Мне нравится шаблонное решение, и оно работает хорошо для меня. – Labbedudl

3

Одним из способов было бы изменить подпись корневого искателя (добавить #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); 

вы вызываете его таким же образом, но каждый раз, когда у вас есть новый тип функции, новый экземпляр шаблона создается в вашем компиляторе.

«Традиционное» решение должно состоять в том, чтобы иметь свободную (или статическую) функцию, которая принимает дополнительный аргумент экземпляра.


Update: "Традиционные решения":

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);

+0

Спасибо за ваши предложения. В том числе «function» и 'stdlib', я получаю' error: 'function' не является членом 'std'', '' bind 'не является членом' std'' и т. Д. Что касается второго (Я не знал о шаблонах!), Означает ли это, что каждый раз, когда я вызываю 'T_PS()', будет новый экземпляр класса 'EOS'? Этого следует избегать из-за накладных расходов (чтение данных и т. Д.). Что вы подразумеваете под «дублирующим кодом»? Наконец, не могли бы вы рассказать о «традиционном» решении? Это может быть очевидным, о котором я не знал. – Labbedudl

+0

Это новое, поэтому вам может потребоваться сказать '-std = C++ 0x' в GCC или использовать' 'и' std :: tr1 :: function' и т. Д. Новый экземпляр шаблона создаваться для каждого нового * типа * функции, которую вы передаете. Это не накладные расходы, только раздувание кода. –

+0

Я добавил пример «традиционного» обратного вызова. Обратите внимание на дополнительные 'void *' для контекстных данных. –

0

Проблема вы сталкиваетесь в том, что указатель на функцию-то другое указателю член funcgtion.

Обычный (Java World) Подход, чтобы обойти проблему, заключается в использовании шаблона стратегии (удовольствие от Биссектора - это реализация стратегии).

Обычный 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));                                              
Смежные вопросы