CppCon 2014 學習:Practical Functional Programming

這段內容是對**在 C++ 中使用函數式編程(Functional Programming, FP)**可以做什么的簡要介紹,下面是逐條的翻譯與理解:

Introduction 簡介

在 C++ 中使用函數式編程(FP)可以做什么?

1. 編寫強大且通用的算法

例如:

std::sort(RAIterator begin, RAIterator end, Compare comp);
  • 通過將函數作為參數傳入(如 comp 比較函數),可以使算法對不同數據類型、排序規則通用。
  • 這是函數式風格的關鍵特征:將行為(函數)當作值傳遞

2. 響應事件的編程模式(反應式編程)

函數式風格常用于定義對事件的反應,例如:

  • 圖形用戶界面(GUI)
    • 響應按鈕點擊、鼠標移動等事件
  • IO / 網絡編程
    • 異步讀取、接收數據等處理
  • 任務調度 / 并行計算
    • 響應任務完成、數據到達等事件驅動

3. 用舊的調用方式組合出新的調用方式

  • 舉例:你可以組合多個已有函數,構建新的函數調用協議(如 currying、partial application)。
  • 函數組合、延遲計算等是函數式編程的重要能力。

4. 將執行流程當作數據結構進行操作

  • 把函數或一系列動作封裝為數據,可以在運行時動態組合、調度和傳遞。
  • 例如:
    • 任務圖
    • 延遲執行鏈
    • 協程 / continuation 風格的編程

總結

函數式編程在 C++ 中的應用包括:

  • 編寫通用算法(如 STL 算法)
  • 構建事件驅動程序(GUI、IO、并發)
  • 靈活組合調用邏輯
  • 操作控制流作為“數據”

這段內容概述了函數式編程在 C++ 中的三個核心概念:

Overview(概覽)

1. First-Class Functions(第一類函數)

  • 意思是:函數在語言中被當作“值”來看待,就像整數或字符串一樣。
  • 你可以:
    • 把函數賦值給變量;
    • 把函數作為參數傳給另一個函數;
    • 從函數中返回另一個函數;
    • 存儲函數在容器中。
      C++ 示例:
auto square = [](int x) { return x * x; };
std::function<int(int)> f = square;

2. Higher-Order Functions(高階函數)

  • 定義:接受函數作為參數返回函數作為結果的函數。
  • 高階函數讓你可以創建更抽象、更通用的邏輯。
    C++ 示例:
void apply_twice(std::function<void()> f) {f();f();
}

3. A (Parallel) Algebra of Functions(函數的代數結構,支持并行)

  • 指的是:可以像數學代數那樣組合函數,形成更復雜的行為,同時還能并行執行
  • 包括:
    • 函數組合(function composition)
    • 映射(map)、過濾(filter)、歸約(reduce)等操作
    • 并行執行這些操作
      示例概念:
std::transform(std::execution::par, begin, end, result, [](auto x) { return x * x; });

這表示在并行環境中對所有元素執行平方操作,函數是“組合”和“并發執行”的。

總結:

概念含義
First-Class Functions函數是值,可以傳遞/賦值
Higher-Order Functions接收函數或返回函數
(Parallel) Algebra of Functions組合函數 + 并行化操作

什么是“第一類函數(First-Class Functions)”,意思是函數在編程語言中像“普通值”一樣被對待。下面是對這段內容的翻譯和理解:

First-Class Functions(第一類函數)

“第一類公民”的權利與特權:
—— 引自《SICP》和 Christopher Strachey(編程語言理論先驅)

權利 1:可以由變量命名

  • 你可以把函數賦值給變量,像給 intstring 命名一樣。
auto square = [](int x) { return x * x; };

權利 2:可以作為參數傳入函數

  • 函數可以作為“實參”傳遞給另一個函數。
void apply(std::function<void()> f) {f();
}

權利 3:可以作為返回值從函數中返回

  • 函數也可以從函數中“生成”并返回。
std::function<int(int)> make_multiplier(int factor) {return [=](int x) { return x * factor; };
}

權利 4:可以被包含在數據結構中

  • 函數可以放在容器里,比如 std::vectorstd::map
std::vector<std::function<int(int)>> funcs;
funcs.push_back([](int x) { return x + 1; });
funcs.push_back([](int x) { return x * 2; });

總結:

擁有“第一類函數”特性的語言(如 C++11 之后的 C++)讓你可以:

操作示例
給函數命名auto f = ...
傳遞函數func(f)
返回函數return [](int x) { return x * x; };
存儲函數放入 std::vector 等容器中

C++ 中如何實現“可調用對象(Callable)”的概念,盡管 C++ 的函數本身不是完全意義上的第一類對象。

Callable 概念

C++ 中函數本身不是第一類對象,但我們有很多方式把函數包裝為第一類對象

什么是 Callable?

  • **Callable(可調用)**是一個泛化概念,表示:

    如果你可以像 T(args...) 這樣調用一個類型 T,并且它會返回一個值,那 T 就是 Callable。
    也就是說:只要某個類型能“像函數一樣被調用”,它就是 Callable。

哪些東西是 Callable?

類型示例說明
函數指針int (*fp)(int)最傳統的函數調用形式
成員函數指針int (MyClass::*mfp)()可以通過對象調用的成員方法
擁有 call 運算符(operator())的類型struct F { int operator()(int); };常見于函數對象、Lambda

