最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++11 lambda表達(dá)式在回調(diào)函數(shù)中的使用方式

 更新時(shí)間:2022年11月05日 10:13:51   作者:MC-Zhang  
這篇文章主要介紹了C++11 lambda表達(dá)式在回調(diào)函數(shù)中的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

在回調(diào)函數(shù)中使用lambda表達(dá)式的好處,在于可以利用C++的RAII機(jī)制來做資源的自動(dòng)申請(qǐng)釋放,避免手動(dòng)管理出錯(cuò)。

一、lambda表達(dá)式在C++異步框架中的應(yīng)用

1. 一個(gè)boost asio的例子

//
// async_tcp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

class session
  : public std::enable_shared_from_this<session>
{
public:
  session(tcp::socket socket)
    : socket_(std::move(socket))
  {
  }

  void start()
  {
    do_read();
  }

private:
  void do_read()
  {
    auto self(shared_from_this());
    socket_.async_read_some(boost::asio::buffer(data_, max_length),
        [this, self](boost::system::error_code ec, std::size_t length)
        {
          if (!ec)
          {
            do_write(length);
          }
        });
  }

  void do_write(std::size_t length)
  {
    auto self(shared_from_this());
    boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
        [this, self](boost::system::error_code ec, std::size_t /*length*/)
        {
          if (!ec)
          {
            do_read();
          }
        });
  }

  tcp::socket socket_;
  enum { max_length = 1024 };
  char data_[max_length];
};

class server
{
public:
  server(boost::asio::io_context& io_context, short port)
    : acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
  {
    do_accept();
  }

private:
  void do_accept()
  {
    acceptor_.async_accept(
        [this](boost::system::error_code ec, tcp::socket socket)
        {
          if (!ec)
          {
            std::make_shared<session>(std::move(socket))->start();
          }

          do_accept();
        });
  }

  tcp::acceptor acceptor_;
};

int test()
{
  try
  {

    boost::asio::io_context io_context;

    server s(io_context, 8080);

    io_context.run();
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

int main(int argc, char** argv){
  test();
}

其中在async_read_some函數(shù)的第二個(gè)參數(shù)使用了lambda表達(dá)式作為參數(shù),并且閉包捕獲了self變量。這樣做的目的是通過shared_ptr增加this的引用計(jì)數(shù)。 在server::do_accept函數(shù)中存在一句代碼:

std::make_shared(std::move(socket))->start();

這里有一個(gè)表達(dá)式亡值,屬于shared_ptr類型。當(dāng)啟動(dòng)start()方法后,會(huì)為該智能指針?biāo)芾淼膶?duì)象增加一次引用計(jì)數(shù)。 所以在離開作用域后,shared_ptr析構(gòu)不會(huì)導(dǎo)致實(shí)際的session對(duì)象析構(gòu)。

最終當(dāng)不再繼續(xù)注冊(cè)異步讀寫回調(diào)時(shí)(在這里的代碼中,當(dāng)讀寫出現(xiàn)錯(cuò)誤時(shí)),即放棄該連接的session時(shí), 智能指針的引用計(jì)數(shù)降為0,觸發(fā)session對(duì)象的析構(gòu)。

void do_read()
{
    auto self(shared_from_this());
    socket_.async_read_some(boost::asio::buffer(data_, max_length),
        [this, self](boost::system::error_code ec, std::size_t length)
        {
          if (!ec)
          {
            do_write(length);
          }
        });
}
void do_accept()
{
    acceptor_.async_accept(
        [this](boost::system::error_code ec, tcp::socket socket)
        {
          if (!ec)
          {
            std::make_shared<session>(std::move(socket))->start();
          }

          do_accept();
        });
}

這樣使用lambda表達(dá)式在資源管理上帶來了傳統(tǒng)的函數(shù)指針不具備的優(yōu)勢(shì)。因?yàn)楫?dāng)回調(diào)函數(shù)被執(zhí)行時(shí),使用傳統(tǒng)寫法需要在每個(gè)條件分支下都要考慮到資源的釋放。

