2016-12-26 15 views
6

Giả sử bạn có đoạn mã sau:Làm thế nào để thiết kế một lớp serializable sao cho bất kỳ thuộc tính không được tuần tự hóa nào dẫn đến lỗi biên dịch?

class A { 
    bool _attribute1; 
}; 

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

Bây giờ một nhà phát triển cho biết thêm một mới bool _attribute2 để class A và quên để cập nhật các serialize chức năng, dẫn đến một lỗi khi chạy. (Đã có?)

Có cách nào để biến vấn đề này thành lỗi biên dịch không? Vì C++ không hỗ trợ sự phản chiếu, tôi có cảm giác điều này là không thể, nhưng tôi có thể thiếu cái gì đó.

+0

Tôi không nghĩ là có thể. –

+1

Điều duy nhất tôi có thể nghĩ đến sẽ là một khẳng định tĩnh ở đâu đó trên sizeof (A). Do đó, nếu một cái gì đó được thêm vào, một cái gì đó sẽ không biên dịch, cho đến khi thành viên lớp mới được tuần tự hóa và khẳng định tĩnh điều chỉnh cho phù hợp. –

+0

Bạn có thể thêm tập lệnh hoặc chương trình tùy chỉnh vào các bước tạo trước hoặc các bước tạo dựng IDE để kiểm tra xem tất cả các thành viên của lớp có được bao gồm trong hàm hay không. – wally

Trả lời

2

Nếu bạn đang sử dụng C++ 1Z bạn có thể tận dụng cấu trúc ràng buộc:

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

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

[live demo]

+0

Đẹp. Bạn có đề xuất chức năng tuần tự hóa sau đó sử dụng 'x' không? – wally

+0

@Muscampester Miễn là bạn có kế hoạch tuần tự hóa các trường công khai của lớp, thì tương đối dễ thực hiện –

+0

Tôi thấy điều này cũng sẽ hoạt động với 'tự động &'. Điều gì có thể được thực hiện về các thành viên tư nhân? – wally

2

Sau đây ta nên làm việc với C++ 11.
một chút khéo léo thực sự, nó dựa trên một lời nhận xét của @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{}); 
} 

Ý tưởng cơ bản là để liệt kê tất cả các loại của các thành viên dữ liệu và định nghĩa một kiểu mới từ chúng với một mixin. Sau đó, đặt một số static_assert vào đúng vị trí.
Lưu ý rằng các thành viên dữ liệu cá nhân cũng được xem xét.

Có một số trường hợp góc có thể phá vỡ nó, nhưng có thể nó có thể hoạt động cho mã thực của bạn.


Là một mặt lưu ý, nó có thể được đơn giản hóa hơn nữa nếu C++ 14 là một lựa chọn:

#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

Nếu bạn đang cam chịu để sử dụng C++ 11 và vẫn còn bạn quan tâm đến serializing chỉ các lĩnh vực công cộng bạn có thể tạo thử nghiệm đặc điểm nếu loại có thể được xây dựng bằng danh sách khởi tạo với một loại thông số đưa ra nhưng thậm chí không một nhiều (của bất kỳ loại):

#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]

Các vấn đề liên quan