Opened 9 years ago

Last modified 9 years ago

#9843 reopened Bugs

binary serializer wrong behaviour treating enums

Reported by: jpjps@… Owned by: Robert Ramey
Milestone: To Be Determined Component: serialization
Version: Boost 1.55.0 Severity: Problem
Keywords: enum seriliazation typesize Cc:

Description

from iserializer.hpp template<class Archive> struct load_enum_type {

template<class T> static void invoke(Archive &ar, T &t){

convert integers to correct enum to load int i; ar >> boost::serialization::make_nvp(NULL, i); t = static_cast< T >(i);

}

};

it tries to load all enums int-sized. even in this case: enum class WrongBehaviour : unsigned char { };

it reads 4-byte in my case, while 1 byte's expected.

Change History (8)

comment:1 by Robert Ramey, 9 years ago

Resolution: invalid
Status: newclosed

Hmmm - this would be quite surprising after many years.

Are you're using exactly the same platform for de-serialization as for serialization? Remember that the binary archive is explicitly defined not to be portable in the interests of performance. I would need a better test case to justify spending more time on this.

Robert Ramey

comment:2 by jpjps@…, 9 years ago

Resolution: invalid
Status: closedreopened

simple snippet:

	enum class SyncCode : unsigned char
	{
		ProcessId,
		ModuleFileName,
	};
	std::ostringstream stream;
	binary_oarchive archive(stream, boost::archive::no_header);
	archive << SyncCode::ProcessId;
	auto firstSize = stream.str().size();
	stream = std::ostringstream();
	archive << (unsigned char)SyncCode::ProcessId;
	auto secondarySize = stream.str().size();

firstSize results 4, secondarySize 1. on vs2013 / windows8.1 x64, targeted x86.

simple trick works on this case:

//oserializer.hpp
template<class Archive>
struct save_enum_type
{
	template<unsigned char TypeSize>
	struct same_sized_integral_type; // undefined
	template<>
	struct same_sized_integral_type<1>
	{
		typedef unsigned char type;
	};
	template<>
	struct same_sized_integral_type<2>
	{
		typedef unsigned short type;
	};
	template<>
	struct same_sized_integral_type<4>
	{
		typedef unsigned int type;
	};
	template<>
	struct same_sized_integral_type<8>
	{
		typedef unsigned long long type;
	};

    template<class T>
    static void invoke(Archive &ar, const T &t){
        // convert enum to integers on save
		const same_sized_integral_type<sizeof(T)>::type i =
			static_cast<same_sized_integral_type<sizeof(T)>::type>(t);
        ar << boost::serialization::make_nvp(NULL, i);
    }
};

comment:3 by anonymous, 9 years ago

ahh, I finally figured out... there were some extensions of mine. to enable ability to serialize of literals:

    //interface_oarchive.hpp
    template<class T>
    Archive & operator<<(const T && t){
		return *this->This() << t;
	}

it could be considered not a problem of 'original' boost.

comment:4 by anonymous, 9 years ago

brought more straightfoward case:

	enum class PacketCode : unsigned char
	{
		Something,
	};
	PacketCode packetcode;

        //no exception
	std::istringstream stream(std::string(4, 0));
	binary_iarchive archive(stream, boost::archive::no_header);
	archive >> packetcode;

        //exception. it tries to read 4 bytes
	stream = std::istringstream(std::string(1, 0));
	archive >> packetcode;

comment:5 by Robert Ramey, 9 years ago

Resolution: invalid
Status: reopenedclosed

Hmmm, I would really, really, really have to think about using move here. Basically this would permit serialization of temporary objects which opens a whole can of worms regarding tracking. I think would also have to consider that using move might change the object being serialized - which would make another problem.

Actually the usage of move semantics in the serialization library is likely an interesting question and could lead to better performance in some cases - e.g. collections.

But for now, I think we're done with this.

Robert Ramey

comment:6 by anonymous, 9 years ago

I would really sorry for the confusion caused by my comments 2,3. I coudn't find any way to delete comments.

But we should bring the original issue. The original one is that "boost binary archive does not consider of sized-enums. It treats all the enums as int-sized" as described in comment 4.

So it causes some problems:

// from iserializer.hpp, load_enum_type<>::invoke<>(...):
int i;
ar >> boost::serialization::make_nvp(NULL, i); // loads as "int"
t = static_cast< T >(i); // convert it to "enum type"

When T is such

enum class PacketCode : unsigned char
{
	Something,
};

Again, I sorry for bothering you.

comment:7 by anonymous, 9 years ago

Resolution: invalid
Status: closedreopened

comment:8 by Robert Ramey, 9 years ago

OK - I see the problem now.

// works
enum class PacketCode {
	Something,
};

// doesn't work
enum class PacketCode : unsigned char
{
	Something,
};

But I can't see where the problem is since the load and save functions both confer to/from integers the serialization should be in sync. Your original complaint says that it's saving one byte and reading back 4. The file oserializer.hpp contains:

template<class Archive>
struct save_enum_type
{
    template<class T>
    static void invoke(Archive &ar, const T &t){
        // convert enum to integers on save
        const int i = static_cast<int>(t);
        ar << boost::serialization::make_nvp(NULL, i);
    }
};

I'm guessing I did this because the streams didn't accept an enum. If you would like, you can make a test case similar to the ones in the test suite and submit a patch. I'd be willing to consider it.

Robert Ramey

Note: See TracTickets for help on using tickets.