2. C++ http框架cutelyst在異步執(zhí)行PostgreSQL數(shù)據(jù)庫sql請(qǐng)求的例子

void SingleDatabaseQueryTest::dbp(Context *c)
{
    const int id = (qrand() % 10000) + 1;

    ASync async(c);
    static thread_local auto db = APool::database();
    db.exec(APreparedQueryLiteral(u"SELECT id, randomNumber FROM world WHERE id=$1"),
                           {id}, [c, async] (AResult &result) {
        if (Q_LIKELY(!result.error() && result.size())) {
            auto it = result.begin();
            c->response()->setJsonBody(QByteArray::fromStdString(
                            picojson::value(picojson::object({
                                                {"id", picojson::value(double(it[0].toInt()))},
                                                {"randomNumber", picojson::value(double(it[1].toInt()))}
                                            })).serialize()));
            return;
        }

        c->res()->setStatus(Response::InternalServerError);
    }, c);
}

其中ASync的構(gòu)造函數(shù)作用是斷開事件處理鏈,即當(dāng)這個(gè)dbp函數(shù)返回時(shí),對(duì)該context不去向?yàn)g覽器發(fā)出http響應(yīng)。代碼大致為:

ASync::ASync(Context *c)
{
    c->detachAsync();
}

析構(gòu)函數(shù)作用是恢復(fù)事件處理鏈,即通知eventloop,可以對(duì)該context發(fā)送http響應(yīng)了。大致為:

ASync::~ASync()
{
    c->attachAsync();
}

通過在異步sql執(zhí)行函數(shù)中注冊(cè)一個(gè)lambda表達(dá)式,lambda表達(dá)式捕獲一個(gè)外部變量,利用RAII機(jī)制,能夠?qū)崿F(xiàn)在異步sql執(zhí)行完畢后再進(jìn)行http響應(yīng)。這是lambda表達(dá)式閉包捕獲變量的優(yōu)勢(shì)。

二、如何在C-style注冊(cè)回調(diào)函數(shù)中使用lambda表達(dá)式?

有一個(gè)c庫,其中存在一個(gè)注冊(cè)回調(diào)函數(shù):

void register_callback(void(*callback)(void *), void * context);

希望可以注冊(cè)C++11的lambda表達(dá)式,而且是帶捕獲變量的lambda表達(dá)式,因?yàn)橐貌东@變量來維持狀態(tài)。

首先分析一下這個(gè)注冊(cè)函數(shù):

這個(gè)注冊(cè)回調(diào)函數(shù)第一個(gè)參數(shù)是C-style函數(shù)指針,不帶狀態(tài)。第二個(gè)參數(shù)void *context ,攜帶函數(shù)執(zhí)行的狀態(tài)。

這樣每次函數(shù)執(zhí)行的時(shí)候,會(huì)將context傳遞進(jìn)來,做到了持續(xù)保持對(duì)狀態(tài)的訪問和修改。

void register_callback( void(*callback)(void*), void * p ) {
  //這里是一個(gè)簡單的模擬。實(shí)際中可能會(huì)多次調(diào)用callback函數(shù)。
  callback(p); //  測(cè)試
  callback(p);
}

對(duì)于將lambda表達(dá)式與函數(shù)指針之間的轉(zhuǎn)換,如果沒有捕獲變量可以直接轉(zhuǎn)換。

void raw_function_pointer_test() {
  int x = 0;
  auto f = [](void* context)->void {
      int *x = static_cast<int*>(context);
      ++(*x); 
  };
  register_callback(f, &x);
  std::cout << x << "\n";
}

調(diào)用代碼

raw_function_pointer_test();

輸出:2

但是這種轉(zhuǎn)換方式,完全屬于C風(fēng)格,充滿了類型不安全。如果想要使用lambda表達(dá)式來直接捕獲變量x,則不行。下面這個(gè)代碼無法通過編譯。

