Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to design a serializable class such that any non-serialized attribute leads to a compile-time error?

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.

like image 303
koral Avatar asked Dec 26 '16 14:12

koral


People also ask

How do you prevent a variable from being serialized in a serializable class?

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.

What is non serialized attribute in serialization?

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.

What happens if your serializable class contains a member which is not serializable?

It'll throw a NotSerializableException when you try to Serialize it. To avoid that, make that field a transient field. Save this answer.

How do you not allow serialization of attributes of a class in Java?

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.


2 Answers

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]

like image 129
W.F. Avatar answered Oct 30 '22 23:10

W.F.


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{});
}
like image 41
skypjack Avatar answered Oct 30 '22 22:10

skypjack