2010-07-01 20 views
6

Tôi không tìm thấy bất kỳ điều gì có liên quan trực tiếp đến tìm kiếm, vì vậy vui lòng tha thứ nếu đây là bản sao.serialize bất kỳ loại dữ liệu nào như vector <uint8_t> - sử dụng reinterpret_cast?

Điều tôi muốn làm là sắp xếp từng dữ liệu qua kết nối mạng. Cách tiếp cận của tôi là chuyển đổi mọi thứ tôi cần để chuyển sang một số std::vector<uint8_t> và bên nhận nhận giải nén dữ liệu vào các biến thích hợp. Biện pháp của tôi trông như thế này:

template <typename T> 
inline void pack (std::vector<uint8_t>& dst, T& data) { 
    uint8_t * src = static_cast < uint8_t* >(static_cast < void * >(&data)); 
    dst.insert (dst.end(), src, src + sizeof (T)); 
} 

template <typename T> 
inline void unpack (vector <uint8_t >& src, int index, T& data) { 
    copy (&src[index], &src[index + sizeof (T)], &data); 
} 

Mà tôi đang sử dụng như

vector<uint8_t> buffer; 
uint32_t foo = 103, bar = 443; 
pack (buff, foo); 
pack (buff, bar); 

// And on the receive side 
uint32_t a = 0, b = 0; 
size_t offset = 0; 
unpack (buffer, offset, a); 
offset += sizeof (a); 
unpack (buffer, offset, b); 

mối quan tâm của tôi là

uint8_t * src = static_cast < uint8_t* >(static_cast < void * >(&data));

dòng

(mà tôi hiểu để làm tương tự như reinterpret_cast). Có cách nào tốt hơn để thực hiện điều này mà không có dàn diễn viên đôi?

Cách tiếp cận ngây thơ của tôi là chỉ sử dụng static_cast< uint8_t* >(&data) không thành công. Tôi đã been told in the past rằng reinterpret_cast là xấu. Vì vậy, tôi muốn tránh nó (hoặc cấu trúc tôi hiện có) nếu có thể.

Tất nhiên, luôn có uint8_t * src = (uint8_t *)(&data).

Đề xuất?

Trả lời

16

Đề xuất của tôi là bỏ qua tất cả những người nói với bạn rằng reinterpret_cast là xấu. Họ nói với bạn nó là xấu, bởi vì nó thường không phải là một thực hành tốt để lấy bản đồ bộ nhớ của một loại và giả vờ rằng đó là một loại. Nhưng trong trường hợp này, đó chính xác là những gì bạn muốn làm, vì toàn bộ mục đích của bạn là truyền bản đồ bộ nhớ thành một chuỗi các byte.

Điều này tốt hơn nhiều so với việc sử dụng một đôi- static_cast, vì nó hoàn toàn chi tiết thực tế là bạn đang dùng một loại và cố ý giả vờ rằng đó là một cái gì đó khác. Tình trạng này là chính xác những gì reinterpret_cast là cho, và dodging sử dụng nó với một trung gian con trỏ void là chỉ đơn giản là che khuất nghĩa của bạn mà không có lợi ích.

Ngoài ra, tôi chắc chắn rằng bạn đang nhận thức được điều này, nhưng xem cho con trỏ trong T.

1

Bạn sẽ không làm bất kỳ thực tế mã hóa đây, bạn chỉ cần sao chép các đại diện của nguyên dữ liệu từ bộ nhớ vào một mảng byte và sau đó gửi ra trên mạng. Điều đó sẽ không hoạt động. Dưới đây là một ví dụ nhanh là tại sao:

struct A { 
    int a; 
}; 

struct B { 
    A* p_a; 
} 

gì xảy ra khi bạn sử dụng phương pháp của bạn để gửi một B ra qua mạng? Người nhận nhận được p_a, địa chỉ của một số đối tượng A trên máy của bạn, nhưng đối tượng đó không có trên máy của họ. Và ngay cả khi bạn gửi cho họ đối tượng A cũng vậy, nó sẽ không ở cùng một địa chỉ. Không có cách nào có thể hoạt động nếu bạn chỉ gửi cấu trúc thô B. Và điều đó thậm chí còn không xem xét các vấn đề tinh tế hơn như sự kết thúc và biểu diễn dấu phẩy động có thể ảnh hưởng đến việc truyền các loại đơn giản như intdouble.

Những gì bạn đang làm bây giờ về cơ bản không khác so với chỉ truyền tới uint8_t* cho dù nó có hoạt động hay không có liên quan (nó sẽ không hoạt động, trừ trường hợp tầm thường nhất).

