Opened 9 years ago
Last modified 9 years ago
#9843 reopened Bugs
binary serializer wrong behaviour treating enums
Reported by: | 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 , 9 years ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
comment:2 by , 9 years ago
Resolution: | invalid |
---|---|
Status: | closed → reopened |
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 , 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 , 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 , 9 years ago
Resolution: | → invalid |
---|---|
Status: | reopened → closed |
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 , 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 , 9 years ago
Resolution: | invalid |
---|---|
Status: | closed → reopened |
comment:8 by , 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
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