void raw_function_pointer_capture_test() {
  int x = 0;
  auto f = [x](void* context) mutable ->void {
      ++x; 
  };
  register_callback(f, nullptr);
  std::cout << x << "\n";
}

那有什么方法能夠?qū)⒉东@變量的lambda表達(dá)式轉(zhuǎn)換成普通函數(shù)指針,同時(shí)能夠保留狀態(tài)呢?

方法一: 聲明一個(gè)全局的invoke_function函數(shù),將lambda表達(dá)式轉(zhuǎn)為為void*,即將lambda表達(dá)式作為狀態(tài)傳遞。

extern "C" void invoke_function(void* ptr) {
    (*static_cast<std::function<void()>*>(ptr))();
}
void lambda_to_function(){
    int x = 0;
    auto lambda_f = [&]()->void { ++x; };
    std::function cpp_function(std::move(lambda_f));
    register_callback(&invoke_function, &cpp_function);
    std::cout << x << "\n";
}

調(diào)用代碼

lambda_to_function();

輸出:2

std::function cpp_function用于接管lambda表達(dá)式的所有權(quán),狀態(tài)都存在這里。此處使用的是棧變量,可以根據(jù)實(shí)際的需要變成堆變量,防止cpp_function析構(gòu)后再使用,成為undefined behavior。

方法二:使用模板,將狀態(tài)存在一個(gè)結(jié)構(gòu)體里面。

#include <iostream>
#include <tuple>
#include <memory>

template<class...Args>
struct callback {
  void(*function)(void*, Args...)=nullptr;
  std::unique_ptr<void, void(*)(void*)> state;
};
template<typename... Args, typename Lambda>
callback<Args...> voidify( Lambda&& l ) {
  using Func = typename std::decay<Lambda>::type;
  std::unique_ptr<void, void(*)(void*)> data(
    new Func(std::forward<Lambda>(l)),
    +[](void* ptr){ delete (Func*)ptr; }
  );
  return {
    +[](void* v, Args... args)->void {
      Func* f = static_cast< Func* >(v);
      (*f)(std::forward<Args>(args)...);
    },
    std::move(data)
  };
}


void lambda_capture_template_test() {
  int x = 0;
  auto closure = [&]()->void { ++x; };
  auto voidified = voidify(closure);
  register_callback( voidified.function, voidified.state.get() );
//   register_callback( voidified.function, voidified.state.get() );
  std::cout << x << "\n";
}

調(diào)用代碼

lambda_capture_template_test();

輸出:2

稍微解釋一下模板做法的含義。

template<class...Args>
struct callback {
  void(*function)(void*, Args...)=nullptr;
  std::unique_ptr<void, void(*)(void*)> state;
};

