2015-10-26 23 views
7

bir işlev oluşturun. Akılda çok özel bir örnek var: GSL (GNU Bilimsel Kütüphanesi), kullanılan ana işlev türü (entegrasyon, kök bulma, ...) tip double(*)(double, void *)Genel bir durumdan başka bir tane daha

Say I double a_squared(double a) {return a*a}; bir gsl_function oluşturmak istediğiniz bir öznitelik function var gsl_function vardır. a__squared 'ın tip I argüman (double(*)(double) f) alarak ve convert(f)(double a, NULL) == f(a)

tatmin Ama başka işlevi tanımlayın olamaz gibi bazı araştırma sonrasında, öyle görünüyor olacağını tip double(*)(double, void *) bir nesne dönen bir convert fonksiyonunu oluşturmak istiyorum double(*)(double) olan benim convert işlevi. Nasıl devam edilir? Bir std::function bir işlev işaretçisi elde etmek yolu yoktur, çünkü ek std::function dayalı bir şey kullanamazsınız (ve bu yakalar kullanarak lambdas dışladı -

+0

Bu niçin 'C++' olarak etiketlenmiş? GSL bir C kütüphanesidir ve bir C tipi fonksiyon göstericisine ihtiyaç duyar. Bu yüzden C++ işlevselliğine dayalı herhangi bir çözüm (std :: bind gibi) başarısız olur. – Walter

+0

Küçük bir sarmalayıcı @Walter - kullanıyorsanız ve bu lambda ve 'std :: bind' içerir C++ işlevselliği başarısız. Benim cevabım –

cevap

4

GSL API ham işlev işaretçisi geçmek gerek ölçüde seçeneklerinizi sınırlar , bu da düzgün bir çözüm sunacaktı).

Bu kısıtlamalar göz önünde bulundurulduğunda, burada statik bir sarmalayıcı sınıfını kullanan olası bir çözüm var. Bu sınıfın içeriğini bir isim alanına koyabilirdiniz, ama en azından sınıfın kullanılması en azından bir kapsülleme şekli verir.

typedef double gsl_function_type(double, void*); // typedef to make things a bit more readable... 

// static class to wrap single-parameter function in GSL-compatible interface 
// this really just serves as a namespace - there are no non-static members, 
// but using a class lets us keep the details private 
class Convert 
{ 
    Convert() = delete; // don't allow construction of this class  

    // pointer to the function to be invoked 
    static double (*m_target)(double); 

    // this is the function we'll actually pass to GSL - it has the required signature 
    static double target(double x, void*) { 
     return m_target(x); // invoke the currently wrapped function 
    } 

public: 
    // here's your "convert" function 
    static gsl_function_type* convert(double (*fn)(double)) { 
     m_target = fn; 
     return ⌖ 
    } 
}; 

Burada canlı bir örnek var: http://coliru.stacked-crooked.com/a/8accb5db47a0c51d Bir C tarzı fonksiyon işaretçisi sağlamak üzere (yerine C++) C kullanarak GSL en (kötü) tasarım seçimi tarafından tuzağa konum

+1

'm_target'ı' statik' sınıf üyesi olarak görmek, bunun global bir değişken gerektirdiğinden emin değildir. Dahası, bu sadece bir fonksiyonun aynı anda dönüştürülmesine izin verir (örneğin, bir kök bulucu gerektiren bir fonksiyonun sayısal integralini hesaplamak istediğinizde, * ve * bu mekanizmayı her ikisini de kullanmak istiyorsanız). – Walter

+0

@Walter, bağlandığım sarıcı bu sınırlamaya sahip değil (her seferinde bir işlev). –

1

. Böylece, (C++ stili) işlev nesnelerini (functor) kullanamazsınız, ancak işaretçiyi gerçek bir işleve sağlamalıdır ve kişi, bir kişiyi, functor'ı benzer şekilde aynı şekilde oluşturamaz.

// from some gsl header: 
extern "C" { 
    typedef double gsl_function(double, void*); 
    // calls func(arg,data_passed_to_func) 
    double gsl_api_function(gsl_function*func, void*data_passed_to_func); 
} 

// in your source code 
double(*target_func)(double);   // global variable can be hidden in some namespace 
extern "C" { 
    double funtion_calling_target(double, void*) 
} 
double funtion_calling_target(double arg, void*) 
{ 
    return target_func(arg); 
} 
bool test(double x, double(*func)(double)) 
{ 
    target_func = func; 
    return x < gsl_api_function(function_calling_target,0); 
} 

