2

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?

Ventu
  • 780
  • 1
  • 12
  • 25
  • What will be the variant member types? How will you serialize (always bitwise copy of the element data as POD?) – sehe Nov 19 '14 at 09:21
  • Yes, for example: A variant member is a struct wich has a time64 value and a uint8 value. So i serialize a uint8 and a uint64 (which is the time64 value). I have to do this, because the time64 has no serilize methode. – Ventu Nov 19 '14 at 10:18

2 Answers2

3

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)

Community
  • 1
  • 1
P0W
  • 46,614
  • 9
  • 72
  • 119
  • 1
    making the crucial assumption that somehow the amount of bytes needed to save the elements is related to `sizeof(T)`. This, however, makes sense only in limited applications. – sehe Nov 19 '14 at 09:20
  • @sehe yeah I realized that now thanks, I don't know how to do it otherwise :( – P0W Nov 19 '14 at 09:23
  • The thing is, we need more information from the OP. I'd be happy if you just inserted a SFINAE check for podness (or comment it for now :)) – sehe Nov 19 '14 at 09:25
  • you get my +1 for posting the right idea. I've elaborated a bit in my own answer, regarding non-POD type support and caveats (note that `boost::variant` itself is not POD) – sehe Nov 19 '14 at 10:48
  • In this specific case its enough to know the size of the single elements. Because if i decode my binary, I'm going the same way. I extract the whole lenght(which i have written at the first place of my binary) and compare it with the following elements, to stop if the binary is empty. – Ventu Nov 20 '14 at 09:41
1

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.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
sehe
  • 374,641
  • 47
  • 450
  • 633
  • This is amazing as usual, whenever a boost/spirit related post comes, I anxiously wait for your answer :) Are you a boost moderator or committee member ? – P0W Nov 19 '14 at 11:54
  • 1
    @P0W Thanks for the kind words. I'm not :) I'm just slowly learning more of boost, and indeed answering SO questions is highly instrumental in that process – sehe Nov 19 '14 at 12:49