废话不多说,直接上代码:

#include <iostream>
#include <any>
#include <memory>
#include <string>
#include <unordered_map>

// Context接口定义
class Context {
public:
    virtual ~Context() = default;
    
    /**
     * 获取与key关联的值,如果不存在则返回空std::any
     * 模拟Go中Context的Value方法
     */
    virtual std::any Value(const std::any& key) const = 0;
    
    /**
     * 便捷方法:尝试将值转换为指定类型,如果失败返回默认值
     */
    template<typename T>
    T ValueAs(const std::any& key, const T& defaultValue = T{}) const {
        std::any result = Value(key);
        try {
            return std::any_cast<T>(result);
        } catch (const std::bad_any_cast&) {
            return defaultValue;
        }
    }
};

// 根Context实现(类似于Go的context.Background())
class BackgroundContext : public Context {
public:
    std::any Value(const std::any& key) const override {
        return {}; // 根Context不存储任何值
    }
    
    std::string String() const { return "BackgroundContext"; }
};

// 存储单个键值对的Context实现
class ValueContext : public Context {
private:
    std::shared_ptr<const Context> parent_; // 父Context的共享指针
    std::any key_;                          // 当前Context存储的键
    std::any value_;                        // 当前Context存储的值

public:
    /**
     * 构造函数:基于父Context创建新的ValueContext
     * @param parent 父Context
     * @param key 要存储的键
     * @param value 要存储的值
     */
    ValueContext(std::shared_ptr<const Context> parent, std::any key, std::any value)
        : parent_(std::move(parent)), key_(std::move(key)), value_(std::move(value)) {}

    std::any Value(const std::any& query_key) const override {
        // 比较键的类型和值
        if (!key_.has_value() || !query_key.has_value()) {
            // 如果任一键为空,继续查找父Context
        } else if (key_.type() == query_key.type()) {
            // 类型相同,比较值
            // 注意:这里简化比较,实际使用时建议使用可比较的类型作为键
            try {
                // 对于基本类型(int、string等)可以这样比较
                if (key_.type() == typeid(int)) {
                    if (std::any_cast<int>(key_) == std::any_cast<int>(query_key)) {
                        return value_;
                    }
                } else if (key_.type() == typeid(std::string)) {
                    if (std::any_cast<std::string>(key_) == std::any_cast<std::string>(query_key)) {
                        return value_;
                    }
                }
                // 可以继续添加其他类型的比较...
            } catch (const std::bad_any_cast&) {
                // 类型转换失败,继续查找父Context
            }
        }
        
        // 如果当前Context没有匹配的键,委托给父Context进行链式查找
        if (parent_) {
            return parent_->Value(query_key);
        }
        
        // 整条链都找不到,返回空any
        return {};
    }
};

// 工厂函数
namespace context {

/**
 * 返回一个全局的根Context(类似于Go的context.Background())
 */
std::shared_ptr<const Context> Background() {
    static auto bg = std::make_shared<BackgroundContext>();
    return bg;
}

/**
 * 基于父Context创建新的Context,并存储一个键值对
 * 类似于Go的context.WithValue(parent, key, value)
 * @param parent 父Context
 * @param key 键
 * @param value 值
 * @return 新的Context共享指针
 */
std::shared_ptr<const Context> WithValue(std::shared_ptr<const Context> parent, 
                                       std::any key, std::any value) {
    return std::make_shared<ValueContext>(std::move(parent), std::move(key), std::move(value));
}

} // namespace context

// 使用示例和测试函数
void demonstrateBasicUsage() {
    std::cout << "=== 基本用法演示 ===" << std::endl;
    
    // 1. 从根Context开始
    auto ctx = context::Background();
    
    // 2. 添加用户信息
    ctx = context::WithValue(ctx, std::string("user_id"), 1001);
    ctx = context::WithValue(ctx, std::string("username"), std::string("Alice"));
    ctx = context::WithValue(ctx, std::string("priority"), 5);
    
    // 3. 读取值
    int user_id = ctx->ValueAs<std::string, int>("user_id", 0);
    std::string username = ctx->ValueAs<std::string, std::string>("username", "unknown");
    int priority = ctx->ValueAs<std::string, int>("priority", 1);
    
    std::cout << "用户ID: " << user_id << std::endl;
    std::cout << "用户名: " << username << std::endl;
    std::cout << "优先级: " << priority << std::endl;
}