示例 1:函數指針

int square(int x) { return x * x; }
int (*fp)(int) = square;
std::cout << fp(5);  // 輸出 25

示例 2:成員函數指針

struct MyClass {int times2(int x) { return x * 2; }
};MyClass obj;
int (MyClass::*mfp)(int) = &MyClass::times2;
std::cout << (obj.*mfp)(5);  // 輸出 10

示例 3:Lambda 或函數對象

auto lambda = [](int x) { return x + 1; };
std::cout << lambda(10);  // 輸出 11
struct Functor {int operator()(int x) { return x - 1; }
};
Functor f;
std::cout << f(10);  // 輸出 9

小結

特性說明
C++ 函數不是天然第一類對象比如不能直接賦值給變量(除非是函數指針)
Callable 是一種統一抽象不管是函數指針、Lambda 還是類對象,只要能調用,就可以使用
實用性用于 std::functionstd::bindstd::invoke、并行算法等場景
#include <iostream>
// 一個簡單的函數
int f() {return 42;
}
// 類型別名:定義三種函數相關類型
typedef int A();     // A 是函數類型(不能定義變量)
typedef int(&B)();   // B 是對函數類型的引用
typedef int(*C)();   // C 是函數指針類型int main() {// A a = f; //  錯誤:不能用函數類型定義變量(這行注釋掉才能編譯通過)B b1 = f;   //  合法:函數引用// B b2 = &f;  //  錯誤C c1 = f;   //  合法:函數隱式轉換為函數指針C c2 = &f;  //  合法:函數地址賦值給函數指針// 調用函數std::cout << "b1(): " << b1() << std::endl;std::cout << "b2(): " << b2() << std::endl;std::cout << "c1(): " << c1() << std::endl;std::cout << "c2(): " << c2() << std::endl;std::cout << "(*c1)(): " << (*c1)() << std::endl;std::cout << "(*c2)(): " << (*c2)() << std::endl;return 0;
}

這段代碼主要是探討 C++ 中不同方式的函數類型定義和使用,尤其是 函數指針函數引用 的寫法與調用方式。我們逐行分析并判斷哪些語句可能 無法編譯

代碼逐行解釋

int f() { return 42; }

定義了一個返回 int 的無參數函數 f

typedef int A();      // A 是函數類型,不是對象或指針
typedef int(&B)();    // B 是對函數類型的引用
typedef int(*C)();    // C 是函數指針類型

解釋:

名稱說明
A表示“函數類型”int()(不能直接用作變量)
B表示“對函數類型的引用”,即 int(&)()
C表示“指向函數的指針”,即 int(*)()
A a = f;

錯誤!

  • A 是一個函數類型,不能定義一個函數對象。
  • 正確做法是使用函數指針或引用。
B b1 = f;

正確。

  • f 會自動轉換為函數引用。
  • b1 成為 f 的別名。
B b2 = &f;

錯誤餓

C c1 = f;

正確。

  • f 可以隱式轉換為函數指針。
C c2 = &f;

正確。

  • &f 是函數地址,類型正是 int(*)()

調用部分

b1();
b2();
c1();
c2();
(*c1)();
(*c2)();

全部 合法調用,因為:

  • b1 / b2 是函數引用。
  • c1 / c2 是函數指針,支持 c1()(*c1)() 兩種調用語法。

哪一行不能編譯?

A a = f;

這是唯一 不能編譯 的行。因為 A 是函數類型,不能定義變量 a

小結

代碼行是否能編譯說明
A a = f;A 是函數類型,不能定義對象
其它定義行與調用全部有效,涉及引用與指針的合法用法

這段代碼展示了 C++ 中函數、函數引用、函數指針之間的微妙差異。我們逐行分析并解釋注釋:

int f() { return 42; }
typedef int A();    //  A 是一個函數類型,表示“返回 int 的函數”
typedef int(&B)();  //  B 是對函數的引用類型
typedef int(*C)();  //  C 是函數指針類型

