You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++模板参数包位置错误修复:高性能Pipeline类实现方案

修复Pipeline类模板参数错误的高性能方案

错误原因

你的代码编译报错有两个核心原因:

  1. C++标准规定,一个模板只能包含一个可变参数包,且必须放在模板参数列表的最后位置。原代码同时使用了typename... Args和StageFunctionRefType<...>... StageFunctions两个参数包,违反了这一规则。
  2. 即使只保留一个参数包,Args位于StageFunctions之前的写法也不符合参数包必须后置的要求。

修复方案(C++17+)

使用C++17引入的**auto模板参数**直接传递阶段函数指针,既能避免参数包冲突,又能让编译器自动推导函数签名,全程保持编译期解析,完全避免虚函数或std::function带来的运行时开销。

修复后完整代码

#include <array>
#include <tuple>
#include <type_traits>
#include <utility>

enum class ParseState
{
    EXTRACTED,   ///< 阶段处理完成,已提取有效数据
    IN_PROGRESS, ///< 阶段处理中,需要更多输入数据
    REJECTED,    ///< 数据无效,处理终止
};

struct ParseStage
{
    std::size_t stage = 0;

    std::size_t get_stage() const noexcept { return stage; }
    std::size_t next_stage() noexcept { return ++stage; }
    void reset() noexcept { stage = 0; }
};

template <typename InputDataType>
using ParseResult = std::pair<ParseState, InputDataType>;

// 核心Pipeline类:使用auto模板参数接收阶段函数指针
template <typename InputDataType, typename ParserState, auto... StageFunctions>
class Pipeline
{
    // 存储阶段函数的编译期数组(constexpr保证编译期初始化)
    static constexpr std::array Stages{ StageFunctions... };

    // 元工具:从阶段函数签名中提取额外参数类型
    template <typename Fn>
    struct ExtractArgs;

    // 处理函数引用的情况
    template <typename... Args>
    struct ExtractArgs<ParseResult<InputDataType>(const InputDataType&, ParserState&, Args...)>
    {
        using ArgsTuple = std::tuple<Args...>;
    };

    // 处理函数指针的情况
    template <typename... Args>
    struct ExtractArgs<ParseResult<InputDataType>(*)(const InputDataType&, ParserState&, Args...)>
    {
        using ArgsTuple = std::tuple<Args...>;
    };

    // 所有阶段函数必须共享相同的额外参数签名
    using ExpectedArgs = typename ExtractArgs<decltype(Stages[0])>::ArgsTuple;

public:
    template <typename... Args>
    static ParseResult<InputDataType> feed(const InputDataType& input_data, ParserState& parser_state,
                                           ParseStage& parse_stage, Args&&... args)
    {
        // 编译期检查:传入的额外参数必须与阶段函数签名匹配
        static_assert(std::is_same_v<std::tuple<std::decay_t<Args>...>, ExpectedArgs>,
                      "额外参数类型与阶段函数签名不匹配");

        // 已被拒绝的状态直接返回
        if (parser_state.parse_state == ParseState::REJECTED)
            return { ParseState::REJECTED, input_data };

        auto remaining_data = input_data;

        while (parse_stage.get_stage() < Stages.size() && !remaining_data.empty())
        {
            const auto parse_result = Stages[parse_stage.get_stage()](remaining_data, parser_state, std::forward<Args>(args)...);
            remaining_data = parse_result.second;

            switch (parse_result.first)
            {
                case ParseState::EXTRACTED:
                    parse_stage.next_stage();
                    break;
                case ParseState::REJECTED:
                    return parse_result;
                case ParseState::IN_PROGRESS:
                    // 需要更多数据,退出循环
                    goto exit_loop;
            }
        }
    exit_loop:
        // 所有阶段处理完成
        if (parse_stage.get_stage() == Stages.size())
            return { ParseState::EXTRACTED, remaining_data };

        return { ParseState::IN_PROGRESS, remaining_data };
    }
};

使用示例

假设你有如下阶段函数和解析状态类:

// 自定义解析状态类
struct MyParserState
{
    ParseState parse_state = ParseState::IN_PROGRESS;
    // 其他解析状态数据...
};

// 阶段函数1:接收std::string输入,额外参数为int
ParseResult<std::string> stage1(const std::string& data, MyParserState& state, int threshold)
{
    // 实现处理逻辑...
    if (data.size() >= threshold)
        return { ParseState::EXTRACTED, data.substr(threshold) };
    return { ParseState::IN_PROGRESS, data };
}

// 阶段函数2:签名与stage1一致
ParseResult<std::string> stage2(const std::string& data, MyParserState& state, int threshold)
{
    // 实现处理逻辑...
    if (data.find("end") != std::string::npos)
        return { ParseState::EXTRACTED, data.substr(data.find("end") + 3) };
    return { ParseState::IN_PROGRESS, data };
}

// 定义Pipeline类型
using MyStringPipeline = Pipeline<std::string, MyParserState, &stage1, &stage2>;

// 使用Pipeline
int main()
{
    MyParserState state;
    ParseStage stage;
    std::string input = "hello123endxyz";

    auto result = MyStringPipeline::feed(input, state, stage, 5);
    // 处理result...
    return 0;
}

方案优势

  1. 极致性能:所有阶段函数调用都是编译期确定的直接函数调用,无虚函数表查询或std::function的类型擦除开销。
  2. 类型安全:编译期检查阶段函数签名一致性,以及传入参数的匹配性,提前发现错误。
  3. 简洁易用:使用auto模板参数直接传递函数指针,无需手动指定复杂的函数类型。

内容的提问来源于stack exchange,提问作者Amir Hossein Sarebani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 09:29:55