Say you have the following code:
class A {
bool _attribute1;
};
// Arbitrarily using std::string, not the point of this question
std::string serialize(const A&);
Now a developer adds a new bool _attribute2
to class A
and forgets to update the serialize
function, which leads to a bug at runtime. (Already been there ?)
Is there a way to turn this issue into compile-time error ? As C++ doesn't support reflection, I have the feeling this is impossible, but I may be missing something.
You can prevent member variables from being serialized by marking them with the NonSerialized attribute as follows. If possible, make an object that could contain security-sensitive data nonserializable. If the object must be serialized, apply the NonSerialized attribute to specific fields that store sensitive data.
When using the BinaryFormatter or SoapFormatter classes to serialize an object, use the NonSerializedAttribute attribute to prevent a field from being serialized. For example, you can use this attribute to prevent the serialization of sensitive data.
It'll throw a NotSerializableException when you try to Serialize it. To avoid that, make that field a transient field. Save this answer.
In order to prevent subclass from serialization we need to implement writeObject() and readObject() methods which are executed by JVM during serialization and deserialization also NotSerializableException is made to be thrown from these methods.
If you are using c++1z you could make use of structured binding:
struct S {
bool b;
//bool c; // causes error
};
int main() {
S s;
auto [x] = s;
(void)x;
}
[live demo]
The following one should work with C++11.
A bit tricky indeed, it's based on a comment of @SamVarshavchik:
#include<cstddef>
#include<functional>
template<std::size_t> struct Int { int i; };
template<std::size_t> struct Char { char c; };
template<std::size_t> struct Bool { bool c; };
template<typename, template<std::size_t> class...>
struct Base;
template<template<std::size_t> class... T, std::size_t... I>
struct Base<std::index_sequence<I...>, T...>: T<I>... {};
template<template<std::size_t> class... T>
struct Check final: Base<std::make_index_sequence<sizeof...(T)>, T...> {};
class A final {
bool _attribute1;
bool _attribute2;
private:
char _attribute3;
// int _attribute4;
};
void serialize(const A &) {
static_assert(sizeof(A) == sizeof(Check<Bool, Bool, Char>), "!");
// do whatever you want here...
}
int main() {
serialize(A{});
}
The basic idea is to list all the types of the data members and define a new type from them with a mixin. Then it's a matter of putting a static_assert
in the right place.
Note that private data members are taken in consideration too.
There exist some corner cases that could break it, but maybe it can work for your real code.
As a side note, it can be further simplified if C++14 is an option:
#include<cstddef>
template<typename... T>
constexpr std::size_t size() {
std::size_t s = 0;
std::size_t _[] = { s += sizeof(T)... };
(void)_;
return s;
}
class A final {
bool _attribute1;
bool _attribute2;
private:
char _attribute3;
// int _attribute4;
};
void serialize(const A &) {
static_assert(sizeof(A) == size<bool, bool, char>(), "!");
// ...
}
int main() {
serialize(A{});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With