Ventu
Ventu

Reputation: 800

C++: Extract size of boost::variant element

I have a vector, which contains structs with boost::variant elements in it.

Now i have to serialize this vector. Because of the specification i have to count the octets, which are needed to save this vector. Now I'm searching for a option to realize this in a easy way.

int allSize = 0;

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

I tried to get the size of the elements with

sizeof(it->variant.type())

but this shows only the size of the variant element (which is the size of the biggest element held from te variant)

So, is there an easy way to get the size of the serialized data? Or do i have to write a visitor with about 7 templates?

Upvotes: 2

Views: 1558

Answers (2)

sehe
sehe

Reputation: 393613

In addition to the other answer, you could

  • make the visitor return the size_t directly
  • use a slightly more generic visitor that does the actual serialization to an output iterator:

    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);
        }
    };
    

    Now, you can make a serialize function that takes any variant (or Visitable type, actually):

    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;
    }
    

    If you don't want to serialize (yet?) but just want to know the size, you can pass a function output iterator instead of a traditional output iterator:

    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";
}

Prints

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

Advanced application

Why so generic? Well the above can be made to apply to non-POD types as well and supports your use case ("I need to serialize it");

See here where I serialize a vector of variants that contain non-POD and user-defined types: Live On Coliru

Note:

  • to get a useful application, you'll want to implement deserialization in a similar fashion
  • to make it useful with variants, you will want to (de)serialize the type-discriminator too!

These are left as an exercise to the reader.

Upvotes: 1

P0W
P0W

Reputation: 47824

You can do that in the visitor, something like following :

/*
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 ;

};

Then,

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

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

Edit: Remove comment to check for POD data type only as commented by sehe, since amount of bytes needed to save a non-pod might not always be equal to sizeof(T)

Upvotes: 3

Related Questions