仿muduo库的高并发服务器——正则表达式与any类介绍及其简单模拟实现

本期我们接着深入项目的编写。

相关代码已经上传至作者个人gitee:仿muduo服务器: 本项目致力于实现一个仿造muduo库的简易并发服务器,为个人项目,参考即可喜欢请点个赞谢谢

目录

正则表达式

库的接口介绍

正则表达式解析处理HTTP请求

any类

类介绍

[默认构造与值构造 / 赋值](#默认构造与值构造 / 赋值)

std::make_any (args...)

has_value()

type()

std::any_cast

emplace (args...)

reset()

swap()

设计思想


正则表达式

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

正则表达式的使用,可以使得HTTP请求的解析更加简单(这里指的是程序员的工作变得简单,这并不代表处理效率会变高,实际上效率上是低于直接的字符串处理的),使我们实现的HTTP组件库使用起来更加灵活。

在C++中,<regex>就是为了正则表达式实现的库

<regex>库的接口介绍

  1. std::regex

    • 表达式std::regex r(const std::string& pattern, flag_type flags = ECMAScript);

    • 参数pattern 为正则表达式字符串;flags 可选语法选项(如 ECMAScriptbasicextendedicase 等)。

    • 作用:编译正则表达式,存储为一个可用的正则对象。

  2. std::regex_match

    • 表达式bool regex_match(const std::string& s, std::smatch& m, const std::regex& r);

    • 参数s 为源字符串;m 用于存储匹配结果(子匹配组);r 为已编译的正则对象。

    • 作用 :检查整个字符串是否完全匹配正则表达式(从开头到结尾必须完全符合)。

  3. std::regex_search

    • 表达式bool regex_search(const std::string& s, std::smatch& m, const std::regex& r);

    • 参数 :同上,m 存储首次匹配的子串及捕获组。

    • 作用 :在字符串中搜索第一个匹配正则表达式的子序列(不需要全串匹配)。

  4. std::regex_replace

    • 表达式std::string regex_replace(const std::string& s, const std::regex& r, const std::string& fmt);

    • 参数s 源字符串;r 正则对象;fmt 替换格式字符串(可用 $1$2 引用捕获组)。

    • 作用 :查找所有匹配正则的子串,并用 fmt 替换,返回新字符串。

  5. std::sregex_iterator

    • 表达式std::sregex_iterator it(s.begin(), s.end(), r);

    • 参数:迭代器范围(字符串首尾)和正则对象。

    • 作用 :迭代遍历字符串中所有匹配的子串,每次解引用得到 std::smatch 对象,用于批量处理匹配。

示例:

cpp 复制代码
#include <iostream>
#include <regex>
#include <string>

int main() 
{
    std::string text = "Contact: alice@example.com, bob@test.org";
    std::regex email_pattern(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");

    // 1. regex_match:要求整个字符串完全匹配
    std::string single = "user@domain.com";
    if (std::regex_match(single, email_pattern))
        std::cout << "Full match: " << single << " is an email\n";
    else
        std::cout << "Not a full match\n";

    // 2. regex_search:查找第一个匹配的子串
    std::smatch match;
    if (std::regex_search(text, match, email_pattern))
        std::cout << "First email found: " << match[0] << "\n";

    // 3. regex_replace:替换所有匹配项
    std::string replaced = std::regex_replace(text, email_pattern, "[EMAIL]");
    std::cout << "Replaced text: " << replaced << "\n";

    // 4. sregex_iterator:遍历所有匹配项
    std::cout << "All emails: ";
    for (std::sregex_iterator it(text.begin(), text.end(), email_pattern), end; it != end; ++it)
        std::cout << it->str() << " ";
    std::cout << "\n";

    return 0;
}

结果为:

正则表达式解析处理HTTP请求

cpp 复制代码
#include <iostream>
#include <regex>
#include <string>

int main() 
{
    // 示例 HTTP 请求行
    std::string request_line = "GET /api/users?id=123&name=john HTTP/1.1";

    // 正则表达式:
    // 分组1: 方法 (大写字母)
    // 分组2: 路径 (非空白且不含 '?')
    // 分组3: 查询字符串 (可选,'?' 后非空白字符)
    // 分组4: HTTP版本 (HTTP/x.x)
    std::regex pattern(R"(^([A-Z]+)\s+([^\s?]+)(?:\?([^\s]+))?\s+(HTTP/\d\.\d)$)");

    std::smatch matches;
    if (std::regex_match(request_line, matches, pattern)) {
        std::cout << "完整请求行: " << request_line << "\n\n";
        std::cout << "请求方法: " << matches[1] << std::endl;
        std::cout << "请求路径: " << matches[2] << std::endl;
        
        // 查询字符串可能不存在
        if (matches[3].matched)
            std::cout << "查询字符串: " << matches[3] << std::endl;
        else
            std::cout << "查询字符串: (无)" << std::endl;
        
        std::cout << "HTTP版本: " << matches[4] << std::endl;
    } else {
        std::cerr << "请求行格式不匹配" << std::endl;
    }

    return 0;
}

结果为:

any类

每一个Connection对连接进行管理,最终都不可避免需要涉及到应用层协议的处理,因此在Connection中需要设置协议处理的上下文来控制处理节奏。但是应用层协议千千万,为了降低耦合度,这个协议接收解析上下文就不能有明显的协议倾向,它可以是任意协议的上下文信息,因此就需要一个通用的类型来保存各种不同的数据结构。

在C语言中,通用类型可以使用void*来管理,但是在C++中,boost库和C++17给我们提供了一个通用类型any来灵活使用,如果考虑增加代码的移植性,尽量减少第三方库的依赖,则可以使用C++17特性中的any,或者自己来实现。

<any>类介绍

默认构造与值构造 / 赋值

表达式: std::any a;std::any a = 值;
参数: 无参构造得到空对象;若传入任意可拷贝类型 T 的值,则从该值构造或赋值。
作用: 创建一个 std::any 容器。默认构造时不含任何值(has_value() 返回 false)。传入具体值时,内部会存储一份 T 的拷贝(或通过移动语义接管资源),此后该 any 对象便拥有该类型的有效数据。

std::make_any<T>(args...)

表达式: auto a = std::make_any<T>(构造参数...);
参数: 目标类型 T 及其构造函数所需的实参列表。
作用: 直接在 any 内部原地构造 T 类型的对象,完全避免临时对象的产生与多余的拷贝/移动开销。与先构造临时对象再存入 any 相比,它更高效且异常安全。

has_value()

表达式: a.has_value()
参数: 无。
作用: 返回布尔值,指示当前 any 对象是否持有有效值(即非空)。空 any 通常由默认构造、调用 reset() 或被移走值产生。

type()

表达式: a.type()
参数: 无。
作用: 返回 const std::type_info&,代表当前所存储对象的准确类型。若 any 为空,则返回 typeid(void)。该接口是进行类型安全检查的基础,常与 any_cast 的指针形式配合使用。

std::any_cast<T>

表达式: 存在两种重载形式:

  • 引用形式:std::any_cast<T>(a)

  • 指针形式:std::any_cast<T>(&a)
    参数: 前者接受 any 对象的引用;后者接受 any 对象的指针。
    作用: 提取 any 中存储的值。

  • 引用形式 :若存储类型与 T 不匹配,抛出 std::bad_any_cast 异常。

  • 指针形式 :若存储类型与 T 不匹配,返回 nullptr;匹配则返回指向内部存储对象的指针。推荐优先使用指针形式进行安全访问。

emplace<T>(args...)

表达式: a.emplace<T>(构造参数...);
参数: 目标类型 T 及其构造参数。
作用: 销毁当前存储的任何旧值(如果有),然后在原地构造一个新的 T 类型对象。它避免了先构造临时值再赋值的中间步骤,且能复用已分配的内存(如果实现支持),提高运行期变更类型时的效率。

reset()

表达式: a.reset();
参数: 无。
作用: 销毁当前存储的对象(调用其析构函数),并将 any 对象重置为空状态。调用后 has_value() 将返回 false。该操作不会影响 any 对象本身的存储空间,仅是清空其内容。

swap()

表达式: a1.swap(a2);std::swap(a1, a2);
参数: 另一个同类型的 std::any 对象。
作用: 交换两个 any 容器所持有的值以及它们的状态。该操作是高效的,通常仅交换内部指针或控制块,不涉及所存储类型的拷贝或移动构造。

示例代码

cpp 复制代码
#include <any>
#include <iostream>
#include <string>
#include <vector>

int main()
{
    // 1. 默认构造(空 any)
    std::any a;
    std::cout << "1. 默认构造后 has_value(): " << a.has_value() << '\n';

    // 2. 值构造:存入 int
    std::any b = 42;                       // 隐式转换自 int
    std::any c(std::string("hello"));      // 显式构造
    std::cout << "2. 存入 int: " << std::any_cast<int>(b) << '\n';

    // 3. std::make_any:原地构造 vector,避免临时对象
    auto d = std::make_any<std::vector<int>>(3, 10); // 三个 10
    std::cout << "3. make_any<vector>: size=" 
              << std::any_cast<std::vector<int>>(d).size() << '\n';

    // 4. has_value() 和 type()
    std::cout << "4. d 是否含有值? " << d.has_value() 
              << ", 类型名: " << d.type().name() << '\n';

    // 5. any_cast 指针形式(安全访问)
    if (auto* ptr = std::any_cast<std::string>(&c)) 
    {
        std::cout << "5. 指针提取 string: " << *ptr << '\n';
    }
    if (auto* ptr = std::any_cast<int>(&c)) 
    {
        // 不会执行,因为 c 里是 string
    } 
    else 
    {
        std::cout << "   类型不匹配,返回 nullptr\n";
    }

    // 6. any_cast 引用形式(可能抛异常)
    try 
    {
        std::cout << "6. 引用提取 int: " << std::any_cast<int>(b) << '\n';
        // 下面这行将抛出 std::bad_any_cast
        // std::cout << std::any_cast<double>(b);
    } 
    catch (const std::bad_any_cast& e) 
    {
        std::cout << "   捕获异常: " << e.what() << '\n';
    }

    // 7. emplace:销毁旧值,原地构造新类型
    b.emplace<double>(3.14159);
    std::cout << "7. emplace<double> 后: " << std::any_cast<double>(b) << '\n';

    // 8. reset:清空内容
    b.reset();
    std::cout << "8. reset 后 has_value(): " << b.has_value() << '\n';

    // 9. swap:交换两个 any 的内容
    std::any x = 100;
    std::any y = std::string("world");
    std::cout << "9. 交换前: x=" << std::any_cast<int>(x) 
              << ", y=" << std::any_cast<std::string>(y) << '\n';
    x.swap(y);
    std::cout << "   交换后: x=" << std::any_cast<std::string>(x) 
              << ", y=" << std::any_cast<int>(y) << '\n';

    return 0;
}

结果为:

设计思想

嵌套一下,设计一个类,专门用于保存其他类型的数据,而Any类保存的是固定类的对象

cpp 复制代码
class Any 
{
private:
    class holder 
    {
    ....
    };
    template<class T>
    class placeholder: holder 
    {
        T_val I
    };
    holder *content;
};

Any类中,保存的是holder类的指针,当Any容器需要保存一个数据的时候,只需要通过placeholder子类实例化一个特定类型的子类对象出来,让子类对象保存数据

源代码:

cpp 复制代码
#pragma once

#include <utility>
#include <typeinfo>

class Any
{
private:
    class holder 
    {
    public:
        virtual ~holder() {}
        virtual const std::type_info& type() const = 0;
        virtual holder* clone() const = 0;
    };

    template<class T>
    class placeholder : public holder 
    {
    public:
        placeholder(const T& val) : _val(val) {}
        virtual ~placeholder() = default;

        virtual const std::type_info& type() const override {
            return typeid(_val);
        }

        virtual holder* clone() const override {
            return new placeholder<T>(_val);
        }

        T _val;
    };

    holder* _holder;

public:
    Any() : _holder(nullptr) {}

    template<class T>
    Any(const T& val) : _holder(new placeholder<T>(val)) {}

    Any(const Any& other)
        : _holder(other._holder ? other._holder->clone() : nullptr) {}

    ~Any() {
        delete _holder;
    }

    Any& Swap(Any& other) noexcept {
        std::swap(_holder, other._holder);
        return *this;
    }

    // 从任意类型赋值
    template<class T>
    Any& operator=(const T& val) {
        Any(val).Swap(*this);
        return *this;
    }

    // 拷贝赋值运算符(非模板)
    Any& operator=(const Any& other) {
        Any(other).Swap(*this);
        return *this;
    }

    // 获取存储值的指针(非常量版本)
    template<class T>
    T* get() {
        if (_holder && typeid(*_holder) == typeid(placeholder<T>)) {
            return &(static_cast<placeholder<T>*>(_holder)->_val);
        }
        return nullptr;
    }

    // 获取存储值的指针(常量版本)
    template<class T>
    const T* get() const {
        if (_holder && typeid(*_holder) == typeid(placeholder<T>)) {
            return &(static_cast<const placeholder<T>*>(_holder)->_val);
        }
        return nullptr;
    }
};

测试

cpp 复制代码
#include "MyAny.h"
#include <iostream>
#include <cassert>
#include <string>

class TestCounter 
{
public:
    static int construct_count;
    static int destruct_count;
    
    TestCounter() { construct_count++; }
    TestCounter(const TestCounter&) { construct_count++; }
    ~TestCounter() { destruct_count++; }
};

int TestCounter::construct_count = 0;
int TestCounter::destruct_count = 0;

void test_default_constructor() 
{
    std::cout << "Test 1: Default Constructor" << std::endl;
    Any a;
    assert(a.get<int>() == nullptr);
    std::cout << "  PASSED" << std::endl;
}

void test_template_constructor() 
{
    std::cout << "Test 2: Template Constructor" << std::endl;
    
    Any a1(42);
    Any a2(3.14);
    Any a3(std::string("hello"));
    
    std::cout << "  PASSED" << std::endl;
}

void test_copy_constructor() 
{
    std::cout << "Test 3: Copy Constructor" << std::endl;
    
    Any a1(42);
    Any a2(a1);
    
    Any a3(std::string("test"));
    Any a4(a3);
    
    std::cout << "  PASSED" << std::endl;
}

void test_destructor() 
{
    std::cout << "Test 4: Destructor" << std::endl;
    
    TestCounter::construct_count = 0;
    TestCounter::destruct_count = 0;
    
    {
        Any a{TestCounter()};  // 修复 most vexing parse
    }
    
    assert(TestCounter::construct_count == TestCounter::destruct_count);
    std::cout << "  PASSED" << std::endl;
}

void test_swap() 
{
    std::cout << "Test 5: Swap Method" << std::endl;
    
    Any a1(42);
    Any a2(3.14);
    
    a1.Swap(a2);
    
    std::cout << "  PASSED" << std::endl;
}

void test_get() 
{ 
    std::cout << "Test 6: Get Method" << std::endl; 
    
    Any a1(42); 
    Any a2(3.14); 
    Any a3(std::string("hello")); 
    
    int* p1 = a1.get<int>(); 
    double* p2 = a2.get<double>(); 
    std::string* p3 = a3.get<std::string>(); 
    
    if (p1) 
    { 
        std::cout << "  int value: " << *p1 << std::endl; 
    } 
    if (p2) { 
        std::cout << "  double value: " << *p2 << std::endl; 
    } 
    if (p3) { 
        std::cout << "  string value: " << *p3 << std::endl; 
    } 
    
    double* wrong_type = a1.get<double>();  // 修改这里
    assert(wrong_type == nullptr); 
    
    std::cout << "  PASSED" << std::endl; 
}

void test_assignment_operator_value() 
{
    std::cout << "Test 7: Assignment Operator (Value)" << std::endl;
    
    Any a1;
    a1 = 42;
    
    Any a2;
    a2 = 3.14;
    
    Any a3;
    a3 = std::string("test");
    
    std::cout << "  PASSED" << std::endl;
}

void test_assignment_operator_any() 
{
    std::cout << "Test 8: Assignment Operator (Any)" << std::endl;
    
    Any a1(42);
    Any a2;
    a2 = a1;
    
    Any a3(std::string("hello"));
    Any a4;
    a4 = a3;
    
    std::cout << "  PASSED" << std::endl;
}

void test_self_assignment() 
{
    std::cout << "Test 9: Self Assignment" << std::endl;
    
    Any a(42);
    a = a;
    
    std::cout << "  PASSED" << std::endl;
}

void test_chained_assignment() 
{
    std::cout << "Test 10: Chained Assignment" << std::endl;
    
    Any a1(42);
    Any a2;
    Any a3;
    
    a3 = a2 = a1;
    
    std::cout << "  PASSED" << std::endl;
}

void test_multiple_types() 
{
    std::cout << "Test 11: Multiple Types" << std::endl;
    
    Any a1(42);
    Any a2(3.14);
    Any a3('c');
    Any a4(true);
    Any a5(std::string("hello"));
    
    std::cout << "  PASSED" << std::endl;
}

void test_empty_any() 
{
    std::cout << "Test 12: Empty Any Operations" << std::endl;
    
    Any a1;
    Any a2;
    
    a1.Swap(a2);
    
    Any a3;
    a3 = a1;
    
    std::cout << "  PASSED" << std::endl;
}

void test_overwrite_value() 
{
    std::cout << "Test 13: Overwrite Value" << std::endl;
    
    Any a(42);
    a = 3.14;
    
    std::cout << "  PASSED" << std::endl;
}

void test_complex_type() 
{
    std::cout << "Test 14: Complex Type" << std::endl;
    
    struct Point {
        int x, y;
        Point(int x_, int y_) : x(x_), y(y_) {}
    };
    
    Any a(Point(10, 20));
    Point* p = a.get<Point>();
    
    if (p) {
        std::cout << "  Point: (" << p->x << ", " << p->y << ")" << std::endl;
    }
    
    std::cout << "  PASSED" << std::endl;
}

void test_swap_with_empty()
{
    std::cout << "Test 15: Swap with Empty Any" << std::endl;
    
    Any a1(42);
    Any a2;
    
    a1.Swap(a2);
    
    std::cout << "  PASSED" << std::endl;
}

int main()
{
    std::cout << "=== MyAny Unit Tests ===" << std::endl << std::endl;
    
    test_default_constructor();
    test_template_constructor();
    test_copy_constructor();
    test_destructor();
    test_swap();
    test_get();
    test_assignment_operator_value();
    test_assignment_operator_any();
    test_self_assignment();
    test_chained_assignment();
    test_multiple_types();
    test_empty_any();
    test_overwrite_value();
    test_complex_type();
    test_swap_with_empty();
    
    std::cout << std::endl << "=== All Tests Completed ===" << std::endl;
    return 0;
}

结果为:

本期内容就到这里了,喜欢请点个赞谢谢

封面图自取:

相关推荐
赋创小助手2 小时前
OpenClaw部署架构详解:从桌面到数据中心的AI Agent服务器选型指南
服务器·人工智能·架构·agent·openclaw
xiaoxiaoxiaolll2 小时前
《Nature Communications》:集成热光调制与3D空间并行的光子神经网络芯片
学习
IT摆渡者2 小时前
Linux 巡检脚本BASH
linux·运维·bash
小宋加油啊2 小时前
服务器双卡5090 配置深度学习环境
运维·服务器·深度学习
minji...2 小时前
Linux 网络套接字编程(二)从 0 到 1 实现 UDP 回声服务器,recvfrom,sendto
linux·运维·网络·单片机·udp
不败公爵2 小时前
finsh_thread_entry这个线程是自动启动的
java·linux·服务器
实心儿儿2 小时前
Linux —— 基础IO — 文件描述符 + 重定向
linux·运维·服务器
码喽7号2 小时前
JsonWeb token(JWT)跨域认证
spring boot·学习
workflower2 小时前
机器人应用-室外区域巡逻
人工智能·设计模式·机器人·软件工程·软件构建