【问题标题】:C++: Extract size of boost::variant elementC++:boost::variant 元素的提取大小
【发布时间】:2014-11-19 18:47:27
【问题描述】:

我有一个向量,其中包含带有 boost::variant 元素的结构。

现在我必须序列化这个向量。由于规范的原因,我必须计算保存该向量所需的八位字节。现在我正在寻找一种以简单的方式实现这一点的选项。

int allSize = 0;

for(auto it=vec.begin(); it != vec.end(); it++){
    //something like size = sizeof(it->variant)
    allsize += size;
}

我试图用

获取元素的大小
sizeof(it->variant.type())

但这仅显示变体元素的大小(这是从变体中保存的最大元素的大小)

那么,有没有一种简单的方法来获取序列化数据的大小? 还是我必须用大约 7 个模板写一个访问者?

【问题讨论】:

  • 变体成员类型是什么?您将如何序列化(总是将元素数据按位复制为 POD?)
  • 是的,例如:变体成员是一个结构,它有一个 time64 值和一个 uint8 值。所以我序列化了一个 uint8 和一个 uint64 (这是 time64 值)。我必须这样做,因为 time64 没有序列化方法。

标签: c++ boost


【解决方案1】:

您可以在访问者中执行此操作,如下所示:

/*
template <typename T>
using EnableIf = typename std::enable_if< std::is_pod<T>::value >::type* ;
*/

struct visit: public boost::static_visitor<>
{
    visit(  ): size(0) { }
    template<typename T /*, EnableIf<T> = nullptr */  >
    void operator()(  T& x) const
    {
        size += sizeof ( x ) ;
        //std::cout <<  sizeof ( x ) << ":" << x << '\n';
    }

    std::size_t get_size() const
    { return size ; }
    private:
        mutable std::size_t size ;

};

那么,

visit visito ;
std::for_each( vec.begin(), vec.end(), 
               boost::apply_visitor( visito) );

std::cout << visito.get_size() ;

编辑:删除评论以检查 POD 数据类型,仅由 sehe 评论,因为保存非 Pod 所需的字节数可能并不总是等于sizeof(T)

【讨论】:

  • 做出关键假设,即保存元素所需的字节数与sizeof(T) 相关。然而,这仅在有限的应用中有意义。
  • @sehe 是的,我意识到现在谢谢,否则我不知道该怎么做:(
  • 问题是,我们需要来自 OP 的更多信息。如果您刚刚插入 SFINAE 检查 podness,我会很高兴(或现在评论它:))
  • 您因为发布了正确的想法而获得了我的 +1。关于非 POD 类型的支持和注意事项,我在自己的回答中进行了详细说明(请注意,boost::variant 本身不是 POD)
  • 在这种特定情况下,它足以知道单个元素的大小。因为如果我解码我的二进制文件,我会走同样的路。我提取整个长度(我在二进制文件的第一个位置编写)并将其与以下元素进行比较,如果二进制文件为空则停止。
【解决方案2】:

除了其他答案,您还可以

  • 让访问者直接返回size_t
  • 使用更通用的访问者,对输出迭代器进行实际序列化:

    struct serializer : public boost::static_visitor<> {
        template<typename T, typename Out>
            void operator()(T const& x, Out& out) const {
            static_assert(boost::is_pod<T>(), "");
            char const* rawp = reinterpret_cast<char const*>(&x);
            std::copy(rawp, rawp+sizeof(T), out);
        }
    };
    

    现在,您可以创建一个 serialize 函数,该函数采用任何变体(实际上是可访问类型):

    template <typename Variant, typename Out>
    Out serialize(Variant const& v, Out into) 
    {
        boost::apply_visitor(boost::bind(serializer(), _1, boost::ref(into)), v);
        return into;
    }
    

    如果您不想序列化(还没有?)但只想知道大小,您可以传递函数输出迭代器而不是传统的输出迭代器:

    template <typename Variant>
    size_t serialized_size(Variant const& v) {
        size_t octets = 0;
        serialize(v, boost::make_function_output_iterator([&octets](char) { ++octets; }));
        return octets;
    }
    

Live On Coliru

#include <boost/array.hpp> // just as a sample

int main() {
    typedef boost::variant<int, double, boost::array<char, 42> > V;

    std::cout << "With int:      " << serialized_size(V(0)) << "\n";
    std::cout << "With double:   " << serialized_size(V(3.14)) << "\n";
    std::cout << "With array:    " << serialized_size(V(boost::array<char,42>())) << "\n";
}

打印

With int:      4
With double:   8
With array:    42

高级应用

为什么这么通用?那么上述内容也可以应用于非 POD 类型并支持您的用例(“我需要序列化它”);

请参见此处我序列化包含非 POD 和用户定义类型的变体向量:Live On Coliru

注意:

  • 要获得有用的应用程序,您需要以类似的方式实现反序列化
  • 为了使其对变体有用,您也希望(反)序列化类型鉴别器!

这些留给读者作为练习。

【讨论】:

  • 这和往常一样棒极了,每当有 boost/spirit 相关的帖子出现时,我都焦急地等待你的回答 :) 你是 boost 版主还是委员会成员?
  • @P0W 感谢您的客气话。我不是 :) 我只是在慢慢学习更多关于提升的知识,而且确实回答 SO 问题在这个过程中非常有用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-10
相关资源
最近更新 更多