2016-05-18 17 views
5

CẬP NHẬT: 6 tháng sau, và tôi vừa xem câu trả lời này: Is it legal to index into a struct?: Answer by Slava. Tôi nghĩ rằng đây là một giải pháp tốt hơn MUCH so với bất kỳ giải pháp nào được cung cấp ở đây, vì hoàn toàn không có hành vi không xác định. Hy vọng rằng điều này sẽ giúp người tiếp theo, vì nó đã quá muộn để tôi thực hiện.Lặp lại qua cấu trúc theo tên biến


Trước khi bạn bình luận nói cho tôi để sử dụng một mảng hoặc vector, hoặc bất kỳ hình thức của container thay vào đó, nó là một sự thật khó mà tôi không thể. Tôi biết, điều này sẽ được giải quyết với một mảng, và bất kỳ giải pháp khác là khá "hacky". Tôi rất thích sử dụng một container, nhưng tôi hoàn toàn không thể.

Tôi là nhà phát triển cấp trung bình tại một công ty rất lớn và chúng tôi đang sử dụng thư viện toàn công ty để gửi dữ liệu qua ethernet. Có nhiều lý do tại sao nó không thể hỗ trợ mảng/vectơ, và thay vào đó, sử dụng cấu trúc của POD (Plain Old Data - ký tự, float, ints, bools). Tôi bắt đầu với một mảng float mà tôi phải sử dụng để điền vào một cấu trúc với cùng số lượng float. Vì mục đích của thư viện này là gửi tin nhắn qua ethernet, tôi chỉ cần thực hiện lặp lại hai lần - một lần trên thư gửi và một khi nhận. Tất cả các lần khác, dữ liệu này được lưu trữ dưới dạng một mảng. Tôi biết - tôi nên tuần tự hóa các mảng và gửi chúng như là, nhưng tôi lặp lại - tôi hoàn toàn không thể.

Tôi có một float[1024], và phải lặp qua mảng và điền vào các cấu trúc sau:

struct pseudovector 
{ 
    float data1; 
    float data2; 
    float data3; 
    ... 
    float data1024; 
} 

Tôi đã tạo struct này với BOOST_PP_REPEATBOOST_PP_SEQ_FOR_EACH_I vì vậy mà tôi không cần phải viết ra tất cả 1024 nổi và tăng khả năng bảo trì/khả năng mở rộng. Trong cùng một thời trang, tôi đã cố gắng lặp qua cấu trúc thông qua pre-compiler ## concatination (https://stackoverflow.com/a/29020943/2066079), nhưng vì điều này được thực hiện tại thời gian trước khi biên dịch, nó không thể được sử dụng cho thời gian chạy nhận/thiết lập.

Tôi đã xem xét việc thực hiện phản chiếu như How can I add reflection to a C++ application?Ponder Library, nhưng cả hai cách tiếp cận yêu cầu bạn phải ghi rõ từng mục có thể được phản ánh. Trong trường hợp đó, tôi cũng có thể chỉ cần tạo một std::map<string, float> và lặp trong vòng lặp for qua chuỗi/integer nối:

for(i=0;i<1024;i++) 
{ 
    array[i] = map.get(std::string("data")+(i+1)) 
} 

bất cứ ai có thể giới thiệu một giải pháp sạch hơn mà không yêu cầu tôi viết ra vượt quá 1024 dòng mã? Trợ giúp của bạn được đánh giá cao!

Một lần nữa, tôi lặp lại - Tôi hoàn toàn không thể sử dụng mảng/vectơ thuộc bất kỳ loại nào.

+0

điều này có vẻ như chống mẫu. bạn có thể bọc cấu trúc này trong một công đoàn? 'union wrapper {float arr [1024]; pseudovector vec; }; ' – vu1p3n0x

+3

Tại sao không chỉ ' float [1024] fa; pseudovector pv; memcpy (fa, & pv, sizeof (pv)); '? –

+0

@ c-smile Bởi vì đây là hành vi không xác định – dberm22

Trả lời

8

Điều này có thể dễ dàng hơn bạn mong đợi. Trước tiên, một số lưu ý:

  1. Mảng được đảm bảo theo tiêu chuẩn để tiếp giáp; nghĩa là, không có đệm được chèn vào giữa chúng và bản thân mảng được căn chỉnh với các yêu cầu căn chỉnh của loại phần tử.

  2. Cốt truyện không có giới hạn nào như vậy; chúng có thể chịu sự đệm tùy ý. Tuy nhiên, một triển khai cụ thể (tại một phiên bản nhất định) sẽ thực hiện tương tự như vậy trong tất cả các đơn vị dịch (nếu không thì định nghĩa cấu trúc tương tự sẽ được sử dụng để truyền dữ liệu qua các đơn vị dịch như thế nào?).Cách thông thường này được thực hiện khá lành mạnh, đặc biệt là khi cấu trúc chứa chỉ thành viên của một loại duy nhất. Đối với cấu trúc như vậy, căn chỉnh thường là khớp với căn chỉnh lớn nhất của các thành viên và có thường là không có đệm bởi vì tất cả các thành viên đều có cùng một căn chỉnh.