int main() {

A a = f;
// 錯誤:不能用函數類型定義變量(函數類型不是對象類型)
// A a; 實際上是試圖定義一個函數 a(),這是非法的初始化
B b1 = f;
//  正確:f 是函數名,是函數的左值,可以綁定到 B(函數引用)
B b2 = &f;
//  錯誤:&f 是函數指針,是一個右值,不能綁定到非常量的左值引用(B 是 int(&)())
// C++ 不允許將右值綁定到非常量左值引用
C c1 = f;
//  正確:函數名 f 在需要指針的上下文中自動衰變為函數指針
C c2 = &f;
//  正確:顯式地取地址得到函數指針

調用:

b1();
b2();
//  正常:函數引用,可以像函數名一樣調用
c1();
c2();
(*c1)();
(*c2)();
//  正常:函數指針也可以直接調用,也可以先解引用再調用

總結:

聲明/語句是否合法原因
A a = f;函數類型不能聲明變量
B b1 = f;函數名是左值,可綁定到函數引用
B b2 = &f;&f 是右值,不能綁定非常量引用
C c1 = f;函數名自動轉換為函數指針
C c2 = &f;明確使用函數指針初始化

推薦寫法:

  • 想保存函數引用:B b = f;
  • 想保存函數指針:C c = f;C c = &f;

這是關于 C++11 中Lambda表達式的說明,我幫你整理翻譯理解如下:

Lambda表達式

  • C++11引入的匿名函數寫法:
    [] (int A, int B) { return A + B; }
    
    這是一個無名函數,接收兩個 int 參數,返回它們的和。
  • Lambda表達式的類型是編譯器自動生成的實現定義類型,但它是一個可調用對象(Callable)
  • 你可以用auto來自動推斷Lambda的類型,例如:
    auto Add = [] (int A, int B) { return A + B; };
    
    Add 是一個變量,持有這個Lambda的實例。
  • 如果Lambda中所有return語句返回的類型相同,編譯器會隱式推斷返回類型。
  • 如果Lambda中返回類型不一致,或者你想顯式指定返回類型,可以寫成:
    [](int A, int B) -> int { return A + B; }
    
    這里-> int明確指出返回類型是int
    總結來說,Lambda表達式是現代C++中非常強大且靈活的函數對象寫法,支持類型推斷和顯式返回類型聲明。

多態函數包裝器 std::function:

多態函數包裝器(Polymorphic Function Wrapper)

  • std::function<Signature> 是一個模板類,用于存儲和調用任何符合給定函數簽名(Signature)的可調用對象。
  • 函數簽名是指函數類型的聲明,比如:
    • R T(Arg0, Arg1, ..., ArgN)
    • 例如 int(int, int) 表示接收兩個 int 參數,返回 int
  • std::function<> 具有值語義,即它是一個值類型,可以復制、賦值。
  • 任何與簽名匹配的可調用對象(函數指針、函數對象、Lambda)都可以存儲在 std::function<> 中。
  • std::function<> 讓調用這些不同類型的可調用對象具有統一的調用語法。

代碼示例

std::function<int(int, int)> Add = [](int A, int B) { return A + B; };
std::cout << Add(4, 5) << std::endl;  // 輸出 9

這里:

  • Add 是一個 std::function,表示一個接收兩個 int 返回 int 的函數。
  • 賦值了一個 Lambda 表達式,捕獲一個加法操作。
  • 調用 Add(4, 5) 等同于調用該Lambda,輸出結果。

簡單總結:
std::function 是一個類型擦除容器,可以保存不同的可調用對象,方便統一調用,廣泛用于需要存儲回調函數、事件處理器等場景。
如果你想,我可以幫你寫更多std::function的示例代碼。

這是在講 MUD(多用戶地牢游戲,Multi-User Dungeon)中的虛函數表(V-Tables)相關內容,我幫你翻譯理解:

V-Tables 在 MUD 中的應用

  • MUDs 是基于文本的多人在線游戲(類似早期的MMORPG)。
  • 它們通常通過 TCP 協議上的反向 telnet 連接來實現。
  • 游戲世界由 房間(rooms)物品(items)怪物(mobs,例如由 AI 控制的角色)玩家(players) 組成。
  • 游戲通常使用內部開發的腳本語言,使游戲世界可以被編程和擴展。
  • Roomprogs 是附加到房間上的腳本,用于定義房間的行為和事件。
    這里提到的 V-Tables 是 C++ 里實現多態的關鍵機制,MUD 的系統可能用類似機制來支持可擴展的腳本和動態行為。
    簡單來說,就是用面向對象和虛函數機制,使得游戲世界中的元素(如房間、怪物等)能通過腳本動態定義行為,增加游戲的可編程性和復雜性。
    如果你需要,我可以幫你詳細講解 C++ 中的 V-Table 是如何實現多態的,或者 MUD 腳本系統如何設計。

這是一個 MUD(多用戶地牢游戲)中的腳本示例,結合了虛函數表(V-Tables)的應用背景,幫你理解一下:

例子解析:MUD 中的 Roomprog 腳本

echo The scaly monstrosity shifts in its slumber as %P enters the chamber from the eastern tunnel
wait 10
force dragon wake
// ... do horrible things to player
  • echo:向房間內玩家顯示一條消息,這里 %P 可能是玩家的占位符(名字)。
  • wait 10:暫停10秒,給玩家一點反應時間。
  • force dragon wake:強制讓房間內的“龍”怪物醒來。
  • 后續代碼(省略)是讓龍對玩家發動攻擊或其他事件。

結合 V-Tables 理解

  • 這種腳本背后的實現,通常用面向對象設計,比如房間和怪物類用虛函數(通過 V-Table 實現)來管理行為。
  • 當腳本調用 force dragon wake,系統可能會調用“龍”對象的虛函數(比如 wake()),觸發多態行為。
  • 通過這種設計,腳本可以透明地控制不同怪物和房間的行為,實現靈活且可擴展的游戲邏輯。
    簡單說:MUD里的腳本通過調用對象的虛函數,利用V-Table機制實現動態、多態的游戲事件和交互,增強游戲體驗。

這段代碼展示了 MUD(多用戶地牢游戲)中房間(room)結構體的一部分設計,結合 V-Table 的思路來實現事件觸發機制。幫你詳細解釋:

代碼結構解釋

struct room
{int room_id;                     // 房間IDstd::string name;                // 房間名稱std::string description;         // 房間描述// ...struct enter_trigs               // 進入觸發器(事件處理器){std::function<void(room&, character&)> east;  // 從東邊進入時觸發的函數// 這里可以有更多方向的觸發函數,比如 north, south 等};
};

理解點

  • room 結構體代表游戲世界中的一個房間。
  • enter_trigs 是一個內部結構體,用來存放“進入房間”的觸發器(trigger)函數。
  • std::function<void(room&, character&)> east 表示一個可調用對象(函數、lambda等),當有角色從“東邊”進入房間時會被調用。
  • 這是一種面向對象的設計方式,使用函數對象封裝事件響應,類似于用虛函數表(V-Table)實現的多態機制,但更靈活,可以動態綁定任意函數。

關聯 V-Table

  • 在傳統面向對象設計中,可以用虛函數定義“進入房間”事件的響應。
  • 這里用 std::function,實現了類似的多態性(多種不同函數可以綁定到不同房間實例),但更加動態和靈活。
  • 這允許游戲設計者或腳本隨時更改某個房間“東邊進入”的行為,而不必繼承重寫類。
    如果需要,我可以幫你寫一個完整的例子來演示如何給房間設置進入觸發器,并響應玩家進入事件。

這段代碼示例展示了如何在 MUD 游戲中動態更新房間的“進入觸發器”(enter trigger),即根據不同方向(room_direction dir)綁定一段腳本行為。

代碼含義逐句解釋

bool room::update_enter_trigger(room_direction dir, std::string script)
{// 1. 將傳入的腳本字符串編譯成一個可調用的函數對象std::function<void(room&, character&)> F = CompileTheScript(script);// 2. 檢查腳本是否有效(編譯是否成功)if (!F)return false;  // 腳本有誤,通知腳本開發者else{// 3. 將編譯好的函數對象綁定到指定方向的進入觸發器this->enter_trigs[dir] = F;// 4. 將更改保存到數據庫this->commit_to_database();// 5. 更新成功,返回 truereturn true;}
}

理解重點

  • room_direction dir 表示進入的方向,比如 eastnorth 等。
  • std::string script 是一個腳本代碼的文本,代表進入時應該執行的邏輯。
  • CompileTheScript 是假想的函數,將字符串腳本編譯成 C++ 的 std::function,以便執行。
  • 如果編譯失敗,返回 false
  • 成功時,將生成的函數存儲在 enter_trigs 里對應的方向槽(enter_trigs[dir]),完成動態綁定。
  • commit_to_database() 負責把更改寫入持久層,確保腳本綁定不會丟失。

總結

這段代碼體現了 MUD 游戲中利用“函數表”(類似虛函數表)動態綁定事件響應腳本的思路,既靈活又支持動態更新。
如果你想,我可以幫你寫一個更完整的示范,包含枚舉方向類型、enter_trigs 結構設計和示例調用。

這段代碼實現了房間(room)的進入觸發器執行邏輯,具體解釋如下:

void room::enter_trigger(room_direction dir, character& char) {if (this->enter_trigs[dir]) {          // 1. 檢查指定方向 dir 是否綁定了進入觸發器this->enter_trigs[dir](*this, char);  // 2. 如果有,調用對應的觸發器函數,傳入當前房間(*this)和進入的角色 char}                                       // 3. 如果沒有綁定任何觸發器,則什么也不做(空操作)
}

重點理解

  • enter_trigs 是一個存放函數對象(std::function<void(room&, character&)>)的容器,按方向索引。
  • 通過 if (this->enter_trigs[dir]) 判斷當前方向是否綁定了一個有效的函數(觸發器)。
  • 如果綁定了,就執行它,參數是當前房間自身和進入的角色。
  • 如果沒有綁定觸發器,則直接跳過,沒有任何動作。

作用

當角色從某個方向進入房間時,enter_trigger 會調用相應方向的“進入腳本”,實現游戲邏輯的動態觸發。

高階函數(Higher-Order Functions) 是指:

  • 接受一個或多個可調用對象(Callable,比如函數、函數指針、lambda等)作為參數,或者
  • 返回一個可調用對象
    舉例:
  • std::for_each:接受一個函數或 lambda 來對區間內的每個元素執行操作。
  • std::bind:返回一個新的可調用對象,通過綁定部分參數來生成新的函數。
    簡單來說,高階函數就是“函數可以像數據一樣傳來傳去”,這增強了程序的靈活性和表達能力。

函數構造(Crafting Functions)

  • 高階函數可以幫助我們轉換函數的調用約定
    例如,給定函數:
int f(int a, float b, std::string const& c);

我們可能想對它的參數列表做兩類變換:

  1. 增加或減少函數的參數個數(函數的“arity”)
    通過綁定某些參數為固定值,實現“部分應用”或“柯里化”,例如將部分參數固定,生成新的函數。
  2. 重新排列參數順序
    調整參數順序以適應不同調用習慣或接口要求。
    總結:
    高階函數可以讓我們靈活地修改函數接口,使得函數調用更符合特定需求或更易組合。

轉發調用包裝器(Forwarding Call Wrapper)

  • std::bind(F, Args&&... args) 返回一個可調用對象(binder),用于將函數 F 和一些參數綁定在一起,形成一個新的可調用函數。
  • 綁定時,Args 中的每個參數可以是以下四種情況之一:
    1. 成員函數綁定
      如果 F 是某個類的成員函數,則第一個參數必須是該類實例的引用或指針。
      例如:std::bind(&A::F, this) 將成員函數 F 綁定到當前對象。
    2. 占位符(Placeholders)
      使用 _1, _2, _3, … 表示調用時傳入的參數位置。綁定時指定這些占位符,binder調用時會把對應的參數傳給 F
    3. 引用包裝器(Reference Wrappers)
      std::refstd::cref 用于按引用或常量引用綁定參數,防止參數被復制。
    4. 其它參數
      默認按值語義綁定(即復制參數)。
      總結:
      std::bind 能靈活地組合函數調用,支持成員函數、占位符和引用參數,方便生成新的調用接口。

綁定參數示例

  • 函數定義:

    int f(int a, float b, std::string const& c);
    
  • 例子1:

    auto g1 = std::bind(&f, 17, _1, _2);
    g1(4.5, "hello");  // 實際調用 f(17, 4.5, "hello");
    

    說明:
    固定第一個參數為17,調用時第一個和第二個參數分別對應f的第二和第三個參數。

  • 例子2:

    std::string input = /*...*/;
    auto g2 = std::bind(&f, _1, _2, std::cref(input));
    g2(9, 0.25);  // 實際調用 f(9, 0.25, input);
    

    說明:
    第三個參數綁定為input的常量引用(防止復制)。

  • 例子3:

    auto g3 = std::bind(&f, _3, _1, _2);
    g3("foobar", 1024, -85.0);  // 實際調用 f(1024, -85.0, "foobar");
    

    說明:
    重新排列參數順序,g3的第3個參數綁定到f的第1個參數,依此類推。

Lambda Closures(閉包)

  • C++11 的 lambda 表達式允許捕獲(bind)其定義時父作用域中的變量。
  • [] 是捕獲列表(capture list),也叫閉包:
    • []:不捕獲任何變量
    • [&]:捕獲所有用到的外部變量,方式為引用捕獲(reference capture)
    • [=]:捕獲所有用到的外部變量,方式為值捕獲(value capture)
    • [&x, =y]:變量 x 按引用捕獲,變量 y 按值捕獲
    • [=, &x]:變量 x 按引用捕獲,其他所有變量按值捕獲

Dangling References(懸掛引用)

  • 用引用捕獲(capture by reference)的變量不會被延長生命周期(不會被“保活”)。
  • 如果 lambda 在其父作用域之外被調用,這種引用將指向已銷毀的變量,導致未定義行為。
  • 類似地,std::refstd::cref 也是一樣,父作用域結束后就失效。
  • 例子中:
    template <typename F>
    std::function<int(int)> badcode(F&& f)
    {int A = 42;                   // 局部變量Areturn [&] (int B) {          // 捕獲A的引用return A + B;};
    }
    
    返回的 lambda 捕獲了局部變量 A 的引用,但 A 生命周期結束后,lambda 持有的引用成為懸掛引用,調用時會導致未定義行為。

綁定成員函數(Binding Member Functions)

  • 當你想用標準算法(如 std::for_each)調用成員函數時,可以用 std::bind 綁定成員函數。
  • 例子:
    MyClass obj;
    std::for_each(begin(my_list), end(my_list),std::bind(&MyClass::add, std::ref(obj), _1));
    
    這里 &MyClass::add 是成員函數指針,std::ref(obj) 綁定對象實例,_1 代表 for_each 傳入的元素作為參數傳遞給成員函數。
  • 也可以用更現代、簡潔的 lambda 表達式:
    MyClass obj;
    std::for_each(begin(my_list), end(my_list),[&obj](int x) { obj.add(x); });
    
  • 兩種方式效果相同,lambda 更直觀且類型推導更友好。

綁定成員函數示例
在類成員函數中,使用 std::for_each 調用同一類的成員函數:

void MyClass::add_for_each(std::list<T>& my_list) {std::for_each(begin(my_list), end(my_list), std::bind(&MyClass::add, this, _1));
}
  • &MyClass::add 是成員函數指針。
  • this 指針綁定當前對象實例。
  • _1 代表傳遞給 for_each 的元素。

使用 lambda 表達式:

void MyClass::add_for_each(std::list<T>& my_list) {std::for_each(begin(my_list), end(my_list), [this](int x) { this->add(x); });
}
  • 捕獲 this 指針。
  • 直接調用成員函數 add
    總結:兩種寫法等價,lambda 更簡潔現代,std::bind 是傳統寫法,理解二者用法對掌握 C++ 函數綁定和成員函數調用非常有幫助。

Echo Server 使用 Asio 和 Lambda 表達式

  • Boost.Asio 是一個支持同步和異步I/O的庫,基于 Proactor 設計模式。
  • 它為多種I/O類型提供通用框架:
    • 網絡套接字(TCP、UDP、ICMP)
    • 文件
    • 串口通信
    • 進程間通信
      用 Asio 編寫 echo 服務器時,通常利用 異步操作lambda表達式 來處理回調,代碼簡潔且靈活。
      比如,異步讀取數據后,用 lambda 處理接收的數據,然后再異步寫回,實現“回聲”功能。
int main() {// 創建io_service對象,作為所有異步操作的核心asio::io_service io_service;// 創建一個IPv4 TCP端點,監聽端口2000asio_tcp::endpoint endpoint(asio_tcp::v4(), 2000);// 創建一個acceptor,用于接受客戶端連接asio_tcp::acceptor acceptor(io_service, endpoint);// 無限循環,處理多個客戶端連接(順序阻塞)for (;;) {// 為每個連接創建一個socketasio_tcp::socket socket(io_service);// 阻塞等待客戶端連接成功acceptor.accept(socket);// 定義讀緩沖區大小std::size_t const max_length = 1024;char msg[max_length];  // 讀寫緩沖區// 定義異步回調函數,用于讀寫操作完成后的處理std::function<void(error_code const&, std::size_t)> f = [&](error_code const& ec, std::size_t bytes){if (!ec) // 如果沒有錯誤{// 異步寫,將收到的數據原樣寫回客戶端(echo)asio::async_write(socket, asio::buffer(msg, bytes),[&](error_code const& ec, std::size_t){if (!ec){// 寫完成后,再異步讀下一批數據auto buf = asio::buffer(msg, max_length);socket.async_read_some(buf, f);}});}};// 啟動第一次異步讀取操作,讀取客戶端數據socket.async_read_some(asio::buffer(msg, max_length), f);// 啟動io_service的事件循環,處理所有異步操作io_service.run();// 運行完畢后,io_service進入停止狀態,循環開始時應重置io_service.reset();  // 注意:示例里沒有寫,實際代碼最好加上}
}

這是一個基于 Boost.Asio 和 lambda 表達式實現的簡單 Echo Server 樣例。核心思想是:

  • 創建 io_service 和 TCP 監聽器 acceptor,監聽端口2000。
  • 循環等待客戶端連接,接收到新連接時,創建對應的 socket
  • 為該連接準備一個緩沖區 msg
  • 定義一個回調 lambda f,用來:
    • 異步讀取客戶端數據 socket.async_read_some
    • 讀取完成后,調用異步寫函數 asio::async_write 把數據回寫給客戶端(回聲)
    • 寫完成后,再次異步讀取,實現持續的收發循環。
  • 進入事件循環 io_service.run(),處理異步事件。
    這個寫法是異步非阻塞,用遞歸的 lambda 實現了持續的讀寫循環。

這段代碼實現了一個并行版本的偽隨機數生成器,基于XORWOW算法,利用了C++ AMP(Accelerated Massive Parallelism)來加速生成過程。下面是詳細解析:

代碼結構和作用

std::vector<std::uint32_t>
xorwow(std::size_t N, std::array<std::uint32_t, 6> seed) {std::vector<std::uint32_t> r(N, 0);  // 存儲生成的隨機數,大小N// 將數據包裝成C++ AMP的array_view,支持GPU/并行訪問concurrency::array_view<std::uint32_t, 1> rv(N, r);concurrency::array_view<std::uint32_t, 1> sv(6, seed);// 并行執行N次,索引從0到N-1concurrency::parallel_for_each(rv.extent,   // 并行范圍[=](concurrency::index<1> idx) restrict(amp) {// 每個線程獨立計算隨機數// 初始化變量,利用種子和線程索引std::uint32_t x = sv[0] << idx[0];  // 左移idx個bit,保證不同線程x不同std::uint32_t y = sv[1];std::uint32_t z = sv[2];std::uint32_t w = sv[3];std::uint32_t v = sv[4];std::uint32_t d = sv[5];// 計算中間值t,用于生成下一個vstd::uint32_t t = (x ^ (x >> 2));// 狀態更新,類似于xorshift系列算法x = y; y = z; z = w; w = v;v = (v ^ (v << 4)) ^ (t ^ (t << 1));// 生成結果,d累加一個常數(362437),加上vrv[idx] = (d += 362437) + v;});return r;  // 返回生成的隨機數向量
}

關鍵點理解

  • xorwow算法是Xorshift家族的一個變體,是一種快速的偽隨機數生成方法。
  • 并行化設計:通過 concurrency::parallel_for_each,每個線程獨立生成一個隨機數,且用線程索引idx影響隨機種子 x,確保不同線程生成的隨機數不同。
  • array_view:是C++ AMP的機制,用于將CPU上的數據映射到GPU并行計算中。
  • restrict(amp):指明lambda在GPU或并行環境中執行。

總結

這段代碼使用C++ AMP并行計算框架,實現了XORWOW偽隨機數生成器的并行版本,適合在GPU或多核CPU上快速生成大量隨機數。每個線程根據不同的索引生成不同的隨機值,保證并行安全和效率。

這部分內容講的是并行計算中future和**并行代數(Parallel Algebra)**的概念,并結合示例展示了如何用future鏈式調用實現異步計算,最后還介紹了并行的transform-reduce操作。

核心概念理解

1. Futures的基本操作
  • f = async(g, ...)
    異步調用函數g,返回一個future<R>,其中Rg的返回類型。future代表一個可能還沒完成的異步操作的結果。
  • f.then(g)
    給已有的future附加一個完成處理器(回調)g,當f完成時自動調用g。返回一個新的future,鏈式調用。
  • make_ready_future(r)
    創建一個立即準備好的future,其值就是r
2. 利用future實現異步迭代計算(例:利息計算)
double add(double principal, double rate) {return principal + principal * rate;
}
double interest(double init_principal, double init_rate, std::size_t time) {future<double> principal = make_ready_future(init_principal); // 創建已完成的future,初始化本金for (std::size_t i = 0; i < time; ++i) {// 用then附加計算步驟,異步計算利息累積principal = principal.then([=rate](double p) { return add(p, rate); });}return principal.get(); // 阻塞等待最終結果
}
  • 這段代碼通過future鏈模擬了異步的“利息累積”過程。
  • 每一步利息計算都異步“掛起”,在前一個計算完成后執行。
  • then里用lambda表達式捕獲rate,用當前本金p調用add
3. Transform-Reduce并行算法示例
std::vector<double> xvalues = // ...
std::vector<double> yvalues = // ...future<double> result = parallel::transform_reduce(parallel::task,boost::counting_iterator<size_t>(0),boost::counting_iterator<size_t>(yvalues.size()),0.0,std::plus<double>(),[&xvalues, &yvalues](size_t i) {return xvalues[i] * yvalues[i];}
);
  • transform_reduce是并行的“先變換再歸約”操作。
  • boost::counting_iterator用來生成索引序列 [0, size).
  • 變換函數是將xvalues[i]yvalues[i]相乘。
  • 歸約操作用std::plus<double>()做加法。
  • 返回的是一個future<double>,結果是所有乘積的和。

總結

  • future提供了一個并行、異步計算的模型,可以通過.then()方法實現鏈式計算。
  • 這種方式可以把同步的順序執行,轉變為異步的并行執行,提升程序性能。
  • 利用并行庫可以方便地進行復雜數據的并行變換和歸約(如transform_reduce)。
  • 示例展示了如何寫異步循環累積和并行算法,非常適合構建高性能計算任務。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/83167.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/83167.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/83167.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

飛牛NAS+Docker技術搭建個人博客站:公網遠程部署實戰指南

文章目錄 前言1. Docker下載源設置2. Docker下載WordPress3. Docker部署Mysql數據庫4. WordPress 參數設置5. 飛牛云安裝Cpolar工具6. 固定Cpolar公網地址7. 修改WordPress配置文件8. 公網域名訪問WordPress總結 前言 在數字化浪潮中&#xff0c;傳統網站搭建方式正面臨前所未…

ComfyUI+阿里Wan2.1+內網穿透技術:本地AI視頻生成系統搭建實戰

文章目錄 前言1.軟件準備1.1 ComfyUI1.2 文本編碼器1.3 VAE1.4 視頻生成模型 2.整合配置3. 本地運行測試4. 公網使用Wan2.1模型生成視頻4.1 創建遠程連接公網地址 5. 固定遠程訪問公網地址總結 前言 各位技術愛好者&#xff0c;今天為您帶來一組創新性的AI應用方案&#xff01…

n8n:技術團隊的智能工作流自動化助手

在當前數字化時代,自動化已經成為提高效率和減輕人工工作負擔的一大推動力。今天,我們要為大家介紹一款極具潛力的開源項目——n8n,它不僅擁有廣泛的應用場景,還具備內置AI功能,能夠完全滿足技術團隊的高效工作需求。n8n的出現,為技術團隊提供了自由編程與快速自動化構建…

1,QT的編譯教程

目錄 整體流程: 1,新建project文件 2,編寫源代碼 3,打開QT的命令行窗口 4,生成工程文件(QT_demo.pro) 5,生成Make file 6,編譯工程 7,運行編譯好的可執行文件 整體流程: 1,新建project文件 新建文本文件,后綴改為.cpp 2,編寫源代碼

深度學習論文: FastVLM: Efficient Vision Encoding for Vision Language Models

深度學習論文: FastVLM: Efficient Vision Encoding for Vision Language Models FastVLM: Efficient Vision Encoding for Vision Language Models PDF: https://www.arxiv.org/abs/2412.13303 PyTorch代碼: https://github.com/shanglianlm0525/CvPytorch PyTorch代碼: https…

十一、【核心功能篇】測試用例管理:設計用例新增編輯界面

【核心功能篇】測試用例管理&#xff1a;設計用例新增&編輯界面 前言準備工作第一步&#xff1a;創建測試用例相關的 API 服務 (src/api/testcase.ts)第二步&#xff1a;創建測試用例編輯頁面組件 (src/views/testcase/TestCaseEditView.vue)第三步&#xff1a;配置測試用例…

三、web安全-信息收集

1、信息搜集的重要性 &#xff08;1&#xff09;明確攻擊面 信息搜集能讓滲透測試人員清晰地勾勒出目標系統的邊界&#xff0c;包括其網絡拓撲結構、開放的服務端口、運行的軟件系統等。例如&#xff0c;通過信息搜集發現目標企業除了對外提供官網服務外&#xff0c;還有一個…

生活小記啊

最近生活上的事情還是蠻多的&#xff0c;想到哪寫到哪。 工作 三月的某個周六&#xff0c;正在加班寫技術方案&#xff0c;大晚上寫完了聽到調動通知&#xff0c;要去新的團隊了。 還是蠻不舍的&#xff0c;看著產品從無到有&#xff0c;一路走過來&#xff0c;傾注了不少感…

vue-08(使用slot進行靈活的組件渲染)

使用slot進行靈活的組件渲染 作用域slot是 Vue.js 中的一種強大機制&#xff0c;它允許父組件自定義子組件內容的呈現。與僅向下傳遞數據的常規 props 不同&#xff0c;作用域 slot 為父級提供了一個模板&#xff0c;然后子級可以填充數據。這提供了高度的靈活性和可重用性&am…

MySQL索引與性能優化入門:讓查詢提速的秘密武器【MySQL系列】

本文將深入講解 MySQL 索引的底層原理、常見類型、使用技巧&#xff0c;并結合 EXPLAIN 工具分析查詢執行計劃&#xff0c;配合慢查詢日志識別瓶頸&#xff0c;逐步建立起系統的 MySQL 查詢優化知識體系。適合有一定基礎、希望在數據量增長或面試中脫穎而出的開發者閱讀。 一、…

C 語言開發中常見的開發環境

目錄 1.Dev-C 2.Visual Studio Code 3.虛擬機 Linux 環境 4.嵌入式 MCU 專用開發環境 1.Dev-C 使用集成的 C/C 開發環境&#xff08;適合基礎學習&#xff09;,下載鏈接Dev-C下載 - 官方正版 - 極客應用 2.Visual Studio Code 結合 C/C 擴展 GCC/MinGW 編譯器&#xff0c…

STM32G4 電機外設篇(二) VOFA + ADC + OPAMP

目錄 一、STM32G4 電機外設篇&#xff08;二&#xff09; VOFA ADC OPAMP1 VOFA1.1 VOFA上位機顯示波形 2 ADC2.1 用ADC規則組對板載電壓和電位器進行采樣 3 OPAMP&#xff08;運放&#xff09;3.1 結合STM32內部運放和ADC來完成對三相電流的采樣3.2 運放電路分析 附學習參考…

再見Notepad++,你好Notepad--

Notepad-- 是一款國產開源的輕量級、跨平臺文本編輯器&#xff0c;支持 Window、Linux、macOS 以及國產 UOS、麒麟等操作系統。 除了具有常用編輯器的功能之外&#xff0c;Notepad-- 還內置了專業級的代碼對比功能&#xff0c;支持文件、文件夾、二進制文件的比對&#xff0c;支…

跳動的愛心

跳動的心形圖案&#xff0c;通過字符打印和延時效果模擬跳動&#xff0c;心形在兩種大小間交替跳動。 通過數學公式生成心形曲線 #include <stdio.h> #include <windows.h> // Windows 系統頭文件&#xff08;用于延時和清屏&#xff09; void printHeart(int …

2.2HarmonyOS NEXT高性能開發技術:編譯優化、內存管理與并發編程實踐

HarmonyOS NEXT高性能開發技術&#xff1a;編譯優化、內存管理與并發編程實踐 在HarmonyOS NEXT全場景設備開發中&#xff0c;高性能是跨端應用體驗的核心保障。本章節聚焦ArkCompiler編譯優化、內存管理工具及多線程并發編程三大技術模塊&#xff0c;結合實戰案例解析底層實現…

C# 類和繼承(使用基類的引用)

使用基類的引用 派生類的實例由基類的實例和派生類新增的成員組成。派生類的引用指向整個類對象&#xff0c;包括 基類部分。 如果有一個派生類對象的引用&#xff0c;就可以獲取該對象基類部分的引用&#xff08;使用類型轉換運算符把 該引用轉換為基類類型&#xff09;。類…

如何在騰訊云 OpenCloudOS 上安裝 Docker 和 Docker Compose

從你提供的 /etc/os-release 文件內容來看&#xff0c;你的服務器運行的是 OpenCloudOS 9.2。這是一個基于 CentOS 和 RHEL 的開源操作系統&#xff0c;因此它屬于 CentOS/RHEL 系列。 關鍵信息總結 操作系統名稱&#xff1a;OpenCloudOS版本&#xff1a;9.2ID&#xff1a;op…

趨勢直線指標

趨勢直線副圖和主圖指標&#xff0c;旨在通過技術分析工具幫助交易者識別市場趨勢和潛在的買賣點。 副圖指標&#xff1a;基于KDJ指標的交易策略 1. RSV值計算&#xff1a; - RSV&#xff08;未成熟隨機值&#xff09;反映了當前收盤價在過去一段時間內的相對位置。通過計算當前…

FEMFAT許可分析的數據可視化方法

隨著企業對FEMFAT軟件使用的增加&#xff0c;如何有效地管理和分析許可數據成為了關鍵。數據可視化作為一種強大的工具&#xff0c;能夠幫助企業直觀地理解FEMFAT許可的使用情況&#xff0c;從而做出更明智的決策。本文將介紹FEMFAT許可分析的數據可視化方法&#xff0c;并探討…

AMBER軟件介紹

AMBER軟件介紹 AMBER&#xff08;Assisted Model Building with Energy Refinement&#xff09;是一套廣泛應用于分子動力學&#xff08;MD&#xff09;模擬和生物分子結構分析的軟件工具集&#xff0c;尤其在蛋白質、核酸、多糖等生物大分子的模擬中表現突出。以下是關于AMBE…