2015-06-30 65 views
5

此問題僅用於測試目的,僅此而已。將void函數指針指向void(*)(),然後重鑄爲原始類型

我目前正試圖用不同數量的參數存儲函數指針(這些參數可以有不同的類型)。

基本上,我已經編碼下面的代碼段在C++ 11:

#include <functional> 
#include <iostream> 

void fct(int nb, char c, int nb2, int nb3) { 
    std::cout << nb << c << nb2 << nb3 << std::endl; 
} 

template <typename... Args> 
void call(void (*f)(), Args... args) { 
    (reinterpret_cast<void(*)(Args...)>(f))(args...); 
} 

int main(void) { 
    call(reinterpret_cast<void(*)()>(&fct), 42, 'c', 19, 94); 
} 

我轉換void(*)(int, char, int, int)函數指針爲通用void(*)()函數指針。然後,通過使用可變參數模板參數,我簡單地將函數指針重新初始化爲其原始類型,並用一些參數調用該函數。

此代碼編譯並運行。大多數時候,它顯示出良好的價值。然而,這段代碼在Mac OS下給我一些Valgrind錯誤(關於未初始化的值),它有時會顯示一些意外的垃圾。

==52187== Conditional jump or move depends on uninitialised value(s) 
==52187== at 0x1004E4C3F: _platform_memchr$VARIANT$Haswell (in /usr/lib/system/libsystem_platform.dylib) 
==52187== by 0x1002D8B96: __sfvwrite (in /usr/lib/system/libsystem_c.dylib) 
==52187== by 0x1002D90AA: fwrite (in /usr/lib/system/libsystem_c.dylib) 
==52187== by 0x100025D29: std::__1::__stdoutbuf<char>::overflow(int) (in /usr/lib/libc++.1.dylib) 
==52187== by 0x10001B91C: std::__1::basic_streambuf<char, std::__1::char_traits<char> >::xsputn(char const*, long) (in /usr/lib/libc++.1.dylib) 
==52187== by 0x10003BDB0: std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> > std::__1::__pad_and_output<char, std::__1::char_traits<char> >(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> >, char const*, char const*, char const*, std::__1::ios_base&, char) (in /usr/lib/libc++.1.dylib) 
==52187== by 0x10003B9A7: std::__1::num_put<char, std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> > >::do_put(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> >, std::__1::ios_base&, char, long) const (in /usr/lib/libc++.1.dylib) 
==52187== by 0x1000217A4: std::__1::basic_ostream<char, std::__1::char_traits<char> >::operator<<(int) (in /usr/lib/libc++.1.dylib) 
==52187== by 0x1000011E8: fct(int, char, int, int) (in ./a.out) 
==52187== by 0x1000013C2: void call<int, char, int, int>(void (*)(), int, char, int, int) (in ./a.out) 
==52187== by 0x100001257: main (in ./a.out) 

我覺得這很好奇,因爲當我調用函數時,我已經將函數指針改寫爲原始類型。我認爲它類似於將數據類型轉換爲void*,然後將其重新轉換爲原始數據類型。

我的代碼有什麼問題?我們不能將指針指向void(*)()指針的函數指針,然後將此指針重新指向原始函數指針簽名?

如果不是,還有其他方法可以實現嗎?我對std::bind不感興趣,它不是我想要的。

+0

你的意思是函數指針?不是指向成員函數的指針? – MSalters

+1

我無法重現。這是你的真實的文字測試案例嗎? –

+0

是的,這正是我已經運行的代碼(在macos優勝美地)。而且,如果我用std :: string替換最後一個參數,我得到了垃圾。 –

回答

1

走出去的肢體和猜測你做了什麼讓它失敗...

#include <functional> 
#include <iostream> 

void fct(int nb, char c, int nb2, std::string nb3) { 
    std::cout << nb << c << nb2 << nb3 << std::endl; 
} 

template <typename... Args> 
void call(void (*f)(), Args... args) { 
    (reinterpret_cast<void(*)(Args...)>(f))(args...); 
} 

int main(void) { 
    call(reinterpret_cast<void(*)()>(&fct), 42, 'c', 19, "foobar"); 
} 

這將失敗,因爲「foobar的」永遠不會被轉換爲std::string ...如何編譯器知道它通過Args...

我不確定std::string究竟是如何被調用者推到調用堆棧上的(字符串引用將作爲指針推入),但我懷疑它不僅僅是指向char*的單個指針。當被呼叫者彈出指向char*期望整個string成員的指針時,它會嚇壞了。

我認爲,如果你改變

void fct(int nb, char c, int nb2, char* nb3) 

call(reinterpret_cast<void(*)()>(&fct), 42, 'c', 19, std::string("foobar")); 

那麼威力工作。

+1

邏輯!謝謝你的解釋。查看variadic列表的處理方式可能是一個好主意。我認爲我得到的valgrind錯誤是由於macOSX上valgrind的實現不穩定造成的。 –

1

你說你還對其他實現感興趣。就個人而言,即使它完美運行,我也不會實現這種方式,函數指針和reinterpret_casts都是我試圖避免的。我沒有測試此代碼,但我的想法是:

#include <functional> 
#include <iostream> 
#include <boost/any.hpp> 

template <typename... Args> 
void call(boost::any clbl, Args... args) { 
    auto f = boost::any_cast<std::function<void(Args...)>>(clbl); 
    f(args...); 
} 

int main(void) { 
    std::function<void(int, char, int, int)> func = fct; 
    call(boost::any(func), 42, 'c', 19, 94); 
} 

編輯:此代碼,您的FCT的定義相結合,正常運行,並且在運行Valgrind的清潔在Fedora上,與clang35編譯。

+1

代碼更清晰,但我認爲這隻隱藏了reinterpret_cast(這肯定是在boost :: any_cast實現中內部完成的)。不過,感謝你的例子,我至今沒有聽說過boost :: any和boost :: any_cast! –

+1

它實際上是一個static_cast內部。甚至有boost的變體::任何在內部使用動態模式的變體......如果你搞砸了,很容易得到一個異常而不是segfault。 –

+0

很高興知道,謝謝! –