I am working on a piece that requires some compile-time data structures to be created from a run-time provided mapping. If the runtime provided mapping matches the pre-defined compile-time pattern, the appropriate data structure should be created. However, the compile-time data structure can be defined by any combination of objects or vector of objects. These objects all inherit from the same parent and they are stored in a map using std::variant.
Given that its essentially pattern matching and is allowed to fail if run-time does not provide a pattern that is defined at compile-time, this should be possible somehow...? However, the std::variant seems to be complicating things a lot. Here is what I have so far:
#include <vector>
#include <unordered_map>
#include <string>
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);
}
};
// the function I want to implement
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);
// implementation specific to example
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>>{};
// Example usage
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);
// Manual approach:
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);
// Can we automate the above? - create a mapping that mirrors above manual approach
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;
}