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