2

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.

9
  • 1
    Since your Frame isn't trivially copyable, it must be written with multiple writes to the stream. If you don't want to do it yourself, you can use a library like the one you use now. By "similar", do you mean another library or what do you mean exactly? Commented Aug 23, 2021 at 8:41
  • I mean in this same boost library if there is similar function for text storage just like binary serialization. Commented Aug 23, 2021 at 8:47
  • @Ted in above example what I'm doing is using make_binary_object and then serializing it. I want to know if there is similar function for text also. Commented Aug 23, 2021 at 8:55
  • Did you read the documentation on archives? Is there any reason not to use text_oarchive? However, note that writing std::string as a sequence of bytes is totally meaningless. Commented Aug 23, 2021 at 8:56
  • @Evg I'm a beginner and I'm using text_oarchive but after that I don't know what to use for text to write it into text file because boost::serialization::make_binary_object won't work as it is for binary. I want to write whole structure at once similar to what I'm doing it with boost::serialization::make_binary_object, is there any similar function for text also? I want to know that because I'm unable to find it. Commented Aug 23, 2021 at 9:37

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

3 Comments

Hi I tried doing this. Idk why my boost lib do not have pfr.hpp, do I have to add it separately to boost, if so then help me with that. And also this Aggregate& s is giving error that it is not defined.
PFR was introduced in Boost 1.75.0. You can do it without, of course: coliru.stacked-crooked.com/a/b5a39df8f2dfd2f3, you can use your own bespoke macro: coliru.stacked-crooked.com/a/cbda0edbdf6073e3 but I'd just keep the manual version in this case
(PS that second macro wizardry generates coliru.stacked-crooked.com/a/114505b9be18306f effectively)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.