2009-09-04 90 views
1

讀完this answer後,我想我有一個解決方案。至少答案是我想要做的,但是我在執行時遇到問題。當使用boost :: function :: target獲取函數指針時的空指針

這裏是什麼,我試圖做

typedef map<string, double*> myMap; 
typedef int (*ftwpt)(const char*, const struct stat*, int); 
typedef boost::function<int(const char*, const struct stat*, int)> MyFTWFunction; 

int myFunction(const char*, const struct stat*, int, myMap*); 

int main() 
{ 
myMap m_map; 
char tmpdir[] = "/tmp/mytmp"; 

MyFTWFunction f = boost::bind(myFunction,_1,_2,_3, &m_map); 

ftwpt* fpt = f.target<ftwpt>(); 
if (fpt) 
    status = ftw(tmpdir, *fpt, 50); 
else 
{ 
    cout << "Boost could not perform runtime conversion on function pointer" << endl; 
    return (EXIT_FAILURE); 
} 
} 

程序沒有錯誤或警告編譯提綱,但我得到一個空指針(FPT)從f.target返回();在運行時。從上面的stackoverflow問題鏈接的引用看來,如果boost無法執行運行時轉換,則返回空指針。但我不知道爲什麼Boost可能無法執行運行時轉換。有任何想法嗎?

+0

@Konrad對其他問題也解釋了爲什麼你的代碼失敗:http://stackoverflow.com/questions/282372/demote-boostfunction-to-a-plain-function-pointer/512233 #512233 – 2009-09-04 20:01:45

+0

@HazyBlueDot - 這個問題是關於C++,而不是C.請嘗試正確標記它。 – 2009-09-05 02:03:56

回答

2

爲此,您需要知道存儲到boost::function對象中的綁定表達式的確切類型。對象boost::bind(....)返回的是一些奇怪的表達式模板,而不是函數指針。

要理解爲什麼這是需要考慮的功能是如何提高::原則

struct base { virtual ~base() { } }; 

template<typename T> 
struct derived : base { 
    derived(T t):t(t) { } 
    T t; 
}; 

struct function { 
    template<typename T> 
    function(T t) { 
    base *b = new derived<T>(t); 
    } 

    template<typename T> 
    T *target() { 
    if(typeid(*b) == typeid(derived<T>)) 
     return &static_cast< derived<T>* >(b)->t; 
    return 0; 
    } 

    base *b; 
}; 

這是最根本的結構來實現,而不operator()膨脹 - 很像boost::any。該機制稱爲type-erasure:構造函數接受任意類型的對象,然後將一個對象封裝到一個對象中,您可以通過虛函數調用(boost::function被優化爲地獄,使用自己的vtable和堆棧分配來避免小型的new等)。

對於函數指針,這很好用,因爲您知道分配給boost::function對象的函數的類型。但對於複雜的可調用對象,它不再適用。

爲了能夠看到它的工作,並看到它只是函數指針的工作沒有,但也與綁定表達式考慮下面的代碼

template<typename T> 
struct id { typedef T type; }; 

template<typename T> 
id<T> make_id(T) { return id<T>(); } 

struct any_type { 
    template<typename T> 
    operator id<T>() const { return id<T>(); } 
}; 

template<typename T, typename Fn> 
T *get_target(boost::function<Fn> &f, id<T>) 
{ return f.template target<T>(); } 

void f(int a, int b) { std::cout << a << " " << b << std::endl; } 

int main() { 
    boost::function<void(int)> g = boost::bind(&f, _1, 10); 
    (*get_target(g, true ? any_type() : make_id(boost::bind(&f, _1, 10))))(2); 
} 

get_target你知道什麼boost::bind回報的類型。您可以使用它調用target調用並返回包裝在boost::function內的對象。在main之內,我們然後調用綁定表達式。請閱讀Eric Niebler的文章Conditional Love以瞭解此代碼片段的工作原理。

+0

那麼什麼是適當的方式去做這件事?我想使用一個函數作爲ftw的回調函數,但我也想傳遞一個指向數據結構的指針,這個數據結構就是回調函數的結果。看起來Boost.Bind應該能夠解決這種問題,所以我該如何去做呢? – HazyBlueDot 2009-09-04 19:48:58

+0

爲什麼不製作'ftw'模板?然後它可以通過'boost :: bind'上的'boost :: function'函數指針接受任何東西。如果你不想使它成爲一個模板,爲什麼不使用'boos :: function'作爲它的參數類型?這就是它的目的:) – 2009-09-04 19:51:10

+0

我應該提供更多細節。 ftw是共享庫中的一個函數(請參閱linux.die.net/man/3/ftw上的man ftw),我無法控制其參數列表。 – HazyBlueDot 2009-09-04 22:53:28

1

另一個答案指出爲什麼你的代碼不起作用。這是一種非常難看的解決方案,適用於某些有限的情況。

typedef int (*ftwpt)(const char*, const struct stat*, int); 
typedef boost::function<int(const char*, const struct stat*, int)> MyFTWFunction; 

template <MyFTWFunction *callback> 
class callback_binder { 
public: 
    static int callbackThunk(const char *s, const struct stat *st, int i) { 
     return (*callback)(s, i); 
    } 
}; 

extern void register_callback(callback_t f); 

int random_func(const char *s, const struct stat *st, int i) 
{ 
    if (s && *s) { 
     return i; 
    } else { 
     return -1; 
    } 
} 

MyFTWFunction myfunc; 

int main(int argc, const char *argv[]) 
{ 
    myfunc = random_func; 
    register_callback(&callback_binder<&myfunc>::callbackThunk); 
    return 0; 
} 

使用指針作爲模板參數的規則要求作爲參數傳入的指針是指向全局變量的指針。當然,該全局變量可以在匿名名稱空間中聲明。

這很醜陋,如果你想有幾個可能的myMap實例可能會被調用,同時你需要儘可能多的全局MyFTWFunction變量同時發生myMap的實例。大多數情況下,這會自動創建使用全局變量內容填充缺失參數的thunk函數。

這裏是一個版本,是少了很多靈活的,做大致相同的事情這條狹窄的情況下,可能使之更明顯這是怎麼回事上:

#include <map> 
#include <string> 

using ::std::map; 
using ::std::string; 
typedef map<string, double*> myMap; 
typedef int (*callback_t)(const char *, struct stat *st, int); 

int myFunction(const char*, struct stat *st, int, myMap*); 

template <myMap **map_ptr> 
class myMap_binder { 
public: 
    static int call_my_function(const char *s, struct stat *st, int i) { 
     return myFunction(s, st, i, *map_ptr); 
    } 
}; 

extern void register_callback(callback_t f); 

myMap *mainmap; 
myMap *othermap; 

int main(int argc, const char *argv[]) 
{ 
    myMap m_map; 
    myMap m_map2; 
    mainmap = &m_map; 
    othermap = &m_map2; 
    register_callback(&myMap_binder<&mainmap>::call_my_function); 
    register_callback(&myMap_binder<&othermap>::call_my_function); 
    return 0; 
} 

正如你可以看到myMap_binder是一個模板它會自動生成填充全局變量內容的thunk函數,以調用您的回調函數。