更好的数据存储和传递解决方案

时间:2015-07-05 06:47:51

标签: c++ c++14

我正在尝试为我正在处理的一些代码找到更优雅的解决方案。我有需要存储的数据然后移动,但我真的不想占用比我需要的存储数据更多的空间。

我有2个解决方案,但似乎都不太好。

使用继承和标记

enum class data_type{
    first, second, third
};

class data_base{
    public:
        virtual data_type type() const noexcept = 0;
};

using data_ptr = data_base*;

class first_data: public data_base{
    public:
        data_type type() const noexcept{return data_type::first;}

        // hold the first data type
};

// ...

然后你传递data_ptr并将其转换为适当的类型。

我真的不喜欢这种方法,因为它需要向上投射和使用裸指针。

使用union并存储所有数据类型

enum class data_type{
    first, second, third
};

class data{
    public:
        data(data_type type_): type(type_){}
        data_type type;
        union{
            // first, second and third data types stored
        };
};

但我不喜欢这种方法,因为当你有一个可能传递的大数据类型时,你会开始浪费大量内存。

然后将此数据传递给将其解析为更大表达式的函数。像这样:

class expression{/* ... */};
class expr_type0: public expression{/* ... */};
// every expression type

using expr_ptr = expression*;

// remember to 'delete'
expr_ptr get_expression(){
    data_ptr dat = get_data();        

    // interpret data
    // may call 'get_data()' many times
    expr_ptr expr = new /* expr_type[0-n] */

    delete dat;
    return expr;
}

并且问题再次出现,但在这种情况下并不重要,因为expr_ptr不需要重新解释,并且将有一个简单的虚函数调用。

什么是更优雅的标记方法并将数据传递给另一个函数?

1 个答案:

答案 0 :(得分:0)

如果没有更多信息,很难设想你正在寻找什么。但是,如果我想要一些允许我以某种结构化的方式存储和检索数据的框架,那么在我未知的存储设备中,这就是我想的那种方式。

这可能不是您正在寻找的答案,但我认为这里的概念会激励您朝着正确的方向前进。

#include <iostream>
#include <tuple>
#include <boost/variant.hpp>
#include <map>


// define some concepts

// bigfoo is a class that's expensive to copy - so lets give it a shared-handle idiom
struct bigfoo {

    struct impl {
        impl(std::string data) : _data(std::move(data)) {}
        void write(std::ostream& os) const {
            os << "I am a big object. Don't copy me: " << _data;
        }
    private:
        std::string _data;
    };

    bigfoo(std::string data) : _impl { std::make_shared<impl>(std::move(data)) } {};

    friend std::ostream& operator<<(std::ostream&os, const bigfoo& bf) {
        bf._impl->write(os);
        return os;
    }

private:
    std::shared_ptr<impl> _impl;
};

// all the data types our framework handles
using abstract_data_type = boost::variant<int, std::string, double, bigfoo>;

// defines the general properties of a data table store concept
template<class...Columns>
struct table_definition
{
    using row_type = std::tuple<Columns...>;
};

// the concept of being able to store some type of table data on some kind of storage medium
template<class IoDevice, class TableDefinition>
struct table_implementation
{
    using io_device_type = IoDevice;
    using row_writer_type = typename io_device_type::row_writer_type;

    template<class...Args> table_implementation(Args&...args)
    : _io_device(std::forward<Args>(args)...)
    {}

    template<class...Args>
    void add_row(Args&&...args) {
        auto row_instance = _io_device.open_row();
        set_row_args(row_instance,
                     std::make_tuple(std::forward<Args>(args)...),
                     std::index_sequence_for<Args...>());
        row_instance.commit();
    }

private:
    template<class Tuple, size_t...Is>
    void set_row_args(row_writer_type& row_writer, const Tuple& args, std::index_sequence<Is...>)
    {
        using expand = int[];
        expand x { 0, (row_writer.set_value(Is, std::get<Is>(args)), 0)... };
        (void)x; // mark expand as unused;
    }

private:
    io_device_type _io_device;
};

// model the concepts into a concrete specialisation

// this is a 'data store' implementation which simply stores data to stdout in a structured way
struct std_out_io
{
    struct row_writer_type
    {
        void set_value(size_t column, abstract_data_type value) {
            // roll on c++17 with it's much-anticipated try_emplace...
            auto ifind = _values.find(column);
            if (ifind == end(_values)) {
                ifind = _values.emplace(column, std::move(value)).first;
            }
            else {
                ifind->second = std::move(value);
            }
        }

        void commit()
        {
            std::cout << "{" << std::endl;
            auto sep = "\t";
            for (auto& item : _values) {
                std::cout << sep << item.first << "=" << item.second;
                sep = ",\n\t";
            }
            std::cout << "\n}";
        }

    private:
        std::map<size_t, abstract_data_type> _values;  // some value mapped by ascending column number
    };

    row_writer_type open_row() {
        return row_writer_type();
    }
};

// this is a model of a 'data table' concept
using my_table = table_definition<int, std::string, double, bigfoo>;


// here is a test
auto main() -> int
{
    auto data_store = table_implementation<std_out_io, my_table>( /* std_out_io has default constructor */);
    data_store.add_row(1, "hello", 6.6, bigfoo("lots and lots of data"));

    return 0;
}

预期产出:

{
        0=1,
        1=hello,
        2=6.6,
        3=I am a big object. Don't copy me: lots and lots of data
}