2015-01-03 43 views
0

C++中的變量函數允許用戶使用任意數量的參數調用函數。作爲示例,sscanf將輸入一個要解析的字符串,一個格式字符串和一組參數,這些參數將採用解析項目的值。 如果我想使這個範例異步呢?我需要解析一些數據,從一些字節中提取可變數量的參數。需要提取的參數在sscanf中以格式字符串指定。我想我要調用的函數是這樣的:C++ variadic回調

function <void (int, int, int)> myfunc = [=] (int a, int b, int c) 
{ 
    // Do something here! 
}; 

asyncsscanf(my_bytes, "iii", myfunc); 

asyncsscanf應該做必要的處理並完成時,我想它來調用MYFUNC與格式字符串指定正確的參數。 是否有可能做這樣的事情?

謝謝

+0

當你有原始類型時,你爲什麼需要'「iii」'? – Yakk

+0

我不確定我是否明白你的觀點。你能否更深入地解釋這一點? –

+0

您將'「iii」'傳遞給您的函數。爲什麼?'myfunc'的類型爲'function ' - 爲什麼要傳遞一個具有該信息副本的字符串?爲何使用運行時數據? – Yakk

回答

3

我不知道如何用你的方法做到這一點。

首先,asyncscanf函數的第三個參數沒有很好的類型。只有在asyncscanf主體中才可以接受(...)(函數的參數數量不限,並且不返回任何內容),但是接着傳遞給asyncscanf的參數應該是第三種類型,這可能是不可接受的。

其次,你必須按照格式(例如「iii」)來分配my_bytes。如果你有不同格式字符串的數量有限(這樣你就可以在所有可能的格式中「切換」)。但在一般情況下,我認爲這是不可能的。

但是因爲你用'variadic-templates'來標記你的問題,我假設你正在使用C++ 11/14。 也許你想讓asyncscanf的格式參數成爲一個更具可讀性的模板參數(我假定格式在編譯時間內總是已知的)。下面的解決方案的片段。

#include <functional> 
#include <iostream> 

// template parsing function, remaining_bytes parameter should contain pointer to not parsed part 
// of my_bytes 
template <typename return_type> return_type parse(const char *my_bytes, const char *&remaining_bytes); 

// specialization of parsing function for desired types, fake implementation 
template <> int parse<int>(const char *my_bytes, const char *&remaining_bytes) { 
    remaining_bytes = my_bytes; 
    return 0; 
} 

// specialization of parsing function for desired types, fake implementation 
template <> long parse<long>(const char *my_bytes, const char *&remaining_bytes) { 
    remaining_bytes = my_bytes; 
    return 1; 
} 

// declare helper template for general case 
template <typename to_be_parsed_tuple, typename parsed_tuple> 
struct asyncscanf_helper; 

// all params parsed case 
template <typename... parsed_params> 
struct asyncscanf_helper<std::tuple<>, std::tuple<parsed_params...>> { 
    void operator()(const char *, std::function<void(parsed_params...)> fun, parsed_params... params) { 
     fun(params...); 
    } 
}; 

// some params to be parsed case 
template <typename first_param_to_be_parsed, typename...to_be_parsed_params, typename... parsed_params> 
struct asyncscanf_helper<std::tuple<first_param_to_be_parsed, to_be_parsed_params...>, std::tuple<parsed_params...>> { 
    void operator()(const char *my_bytes, std::function<void(parsed_params..., first_param_to_be_parsed, to_be_parsed_params...)> fun, parsed_params... params) { 
     const char *remaining_bytes = 0; 
     first_param_to_be_parsed p1 = parse<first_param_to_be_parsed>(my_bytes, remaining_bytes); 
     asyncscanf_helper<std::tuple<to_be_parsed_params...>, std::tuple<parsed_params..., first_param_to_be_parsed>>()(remaining_bytes, fun, params..., p1); 
    } 
}; 

template <typename... params> 
void asyncscanf(const char *my_bytes, void function(params...)) { 
    asyncscanf_helper<std::tuple<params...>, std::tuple<>>()(my_bytes, function); 
} 

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

void test_fun2(int a, long b, int c) { 
    std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl; 
} 

int main() { 
    asyncscanf("1 2 3", test_fun); 
    asyncscanf("1 2 3", test_fun2); 
} 

注意有關代碼:

  • 的想法是,我們有兩個參數包:一個不解析尚未參數和一個用於已解析的參數,並通過一個從第一組到第二傳遞參數之一;當所有參數被解析時,只需調用函數
  • 如果您忘記專門爲asyncscanf編譯器會告訴您的作爲第三個參數傳遞的函數所需的類型解析函數。
  • 由於在一個模板函數中使用兩個參數包時出現問題,我必須使用模板struct asyncscanf_helper和元組來代替簡單函數模板asyncscanf_helper。
  • 我在asyncscanf_helper中使用了std :: function,因爲它更通用,你可以使用例如lambdas作爲參數,但是目前我將asyncscanf中的標準函數作爲參數類型保留,否則它的第二個參數必須顯式強制轉換爲具有適當簽名的std :: function,或者必須明確聲明模板參數。
  • 由於解析函數專門化的假實現,如果您運行代碼但不會看到預期的結果,但由於解析不是您問題的一部分,所以我將它留給了嘲笑。
+0

稍微改進的方法是通過用可推導的T指針替換'template'類型來啓用ADL的'parse'。你可以在那裏寫(初始化或不),或者忽略它並返回值。 – Yakk