(gizleme target_func olarak statik üyesi:

Aslında küresel değişkeni çağıran belirli gsl_function tanımlamak daha sonra gerçek fonksiyonunu (a_squared) depolamak ve küresel bir değişkeni kullanabilirsiniz ( tavsiye edilmez) bazı sınıfların atkins'in cevabı gibi, hala global bir değişken gerektirir). Bu çalışır, ancak zayıftır, çünkü 1) bu mekanizma global bir değişken gerektirir ve 2) sadece bir hedef fonksiyonun herhangi bir zamanda kullanılmasına izin verir (bunu sağlamak zor olabilir).

(Önerilen) Ancak, başka bir işlev işaretçisini argüman olarak alan ve bunu veri öğesi olarak geçiren özel bir işlev tanımlayabilirsiniz. Bu, aslında gsl_function'un tasarımının ardındaki fikirdi: void*, işlevin gerektirebileceği herhangi bir yardımcı veriyi gösterebilir. Bu veriler başka bir işlev olabilir.

// your header 
extern "C" { 
    double function_of_double(double, void*); 
} 
inline double function_of_double(double arg, void*func) 
{ 
    typedef double(*func_of_double)(double); 
    return reinterpret_cast<func_of_double>(func)(arg); 
} 

// your application 
bool test(double x, double(*func)(double)) 
{ 
    return x < gsl_api_function(function_of_double, (void*)(func)); 
} 

Bu, genel bir değişken gerektirmez ve istediğiniz kadar çok sayıda eş zamanlı işlevle çalışır. Tabii ki, burada, her mantıklı C++ programcısı olan bir şey olan void* ile uğraşırken, void* manipülasyonlarına dayanan korkunç bir C kütüphanesi kullanıyorsunuz.

1

Bu lambda tabanlı girişimleri ekleyeceğimi düşündüm.

Bu prensipte çalışıyor:

// function we want to pass to GSL 
double a_squared(double a) { return a*a; } 

typedef double gsl_function_type(double, void*); // convenient typedef 

// lambda wrapping a_squared in the required interface: we can pass f directly to GSL 
gsl_function_type* f = [](double x, void*) { return a_squared(x); }; 

Ama biz gerçekten herhangi bir işleve bu uygulamak için bir yöntem yazmak istiyorum. Böyle bir şey:

gsl_function_type* convert(double (*fn)(double)) 
{ 
    // The lambda has to capture the function pointer, fn. 
    return [fn](double x, void*) { return fn(x); }; 
} 

Ancak lambda şimdi fn (ilk örnekte statik işlevi a_squared aksine) otomatik depolama süresine sahip olduğundan, işaretçi fn yakalamak zorundadır. Bu, derlemeyi yapmaz çünkü bir yakalama kullanan bir lambda, fonksiyonumuzun dönüş değeriyle gerektiği gibi basit bir fonksiyon göstericisine dönüştürülemez. Bu lambda'yı geri alabilmek için std::function'u kullanmak zorundayız, ama bundan bir ham fonksiyon gösterici elde etmenin bir yolu yok, bu yüzden burada kullanım yok. olarak,

#include <iostream> 
using namespace std; 

typedef double gsl_function_type(double, void*); // convenient typedef 

// example GSL function call 
double some_gsl_function(gsl_function_type* function) 
{ 
    return function(5.0, nullptr); 
} 

// function we want to pass to GSL 
double a_squared(double a) { return a*a; } 

// macro to define an inline lambda wrapping f(double) in GSL signature 
#define convert(f) [](double x, void*) { return f(x); } 

int main() 
{ 
    cout << some_gsl_function(convert(a_squared)) << endl; 
} 

Şahsen:

#define convert(f) [](double x, void*) { return f(x); } 

Bu da beni böyle bir şey yazmanıza olanak tanır:

Yani işe bu olsun başardınız tek yolu önişlemci makro kullanmaktır Makroları kullanmayı sevmediğimden, bunu diğer öneriye tercih ederim. Özellikle, bu fikirle işaret edilen @Walter problemlerini çözer.

+1

Bu, bir C++ lambda'nın fonksiyon gösterici üzerinden C bağlantısına sahip bir işleve geçebileceğinizi varsayar. Bunun standartlar tarafından desteklenip desteklenmediğinden emin değilim, ancak bunu tavsiye etmem. – Walter

1

Önceki yanıtlar - kabul edilen olanı içeren - doğru gibi görünüyor, ancak diğer işlev türlerini gsl_function öğesine dönüştürmeniz gerektiğinde (örneğin üye işlevleri dahil), bunlar yeterince genel değillerdir. Yani, daha güçlü bir alternatif ekleyeyim. Eğer here tarif edilen sarıcı kullanın

, o zaman iki basit çizgiler

// Example 
gsl_function_pp Fp([&](double x){return a_squared(x);}); 
gsl_function *F = static_cast<gsl_function*>(&Fp); 

Bu, ilgili dönüşüm sorunları çözer içinde gsl_functions herhangi bir C++ lambdas dönüştürebilir. Std :: bind ve std :: işlevlerini de kullanabilirsiniz.

+0

Evet, bunu beğendim. – atkins

İlgili konular