seastar中apply模板的實現
我在閱讀seastar的原始碼時(這並不代表我熟悉seastar),偶然發現了seastar把tuple裡的每一個element傳給一個函式作為引數,這讓我很好奇它是如何實現的,讓我不自量力地分析一下。
seastar有個標頭檔案apply.hh包含如下程式碼:
namespace seastar {
template <typename Func, typename Args, typename IndexList>
struct apply_helper;
template <typename Func, typename Tuple, size_t... I>
struct apply_helper<Func, Tuple, std::index_sequence<I...>> {
static auto apply(Func&& func, Tuple args) {
return func(std::get<I>(std::forward<Tuple>(args))...);
}
};
template <typename Func, typename... T>
inline
auto apply(Func&& func, std::tuple<T...>&& args) {
using helper = apply_helper<Func, std::tuple<T...>&&, std::index_sequence_for<T...>>;
return helper::apply(std::forward<Func>(func), std::move(args));
}
template <typename Func, typename... T>
inline
auto apply(Func&& func, std::tuple<T...>& args) {
using helper = apply_helper<Func, std::tuple<T...>&, std::index_sequence_for<T...>>;
return helper::apply(std::forward<Func>(func), args);
}
template <typename Func, typename... T>
inline
auto apply(Func&& func, const std::tuple<T...>& args) {
using helper = apply_helper<Func, const std::tuple<T...>&, std::index_sequence_for<T...>>;
return helper::apply(std::forward<Func>(func), args);
}
以上這段程式碼實現的功能是把通過tuple打包的各個element資料拆開,並作為一個個引數去呼叫其他函式,下面展示如何使用它:
void print(int i, double f)
{
std::cout << "i:" << i << " f:" << f << `
`;
}
int main()
{
std::tuple<int, double> values(1, 2.3); //對資料1和2.3打包
apply(print, values); //呼叫 print, 把1和2.3作為呼叫引數
}
以上就是把值1和2.3打包,然後通過apply把這些打包的引數通過apply來呼叫函式print。
上面seastar中apply_helper的主模板沒有定義,只是寫了一個宣告:
template <typename Func, typename Args, typename IndexList>
struct apply_helper;
然後就是準對第三個模板引數做了部分特化,這第三個引數用的到就是index_sequence模板例項型別:
template <typename Func, typename Tuple, size_t... I>
struct apply_helper<Func, Tuple, std::index_sequence<I...>> {
static auto apply(Func&& func, Tuple args) {
return func(std::get<I>(std::forward<Tuple>(args))...);
}
};
這個apply靜態函式的祕密就是利用index_sequence模板例項化時攜帶的非型別可變引數列表”I…”,利用編譯器自動對可變引數列表的展開,把這些引數列表裡的一個個引數去呼叫std::get,而這個get則是獲取tuple裡由Index指定的element。Index是一個整形值。注意呼叫的方式,get後面跟隨一個…操作符:
std::get<I>(std::forward<Tuple>(args))...
它把index_sequence例項化的引數列表I的每一個引數去分別呼叫get,由編譯器自動重複這個過程。
例如,如果你有index_sequence<1,2,3>這個模板例項定義的型別,那麼
func(std::get<I>(std::forward<Tuple>(args))...)
的意思就是:
func(std::get<1>(std::forward(args)), std::get<2>(std::forward(args)), std::get<3>(std::forward(args)))
STL中的index_sequence_for模板的定義
template<typename... _Types>
using index_sequence_for = make_index_sequence<sizeof...(_Types)>;
具體細節我不多講了,有興趣可以去看libstdc++標頭檔案的實現,位於utility標頭檔案。
它的作用就是為給定的一個引數列表,例項化模板index_sequence得到一個新的型別,該型別的模板引數是一系列索引號列表:
例如 index_sequence_for生成一個例項化型別:
index_sequence<0,1,2>。
這樣0,1,2這個引數列表就可以用在apply一系列的模板中。
由於在apply程式碼中,模板演繹時tuple始終不丟棄,所以實際上我們在展開這些tuple element作為函式呼叫的引數時,只需要一系列序號讓編譯器去重複呼叫get模板,這就是index_sequence的作用所在。
相關文章
- 模擬js中的call、apply和bind的實現JSAPP
- js call、apply、bind的實現JSAPP
- Seastar 教程(三)AST
- Seastar 教程(二)AST
- Seastar 教程(一)AST
- apply call bind的用法與實現APP
- JavaScript之call, apply, bind, new的實現JavaScriptAPP
- OpenAPI生成器中實現自定義模板API
- call,apply,bind,new實現原理APP
- 模擬實現apply/call/bindAPP
- bind,call,apply模擬實現APP
- 前端模板的原理與實現前端
- bind、call、apply的區別與實現原理APP
- 也談如何實現bind、apply、callAPP
- bind、call、apply區別?如何實現?APP
- Scala中apply的用法APP
- js中的call、applyJSAPP
- 詳解 new/bind/apply/call 的模擬實現APP
- js之call,apply和bind的模擬實現JSAPP
- JavaScript 深入之 call 和 apply 的模擬實現JavaScriptAPP
- JavaScript 之 call和apply,bind 的模擬實現JavaScriptAPP
- JavaScript深入之call和apply的模擬實現JavaScriptAPP
- JavaScript 深入之call和apply的模擬實現JavaScriptAPP
- 前端模板引擎的實現總結前端
- js深入之實現call、apply和bindJSAPP
- javascript之模擬call以及apply實現JavaScriptAPP
- JavaScript自我實現系列(2):call,apply,bindJavaScriptAPP
- js如何是利用apply實現繼承JSAPP繼承
- 使用SQL Apply實現滾動升級SQLAPP
- 用Lambda實現模板模式模式
- 通過模板實現POI
- JavaScript中的call()和apply()JavaScriptAPP
- JavaScript 中的 apply、call、bindJavaScriptAPP
- JS中的call、apply、bindJSAPP
- 實現最簡單的模板替換
- KOA的簡易模板引擎實現方式
- 實現一個簡單的模板引擎
- ES6模板字串的實現原理字串