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

基于运行时映射构建编译期tuple结构的技术问询

问题描述

我正在开发一项功能,需基于运行时提供的映射创建编译期数据结构。若运行时映射匹配预定义的编译期模式,应生成对应结构;该结构可由对象或对象vector的任意组合构成,所有对象均继承自同一父类,并通过std::variant存储在map中。这本质属于模式匹配场景,若运行时映射不符合编译期定义的模式,允许执行失败,但std::variant大幅增加了实现复杂度。以下是我已完成的代码,希望实现get_tuple函数,自动完成手动构建Test::DataKV_tuple的过程:

#include <vector>
#include <unordered_map>
#include <string>
#include <tuple>
#include <variant>
#include <stdexcept>


template <typename K, typename V>
class DataKV{
public:
    using typeK = K;
    using typeV = V;
};

template <typename... Ts>
class DataKVPackage{
public:
    using DataKVTuple = std::tuple<Ts...>;
    DataKVTuple data_;
    explicit DataKVPackage(DataKVTuple& data): data_(data){}
};

template <typename T>
struct remove_vector {
    using type = T;
};

template <typename T>
struct remove_vector<std::vector<T>> {
    using type = T;
};

template <typename T>
using remove_vector_t = typename remove_vector<T>::type;

template<class Derived, typename... Ts>
class Foo_interface{
public:
    using DataKV_raw_variants = std::variant<remove_vector_t<Ts>...>;
    using DataKV_tuple = std::tuple<Ts...>;
    using DataKVPackage = DataKVPackage<Ts...>;
    Foo_interface()=default;
};

template <typename T>
class Orchestrator{
public:
    std::unordered_map<std::string, typename T::DataKV_raw_variants> map_data;
    void add_data(std::string data_id, const auto& dataKV_ptr){
        map_data[data_id] = typename T::DataKV_raw_variants(dataKV_ptr);
    }
};

// 待实现的函数
template <typename T>
typename T::DataKV_tuple get_tuple(std::unordered_map<int, std::variant<std::string, std::vector<std::string>>> mapping, Orchestrator<T> orch);


// 示例专属定义
using DATA_A = DataKV<int, float>;
using DATA_B = DataKV<double, std::string>;
using DATA_C = DataKV<char, bool>;
class Test : public Foo_interface<Test, std::vector<DATA_A>, DATA_B, std::vector<DATA_C>>{};



// 示例用法
int main() {
    Orchestrator<Test> foo;
    DATA_A F_a_0 = DATA_A();
    DATA_A F_a_1 = DATA_A();
    DATA_A F_a_2 = DATA_A();
    DATA_B F_b_0 = DATA_B();
    DATA_B F_b_1 = DATA_B();
    DATA_B F_b_2 = DATA_B();
    DATA_C F_c_0 = DATA_C();
    DATA_C F_c_1 = DATA_C();
    DATA_C F_c_2 = DATA_C();
    foo.add_data("F_a_0", F_a_0);
    foo.add_data("F_a_1", F_a_1);
    foo.add_data("F_a_2", F_a_2);
    foo.add_data("F_b_0", F_b_0);
    foo.add_data("F_b_1", F_b_1);
    foo.add_data("F_b_2", F_b_2);
    foo.add_data("F_c_0", F_c_0);
    foo.add_data("F_c_1", F_c_1);
    foo.add_data("F_c_2", F_c_2);

    // 手动构建方式
    std::vector<DATA_A> many_F_a;
    many_F_a.push_back(F_a_0);
    many_F_a.push_back(F_a_1);
    std::vector<DATA_C> many_F_c;
    many_F_c.push_back(F_c_1);
    many_F_c.push_back(F_c_2);
    Test::DataKV_tuple dat_manual = std::make_tuple(many_F_a, F_b_0, many_F_c);
    Test::DataKVPackage data_pack_manual = DataKVPackage<std::vector<DATA_A>, DATA_B, std::vector<DATA_C>>(dat_manual);

    // 自动构建的映射配置
    std::unordered_map<int, std::variant<std::string, std::vector<std::string>>> mapping;
    std::vector<std::string> datas_a = {"F_a_0", "F_a_1"};
    std::string data_b = "F_b_0";
    std::vector<std::string> datas_c = {"F_c_1", "F_c_2"};
    mapping[0] = datas_a;
    mapping[1] = data_b;
    mapping[2] = datas_c;
    
    Test::DataKV_tuple dat_auto = get_tuple<Test>(mapping, foo);
//    auto data_pack_auto = Test::DataKVPackage(dat_auto);

    return 0;
}
实现方案