Trong trường hợp của bạn, mảng của bạn 1024 phao nổi và struct của bạn với 1024 thành viên float gần như chắc chắn có chính xác bố trí bộ nhớ tương tự. Đây là hoàn toàn không được đảm bảo theo tiêu chuẩn, nhưng trình biên dịch của bạn có thể ghi lại quy tắc bố cục cấu trúc và bạn luôn có thể xác nhận kích thước và sắp xếp khớp trong các bài kiểm tra đơn vị của mình (bạn có bài kiểm tra đơn vị, đúng không?)

những cảnh báo đó, bạn sẽ gần như chắc chắn có thể chỉ đơn giản là reinterpret_cast (hoặc memcpy) giữa hai thiết bị.

2

Bạn có thể sử dụng loại punning để xử lý cấu trúc dưới dạng mảng.

float array[1024] = { ... }; 
pseudovector pv1; 
float *f = static_cast<float*>(static_cast<void*>(&pv1)); 
for (int i = 0; i < 1024; i++) { 
    f[i] = array[i]; 
} 
+0

Miễn là bạn đang OK với hành vi không xác định. –

+0

Vì anh ấy gửi cấu trúc qua Ethernet, anh ấy rõ ràng là OK với điều đó. Mã để viết nó gần như chắc chắn làm cho cùng một giả định của dữ liệu tiếp giáp. – Barmar

+0

Rất tốt, cảm ơn! Và điều này làm việc bởi vì tất cả các mục trong cấu trúc là nổi, phải không? Nếu có các loại hỗn hợp (tức là thông tin tiêu đề trong mỗi cấu trúc) thì điều này không thể được sử dụng? – dberm22

1

Bạn có thể sử dụng lập trình meta lập trình trước để tạo mảng của mình. Bạn có thể làm điều gì đó như:

#define ACCESSOR(z, n, type) &type::data ## n 

auto arr[] = { 
    BOOST_PP_ENUM(1000, ACCESSOR, pseudovector) 
}; 

Cần điều chỉnh ACCESSOR nhiều khả năng. Có thể không hợp pháp khi sử dụng auto tại đây.

Sau đó, bạn làm:

auto val = (pv1.*arr)[4]; 

vv ...

Không UB cần thiết.

1

Sử dụng Boost.Hana, nếu bạn có thể biên dịch với các phiên bản gần đây của Clang hoặc GCC (afaik, các trình biên dịch chỉ được hỗ trợ.)

Cho phép tôi trích dẫn phần Introspection từ hướng dẫn:

mẫn tĩnh , như chúng ta sẽ thảo luận ở đây, là khả năng của một chương trình để kiểm tra kiểu của một đối tượng tại thời gian biên dịch. Trong các từ khác , đó là giao diện có lập trình để tương tác với các loại tại thời gian biên dịch. Ví dụ: bạn có bao giờ muốn kiểm tra xem một số loại không xác định nào có thành viên có tên là foo không? Hoặc có lẽ tại một số điểm bạn có cần thiết để lặp lại các thành viên của một cấu trúc?

Đối introspecting user-defined types bạn phải xác định các cấu trúc với Hana, nhưng điều này không có nhiều khác biệt so với quy định một cấu trúc khác:

struct pseudovector { 
    BOOST_HANA_DEFINE_STRUCT(pseudovector, 
    (float, data1), 
    (float, data2), 
    … 
); 
}; 

Và bạn nên dễ dàng có thể sửa đổi bất cứ điều gì macro bạn phải tạo cấu trúc hiện tại của bạn, để tạo ra cấu trúc này thay thế.

Điều này thêm cấu trúc lồng nhau vào pseudovector, chỉ bao gồm một hàm thành viên tĩnh. Nó không ảnh hưởng đến POD-ness, kích thước, hoặc bố trí dữ liệu.

Sau đó, bạn có thể lặp qua nó như trong ví dụ này:

pseudovector pv; 

hana::for_each(pv, [](auto pair) { 
    std::cout << hana::to<char const*>(hana::first(pair)) << ": " 
      << hana::second(pair) << std::endl; 
}); 

Ở đây, các thành viên pair 's là một chuỗi hana thời gian biên dịch (tên thành viên) và giá trị. Nếu bạn muốn có lambda của bạn có hai đối số (tên và giá trị), sử dụng hana::fuse:

hana::for_each(pv, hana::fuse([](auto name, auto member) { 
    std::cout << hana::to<char const*>(name) << ": " << member << std::endl; 
})); 
+0

@Andrew: done. FWIW, at thời gian tôi đã suy nghĩ về việc chứng minh rằng Hana làm những gì mong muốn, bằng cách trích dẫn các tài liệu – Kundor

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