Những gì bạn cần làm là đưa ra phương pháp serialization. Serialization có nghĩa là bất kỳ cách nào để giải quyết loại vấn đề này: làm thế nào để có được các đối tượng trong bộ nhớ ra vào mạng trong một hình thức sao cho chúng có thể được tái tạo có ý nghĩa ở phía bên kia. Đây là một vấn đề phức tạp, nhưng nó là một vấn đề nổi tiếng và được giải quyết nhiều lần. Dưới đây là một điểm khởi đầu tốt để đọc: http://www.parashift.com/c++-faq-lite/serialization.html

+0

Vì vậy, có, nhầm lẫn. Đối với phần còn lại của bình luận của bạn: câu hỏi, như đặt ra, là một đơn giản hóa để hỏi về việc có hay không 'reinterpret_cast' (hoặc tương tự) - Tôi sẽ đổi tên thành specfic hơn. Tôi nhận thức được sự tinh tế trong việc truyền dữ liệu và nội bộ mọi thứ đều có gói/giải nén mà về cơ bản tôi làm những gì tôi mô tả ở trên cho dữ liệu của chính nó. – ezpz

2

Bạn có thể loại bỏ một diễn viên bằng cách khai thác thực tế là bất kỳ con trỏ nào cũng có thể được truyền hoàn toàn đến void*. Ngoài ra, bạn có thể muốn thêm một vài const:

//Beware, brain-compiled code ahead! 
template <typename T> 
inline void encode (std::vector<uint8_t>& dst, const T& data) 
{ 
    const void* pdata = &data; 
    uint8_t* src = static_cast<uint8_t*>(pdata); 
    dst.insert(dst.end(), src, src + sizeof(T)); 
} 

Bạn có thể muốn thêm một tấm séc thời gian biên dịch cho T là một POD, không struct, và không có con trỏ.

Tuy nhiên, việc giải thích một số bộ nhớ của đối tượng ở cấp byte sẽ không bao giờ được lưu, thời gian. Nếu bạn phải làm điều đó, sau đó làm điều đó trong một wrapper tốt đẹp (như bạn đã làm), và vượt qua nó. Khi bạn chuyển sang một nền tảng/trình biên dịch khác, hãy chú ý đến những thứ này.

+0

Tôi có 'const' trong đó nhưng elided cho ngắn gọn. Tôi không, tuy nhiên, có kiểm tra cho con trỏ và/hoặc struct. Điều này chỉ được sử dụng bởi bản thân tôi, nhưng có lẽ sẽ an toàn nhất để thêm những kiểm tra đó để chắc chắn. Cảm ơn. – ezpz

6

Tình huống của bạn chính xác là những gì reinterpret_cast cho, nó đơn giản hơn một đôi static_cast và tài liệu rõ ràng những gì bạn đang làm.

Chỉ cần để được an toàn, bạn nên sử dụng unsigned char thay vì uint8_t:

  • làm reinterpret_cast-unsigned char * và sau đó dereferencing con trỏ kết quả là an toàn và di động và được cho phép rõ ràng [basic.lval] §3.10/10
  • làm reinterpret_cast-std::uint8_t * và sau đó dereferencing con trỏ kết quả là một sự vi phạm quy tắc nghiêm ngặt và răng cưa là hành vi không xác định nếu std::uint8_t được thực hiện như ext đã kết thúc loại số nguyên không dấu.

    Nếu nó tồn tại, uint8_t phải luôn có chiều rộng giống như unsigned char. Tuy nhiên, nó không phải là cùng loại; nó có thể là một loại số nguyên mở rộng khác biệt. Nó cũng không cần phải có cùng một biểu diễn như unsigned char (xem When is uint8_t ≠ unsigned char?).

    (Đây không phải là hoàn toàn giả thuyết: làm [u]int8_t một mở rộng loại nguyên đặc biệt cho phép một số tối ưu hóa mạnh mẽ)

Nếu bạn thực sự muốn uint8_t, bạn có thể thêm:

static_assert(std::is_same<std::uint8_t, unsigned char>::value, 
       "We require std::uint8_t to be implemented as unsigned char"); 

nên rằng mã sẽ không biên dịch trên nền tảng mà trên đó nó sẽ dẫn đến hành vi không xác định.

+0

+1 cho điều này là tốt hơn so với chuỗi 'static_cast' và đặc biệt là các cảnh báo về 'uint8_t'. Tôi đã đọc một bài đăng như thế này, thậm chí có thể giống như vậy, trong quá khứ - và nhanh chóng phải làm rất nhiều 's/uint8_t/unsigned char/g';) –

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