以下是get_tuple函数的完整实现,通过编译期类型遍历和运行时数据提取完成自动构建:

// 辅助模板:提取Foo_interface的类型参数列表
template<typename T>
struct get_foo_types;

template<typename Derived, typename... Ts>
struct get_foo_types<Foo_interface<Derived, Ts...>> {
    using types = std::tuple<Ts...>;
};

// 辅助函数:提取单个DataKV实例
template<typename T>
T extract_single(const std::string& key, const Orchestrator<auto>& orch) {
    auto it = orch.map_data.find(key);
    if (it == orch.map_data.end()) {
        throw std::runtime_error("Key not found: " + key);
    }
    try {
        return std::get<T>(it->second);
    } catch (const std::bad_variant_access&) {
        throw std::runtime_error("Type mismatch for key: " + key);
    }
}

// 辅助函数:提取多个DataKV实例并组装成vector
template<typename T>
std::vector<T> extract_vector(const std::vector<std::string>& keys, const Orchestrator<auto>& orch) {
    std::vector<T> vec;
    vec.reserve(keys.size());
    for (const auto& key : keys) {
        vec.push_back(extract_single<T>(key, orch));
    }
    return vec;
}

// 辅助函数:根据元素类型处理映射项
template<typename ElemType>
ElemType process_mapping_item(const std::variant<std::string, std::vector<std::string>>& var, const Orchestrator<auto>& orch) {
    if constexpr (std::is_same_v<ElemType, remove_vector_t<ElemType>>) {
        // 当前元素是单个对象
        if (!std::holds_alternative<std::string>(var)) {
            throw std::runtime_error("Expected single string for element type");
        }
        return extract_single<ElemType>(std::get<std::string>(var), orch);
    } else {
        // 当前元素是vector
        using InnerType = remove_vector_t<ElemType>;
        if (!std::holds_alternative<std::vector<std::string>>(var)) {
            throw std::runtime_error("Expected string vector for element type");
        }
        return extract_vector<InnerType>(std::get<std::vector<std::string>>(var), orch);
    }
}

// 核心实现:遍历tuple索引构建元素
template<typename T, std::size_t... Is>
typename T::DataKV_tuple get_tuple_impl(const std::unordered_map<int, std::variant<std::string, std::vector<std::string>>>& mapping, 
                                        const Orchestrator<T>& orch,
                                        std::index_sequence<Is...>) {
    return std::make_tuple(
        process_mapping_item<std::tuple_element_t<Is, typename get_foo_types<typename T::Foo_interface>::types>>(
            mapping.at(Is), orch
        )...
    );
}

// 对外接口函数
template <typename T>
typename T::DataKV_tuple get_tuple(std::unordered_map<int, std::variant<std::string, std::vector<std::string>>> mapping, Orchestrator<T> orch) {
    using TupleType = typename T::DataKV_tuple;
    constexpr auto tuple_size = std::tuple_size_v<TupleType>;
    return get_tuple_impl<T>(mapping, orch, std::make_index_sequence<tuple_size>());
}
关键逻辑说明
  • 类型提取:通过get_foo_types模板特化,从Foo_interface中获取预定义的编译期类型列表,这是实现类型匹配的核心。
  • 数据提取:
    • extract_single处理单个元素的提取,处理键不存在、类型不匹配的异常情况。
    • extract_vector批量提取元素并组装成vector。
  • 映射处理:process_mapping_item通过constexpr编译期判断元素类型是单个对象还是vector,自动选择对应提取逻辑,并验证映射项的类型是否匹配。
  • Tuple组装:利用std::index_sequence遍历tuple的每个索引,为每个位置生成对应的元素,最终组装成目标tuple。
  • 错误处理:所有运行时不匹配的情况(键不存在、类型不匹配、映射项类型错误)均抛出异常,符合需求中“允许执行失败”的要求。

内容的提问来源于stack exchange,提问作者user3641187

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 13:55:00