void demonstrateChainLookup() {
    std::cout << "\n=== 链式查找演示 ===" << std::endl;
    
    // 创建Context链:ctx3 -> ctx2 -> ctx1 -> root
    auto ctx1 = context::WithValue(context::Background(), std::string("level"), 1);
    auto ctx2 = context::WithValue(ctx1, std::string("level"), 2); // 覆盖level值
    auto ctx3 = context::WithValue(ctx2, std::string("data"), std::string("important"));
    
    // ctx3可以访问所有层级的值
    int level_from_ctx3 = ctx3->ValueAs<std::string, int>("level", 0);
    std::string data = ctx3->ValueAs<std::string, std::string>("data", "");
    
    std::cout << "ctx3中level的值: " << level_from_ctx3 << " (来自ctx2的覆盖)" << std::endl;
    std::cout << "ctx3中data的值: " << data << std::endl;
    
    // ctx1只能访问到它和父级的值
    int level_from_ctx1 = ctx1->ValueAs<std::string, int>("level", 0);
    std::cout << "ctx1中level的值: " << level_from_ctx1 << std::endl;
}

void demonstrateImmutability() {
    std::cout << "\n=== 不可变性演示 ===" << std::endl;
    
    auto original = context::WithValue(context::Background(), std::string("version"), 1);
    
    // 基于original创建新的Context,不改变original
    auto extended = context::WithValue(original, std::string("stage"), std::string("testing"));
    
    // original看不到新添加的值
    auto stage_in_original = original->ValueAs<std::string, std::string>("stage", "not_found");
    auto version_in_original = original->ValueAs<std::string, int>("version", 0);
    
    // extended可以看到所有值
    auto stage_in_extended = extended->ValueAs<std::string, std::string>("stage", "not_found");
    auto version_in_extended = extended->ValueAs<std::string, int>("version", 0);
    
    std::cout << "original中stage: " << stage_in_original << std::endl;
    std::cout << "original中version: " << version_in_original << std::endl;
    std::cout << "extended中stage: " << stage_in_extended << std::endl;
    std::cout << "extended中version: " << version_in_extended << std::endl;
}

// 模拟实际业务场景中的使用
class RequestProcessor {
private:
    std::shared_ptr<const Context> ctx_;
    
public:
    RequestProcessor(std::shared_ptr<const Context> ctx) : ctx_(std::move(ctx)) {}
    
    void process() {
        // 从Context中获取请求信息
        int user_id = ctx_->ValueAs<std::string, int>("user_id", 0);
        std::string request_id = ctx_->ValueAs<std::string, std::string>("request_id", "unknown");
        
        std::cout << "处理请求 - 用户ID: " << user_id 
                  << ", 请求ID: " << request_id << std::endl;
        
        // 在处理过程中可以添加更多信息
        auto processing_ctx = context::WithValue(ctx_, std::string("status"), std::string("processing"));
        
        // 传递给下一层处理
        validateRequest(processing_ctx);
    }
    
private:
    void validateRequest(std::shared_ptr<const Context> ctx) {
        std::string request_id = ctx->ValueAs<std::string, std::string>("request_id", "unknown");
        std::string status = ctx->ValueAs<std::string, std::string>("status", "unknown");
        
        std::cout << "验证请求 - 请求ID: " << request_id 
                  << ", 状态: " << status << std::endl;
    }
};

void demonstrateRealWorldScenario() {
    std::cout << "\n=== 实际业务场景演示 ===" << std::endl;
    
    // 创建包含请求信息的Context
    auto ctx = context::Background();
    ctx = context::WithValue(ctx, std::string("user_id"), 1001);
    ctx = context::WithValue(ctx, std::string("request_id"), std::string("req_abc123"));
    ctx = context::WithValue(ctx, std::string("timestamp"), 1637084400);
    
    // 使用Context处理请求
    RequestProcessor processor(ctx);
    processor.process();
}

int main() {
    std::cout << "C++ Context实现 - 模拟Go语言Context的信息传递功能\n" << std::endl;
    
    try {
        demonstrateBasicUsage();
        demonstrateChainLookup();
        demonstrateImmutability();
        demonstrateRealWorldScenario();
        
        std::cout << "\n=== 所有演示完成 ===" << std::endl;
        
    } catch (const std::exception& e) {
        std::cerr << "发生异常: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

编译和运行说明

编译命令(需要C++17支持):

g++ -std=c++17 -o context_demo context_demo.cpp

或者使用clang:

clang++ -std=c++17 -o context_demo context_demo.cpp

运行:

./context_demo

核心特性说明

这个实现包含了Go Context的核心思想:

  1. 链式查找:通过ValueContext维护父Context的引用,实现递归查找

  2. 不可变性:每次调用WithValue都创建新的Context,原Context保持不变

  3. 类型安全:使用模板方法ValueAs提供类型安全的访问

  4. 简单接口:专注于信息传递,去除了复杂的取消机制

关键设计要点

  • 使用std::shared_ptr管理生命周期:确保Context链的正确内存管理

  • std::any存储任意类型:支持灵活的数据存储

  • 简单的键比较逻辑:支持基本类型的键比较,可根据需要扩展

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