c++boostboost-serializationbinary-serialization

writing struct to .txt file


I'm trying to store my struct into txt file using boost but unable to do it. I'm using boost library. example structure

struct Frame
{
    uint32_t address{ 0 };
    uint16_t marks{ 0 };
    uint16_t age{ 0 };
    char gender{ 'M' };
    std::string userName;
};

for binary there is simple code

boost::archive::binary_oarchive ar(ofs, boost::archive::no_header);
ar << boost::serialization::make_binary_object(&f, sizeof(Frame));

assume file is open with fstream object ofs and 'f' is object of 'Frame'

I want to know if there is similar way to write structure to txt file and I do not want to write data types one by one. assume we don't know the types/number of datatypes inside structure.


Solution

  • As others have commented you will have to provide serialization helpers that tell Boost how to member-wise serialize.

    If you have only aggregates like this, you can automate the generation of this function to a degree with Boost PFR:

    pfr::for_each_field(s, [&](auto&& f) { ar & f; });               
    

    Here's an example:

    namespace MyLib {
        struct Frame {
            uint32_t    address{0};
            uint16_t    marks{0};
            uint16_t    age{0};
            char        gender{'M'};
            std::string userName;
        };
    
        struct Other {
            std::string userName;
            std::map<uint32_t, std::string> properties;
        };
    
    } // namespace MyLib
    

    Note I stick them in a namespace for good style and also so we can highlight that ADL is used to find a serialize overload. Now let's define the overloads:

    namespace MyLib {
        #define SERIALIZER(Aggregate)                                                  \
            template <typename Archive>                                                \
            void serialize(Archive& ar, Aggregate& s, unsigned version)                \
            {                                                                          \
                pfr::for_each_field(s, [&](auto&& f) { ar & f; });               \
            }
    
    
        SERIALIZER(Frame)
        SERIALIZER(Other)
    } // namespace MyLib
    

    Using the macro we avoid repeated code. Of course you could do this without a macro as well.

    Now you can serialize both. Let's say we have:

    MyLib::Frame const diablo{178, 42, 37, 'F', "Diablo"};
    MyLib::Other const other{"diablo", {{1, "one"}, {2, "two"}, {3, "three"},}};
    

    Then serializing to a text stream:

    boost::archive::text_oarchive oa(ss);
    oa & diablo;
    oa & other;
    

    Already results in the stream containing e.g.

    22 serialization::archive 19 0 0 178 42 37 70 6 Diablo 0 0 6 diablo 0 0 3 0 0
     0 1 3 one 2 3 two 3 5 three
    

    Full Demo

    This demo checks that the result of deserializing is actually identical to the original structs:

    Live On Coliru

    #include <string>
    #include <map>
    
    namespace MyLib {
        struct Frame {
            uint32_t    address{0};
            uint16_t    marks{0};
            uint16_t    age{0};
            char        gender{'M'};
            std::string userName;
        };
    
        struct Other {
            std::string userName;
            std::map<uint32_t, std::string> properties;
        };
    
    } // namespace MyLib
    
    #include <boost/pfr.hpp>
    namespace pfr = boost::pfr;
    
    namespace MyLib {
    #define SERIALIZER(Aggregate)                                                  \
        template <typename Archive>                                                \
        void serialize(Archive& ar, Aggregate& s, unsigned version)                \
        {                                                                          \
            pfr::for_each_field(s, [&](auto&& f) { ar & f; });               \
        }
    
        SERIALIZER(Frame)
        SERIALIZER(Other)
    } // namespace MyLib
    
    #include <iostream>
    #include <boost/archive/binary_oarchive.hpp>
    #include <boost/archive/binary_iarchive.hpp>
    #include <boost/archive/text_oarchive.hpp>
    #include <boost/archive/text_iarchive.hpp>
    #include <boost/serialization/map.hpp>
    
    int main() {
        MyLib::Frame const diablo{178, 42, 37, 'F', "Diablo"};
        MyLib::Other const other{"diablo", {{1, "one"}, {2, "two"}, {3, "three"},}};
    
        std::stringstream ss;
        {
            boost::archive::text_oarchive oa(ss);
            oa << diablo;
            oa << other;
        }
    
        std::cout << ss.str() << "\n";
    
        {
            boost::archive::text_iarchive ia(ss);
            MyLib::Frame f;
            MyLib::Other o;
    
            ia >> f >> o;
    
            std::cout << std::boolalpha;
            std::cout << "Frame identical: " << pfr::eq(diablo, f) << "\n";
            std::cout << "Other identical: " << pfr::eq(other, o) << "\n";
        }
    }
    

    Prints

    g++ -std=c++2a -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization && ./a.out
    22 serialization::archive 19 0 0 178 42 37 70 6 Diablo 0 0 6 diablo 0 0 3 0 0 0 1 3 one 2 3 two 3 5 three
    
    Frame identical: true
    Other identical: true