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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…