2016-12-26 19 views
6

Powiedzmy, że masz następujący kod:Jak zaprojektować klasę serializowalną, tak aby każdy nieserializowany atrybut powodował błąd podczas kompilacji?

class A { 
    bool _attribute1; 
}; 

// Arbitrarily using std::string, not the point of this question 
std::string serialize(const A&); 

Teraz deweloper dodaje nowy bool _attribute2 do class A i zapomina o aktualizacji funkcji serialize, co prowadzi do błędów w czasie wykonywania. (Już tam było?)

Czy istnieje sposób, aby włączyć ten problem do błędu podczas kompilacji? Ponieważ C++ nie obsługuje refleksji, mam wrażenie, że to niemożliwe, ale może czegoś brakuje.

+0

Nie sądzę, że to możliwe. –

+1

Jedyne, co mogłem wymyślić, to statyczne stwierdzenie gdzieś na sizeof (A). W związku z tym, jeśli coś zostanie dodane, coś nie uda się skompilować, dopóki nowy członek klasy nie zostanie zserializowany, a asercja statyczna zostanie odpowiednio dostosowana. –

+0

Możesz dodać niestandardowy skrypt lub program do swoich makefile lub wstępnych kroków IDE, aby sprawdzić, czy wszyscy członkowie klasy są włączeni do tej funkcji. – wally

Odpowiedz

2

Jeśli używasz C++ 1z można wykorzystać strukturę wiązania:

struct S { 
    bool b; 
    //bool c; // causes error 
}; 

int main() { 
    S s; 
    auto [x] = s; 
    (void)x; 
} 

[live demo]

+0

Nice. Czy poleciłbyś, aby funkcja serializacji używała 'x'? – wally

+0

@Muscampester Tak długo, jak planujesz serializować tylko publiczne pola klasy, jest stosunkowo łatwo wykonać –

+0

Widzę, że to również działa z 'auto &'. Co można zrobić z członkami prywatnymi? – wally

2

Poniżej jeden powinien pracować z C++ 11.
trochę skomplikowane rzeczywiście, jest ona oparta na komentarzu @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{}); 
} 

Podstawową ideą jest, aby wymienić wszystkie rodzaje członków danych i zdefiniować nowy typ z nich z wstawek. To kwestia umieszczenia static_assert we właściwym miejscu.
Należy pamiętać, że prywatni członkowie danych są również brani pod uwagę.

Istnieje kilka przypadków narożnych, które mogą go złamać, ale może to zadziałać w przypadku rzeczywistego kodu.


Na marginesie, to może być dalej uproszczone, jeśli C++ 14 jest opcją:

#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{}); 
} 
0

Jeśli jesteś skazany użyć C++ 11 i nadal jesteś zainteresowany szeregowania tylko pola publiczne można tworzyć testy cecha jeśli typ może być skonstruowany przy użyciu listy inicjalizacji z podanych typów parametrów ale nawet jeden (dowolnego typu):

#include <type_traits> 

struct default_param { 
    template <class T> 
    operator T(); 
}; 

template <class T, class...> 
using typer = T; 

template <class, class, class... Args> 
struct cannot_one_more: std::true_type {}; 

template <class Tested, class... Args> 
struct cannot_one_more<typer<void, decltype(Tested{std::declval<Args>()..., default_param{}})>, Tested, Args...>: std::false_type { 
}; 

template <class...> 
struct is_list_constructable: std::false_type {}; 

template <class Tested, class... Args> 
struct is_list_constructable<Tested(Args...)>: is_list_constructable<void, Tested, Args...> { }; 

template <class Tested, class... Args> 
struct is_list_constructable<typer<void, decltype(Tested{std::declval<Args>()...}), typename std::enable_if<cannot_one_more<void, Tested, Args...>::value>::type>, Tested, Args...>: std::true_type { }; 

struct S { 
    bool b; 
    //bool c; // causes error 
}; 

int main() { 
    static_assert(is_list_constructable<S(bool)>::value, "!"); 
} 

[live demo]

Powiązane problemy