這個(gè)模板類callback,第一個(gè)成員就是普通函數(shù)指針,用于注冊(cè)回調(diào)函數(shù)時(shí)使用。第二個(gè)成員是自定義deleter的unique_ptr,智能指針管理的是一個(gè)匿名類(即lambda表達(dá)式所屬的類)。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • C語言修煉之路靈根孕育源流出?初識(shí)C言大道生下篇

    C語言修煉之路靈根孕育源流出?初識(shí)C言大道生下篇

    C語言是一門面向過程、抽象化的通用程序設(shè)計(jì)語言,廣泛應(yīng)用于底層開發(fā)。C語言能以簡易的方式編譯、處理低級(jí)存儲(chǔ)器。C語言是僅產(chǎn)生少量的機(jī)器語言以及不需要任何運(yùn)行環(huán)境支持便能運(yùn)行的高效率程序設(shè)計(jì)語言
    2022-03-03
  • C++ 類的賦值運(yùn)算符''''=''''重載的方法實(shí)現(xiàn)

    C++ 類的賦值運(yùn)算符''''=''''重載的方法實(shí)現(xiàn)

    這篇文章主要介紹了C++ 類的賦值運(yùn)算符'='重載的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • OpenCV圖像旋轉(zhuǎn)Rotate的詳細(xì)介紹

    OpenCV圖像旋轉(zhuǎn)Rotate的詳細(xì)介紹

    這篇文章主要介紹了OpenCV圖像旋轉(zhuǎn)Rotate,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • 使用C語言調(diào)用luajit的方法詳解

    使用C語言調(diào)用luajit的方法詳解

    C語言是一種非常流行的編程語言,而Lua是一種基于C語言開發(fā)的腳本語言,在Lua的各種實(shí)現(xiàn)中,luajit也是其中一種非常流行的實(shí)現(xiàn),在本文中,我將為大家介紹如何使用C語言調(diào)用luajit,并且詳細(xì)介紹如何傳入?yún)?shù),傳入結(jié)構(gòu)體參數(shù),以及獲取返回值
    2023-11-11
  • C++中cout的格式使用詳細(xì)介紹

    C++中cout的格式使用詳細(xì)介紹

    cout 是C++中 ostream 類型的對(duì)象,該類被封裝在 < iostream > 庫中,該庫定義的名字都在命名空間 std 中,所以 cout 全稱是 std::cout 。本文重點(diǎn)給大家介紹C++中cout的格式使用,需要的朋友參考下吧
    2021-06-06
  • C++面向?qū)ο髮?shí)現(xiàn)五子棋小游戲

    C++面向?qū)ο髮?shí)現(xiàn)五子棋小游戲

    本文介紹了如何運(yùn)用面向?qū)ο笏枷脒M(jìn)行五子棋游戲的設(shè)計(jì)與開發(fā),與面向過程程序設(shè)計(jì)比較,面向?qū)ο蟪绦蛟O(shè)計(jì)更易于實(shí)現(xiàn)對(duì)現(xiàn)實(shí)世界的描述,提高軟件的擴(kuò)展性和可維護(hù)性。附上最終的程序源碼,推薦給大家,有需要的小伙伴可以參考下。
    2015-03-03
  • C++ SOCKET多線程實(shí)現(xiàn)聊天小程序

    C++ SOCKET多線程實(shí)現(xiàn)聊天小程序

    這篇文章主要為大家詳細(xì)介紹了C++ SOCKET多線程實(shí)現(xiàn)聊天小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Qt繪制時(shí)鐘效果

    Qt繪制時(shí)鐘效果

    這篇文章主要為大家詳細(xì)介紹了Qt繪制時(shí)鐘效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • C++ main函數(shù)的幾點(diǎn)細(xì)節(jié)

    C++ main函數(shù)的幾點(diǎn)細(xì)節(jié)

    這篇文章主要介紹了C++ main函數(shù)的幾點(diǎn)細(xì)節(jié),幫助大家更好的理解和學(xué)習(xí)C++,感興趣的朋友可以了解下
    2020-08-08
  • C語言實(shí)現(xiàn)opencv提取直線、輪廓及ROI實(shí)例詳解

    C語言實(shí)現(xiàn)opencv提取直線、輪廓及ROI實(shí)例詳解

    這篇文章主要介紹了C語言實(shí)現(xiàn)opencv提取直線、輪廓及ROI實(shí)例詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01

最新評(píng)論

锡林郭勒盟| 蒙山县| 额尔古纳市| 泊头市| 海伦市| 象州县| 孝感市| 闽侯县| 东莞市| 筠连县| 花莲县| 甘肃省| 塔河县| 青岛市| 封开县| 马公市| 江源县| 延吉市| 肃北| 栾川县| 阳江市| 黄冈市| 湖南省| 曲松县| 普安县| 城口县| 安泽县| 荆门市| 盖州市| 高密市| 左云县| 台江县| 遂昌县| 康乐县| 丽江市| 宾川县| 年辖:市辖区| 百色市| 利津县| 拜城县| 泰和县|