从具有运行时索引的元组中选择一组值 [英] select a set of values from tuple with run-time index

查看:67
本文介绍了从具有运行时索引的元组中选择一组值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题的简短介绍:
我正在尝试使用stl容器实现某种关系数据库。这只是出于娱乐/教育目的,因此不需要诸如使用此库,这绝对没有用之类的答案。
我知道此时的标题有些混乱,但是我们会达到目的(确实欢迎对标题进行改进的建议)。

Short introduction to my questions: i'm trying to implement a "sort of" relational database using stl containers. This is just for fun/educational purpose, so no need for answers like "use this library", "this is absolutely useless" and so on. I know title is a little bit confusing at this point, but we will reach the point (suggestions for improvement to title are really welcome).

我继续只需几个步骤:


  1. 我可以将表构建为从列名到其值的映射向量=> std: :vector< std :: map< std :: string,some_variant>> 。很简单,它代表了我的需要。

  2. 等等,我只存储一次列名,然后使用其索引访问值。 => std :: vector< std :: vector< some_variant>> 。与第1点一样简单,但比第1点要快。

  3. wait wait,在数据库中,表从字面上看是一个元组序列=> std :: vector< std :: tuple< args ...>> 。这很酷,它恰好表示我在做什么,没有变体的正确类型,甚至比另一个还快。

  1. i can build table as vector of maps from columns name to their values => std::vector<std::map<std::string, some_variant>>. It's simple and it represents what i need.
  2. wait, i can just store column's names once and access values with their index. => std::vector<std::vector<some_variant>>.As simple as point 1, but faster than that.
  3. wait wait, in a database a table is literrally a sequence of tuple => std::vector<std::tuple<args...>>. This is cool, it represents exactly what i'm doing, correct type without variant and even faster than the other.

注意:对于
1000000个记录,用这样的简单循环来测量快于:

Note: the "faster than" was measured for 1000000 records with a simple loop like this:

std::random_device dev;
std::mt19937 gen(dev());
std::uniform_int_distribution<long> rand1_1000(1, 1000);
std::uniform_real_distribution<double> rand1_10(1.0, 10.0);

void fill_1()
{
    using my_variant = std::variant<long, long long, double, std::string>;
    using values = std::map<std::string, my_variant>;
    using table = std::vector<values>;

    table t;
    for (int i = 0; i < 1000000; ++i)
        t.push_back({ {"col_1", rand1_1000(gen)}, {"col_2", rand1_1000(gen)}, {"col_3", rand1_10(gen)} });
    std::cout << "size:" << t.size() << "\n";//just to prevent optimization
}




  1. 2234101600ns-avg:2234

  1. 2234101600ns - avg:2234

446344100ns-avg:446

446344100ns - avg:446

132075400ns-平均:132

132075400ns - avg:132

插入:
任何一种解决方案,插入都像示例中的推回元素一样简单。

INSERT: No problem with any of these solutions, insert are as simple as pushing back elements as in the example.

SELECT:
1和2是很简单,但是3却很棘手。

SELECT: 1 and 2 are simple, but 3 is tricky.

所以,最后,问题是:


  1. 内存使用量:使用解决方案1和2会消耗大量内存。因此,这里3似乎再次是正确的选择。
    例如,有100万条记录,记录了2个 long s和 double 的记录,我期望的记录接近4MB * 2为多头,8MB为双打,外加矢量,映射和变体的使用开销。相反,我们有(用Windows任务管理器测量,不是很准确,我知道):

  1. Memory usage: there is a lot of overhead using solution 1 and 2 in term of used memory. So, 3 seems to be again the right choice here. For the example with 1 million records of 2 longs and a double i was expecteing something near 4MB*2 for longs and 8MB for doubles plus some overhead for vectors, maps and variants where used. Instead we have (measured with windows task manager, not extremely accurate, i know):

1.340 MB

2.120 MB

3.31 MB

我丢失了什么吗?除了保留

是否可以使用预先设置的正确大小?或者在插入循环后使用 shrink_to_fit ?运行时是否检索某些元组字段(如select语句一样?)?

Is there a way to run-time retrieve some tuple field as in the case of a select statement?



using my_tuple = std::tuple<long, long, string,  double>;
std::vector<my_tuple> table;
int to_select;//this could be a vector of columns to select obviosly
std::cin>>to_select;
auto result = select (table, to_select);

您是否看到以任何方式实施最后一行的机会?
对于我所看到的,我们有两个问题:结果类型应该采用起始元组的类型,然后实际执行所需字段的选择。

Do you see any chance to implement this last line in any way? We have two problem for what i see: the result type should take the the type from the starting tuple and then, actually perform the selection of desired fields.

我读了很多有关此问题的答案,他们都谈论使用 make_index_sequence 或符合时间的已知索引的连续索引。
我还找到了本文,非常有趣,但在这种情况下并不是很有用。

I read a lot of answers about that, they all talk about contiguous indexes using make_index_sequence or complile-time known index. I also found this article, very interesting, but not really useful for this case.

推荐答案

这是可行的,但很奇怪:

This is doable but it is strange:

template<size_t candidate, typename ...T>
constexpr std::variant<T...> helperTupleValueAt(const std::tuple<T...>& t, size_t index)
{
    if constexpr (candidate >= sizeof...(T)) {
        throw std::logic_error("out of bounds");
    } else {
        if (candidate == index) {
            return std::variant<T...>{ std::in_place_index<candidate>, std::get<candidate>(t) };
        } else {
            return helperTupleValueAt<candidate + 1>(t, index);
        }
    }
}

template<typename ...T>
std::variant<T...> tupleValueAt(const std::tuple<T...>& t, size_t index)
{
    return helperTupleValueAt<0>(t, index);
}

https://wandbox.org/permlink/FQJd4chAFVSg5eSy

这篇关于从具有运行时索引的元组中选择一组值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