2010-09-16 27 views
5

Tôi đã viết sơ lược về một lớp để tạo cấu trúc động trong C++. Thành phần cấu trúc động được lưu trữ liên tục với (theo như các thử nghiệm của tôi cho biết) cùng một đệm mà trình biên dịch sẽ chèn vào trong cấu trúc tĩnh tương đương. Do đó, các cấu trúc động có thể được chuyển đổi hoàn toàn thành các cấu trúc tĩnh để tương thích với các API hiện có.Trên nền tảng nào sự cố này sẽ xảy ra và tôi có thể cải thiện nó như thế nào?

Quan trọng nhất, tôi không tin tưởng bản thân mình để có thể viết mã chất lượng tăng cường có thể biên dịch và hoạt động trên nhiều nền hoặc ít hơn bất kỳ nền tảng nào. Những phần nào của mã này có nguy cơ cần sửa đổi?

Tôi có một câu hỏi liên quan đến thiết kế khác: Là một cách truy cập đơn giản cung cấp trình biên dịch với thông tin kiểu tĩnh cần thiết cho mã loại an toàn? Vì vậy, người dùng của dynamic_struct phải chỉ định loại thành viên mà họ đang truy cập, bất cứ khi nào họ truy cập. Nếu loại đó thay đổi, tất cả các số của các truy cập trở nên không hợp lệ và sẽ gây ra các sự cố ngoạn mục — trở xuống, không thành công. Và nó không thể bị bắt lúc biên dịch. Đó là một rủi ro rất lớn, và tôi muốn khắc phục.

Ví dụ về sử dụng:

struct Test { 

    char a, b, c; 
    int i; 
    Foo object; 

}; 

void bar(const Test&); 

int main(int argc, char** argv) { 

    dynamic_struct<std::string> ds(sizeof(Test)); 

    ds.append<char>("a") = 'A'; 
    ds.append<char>("b") = '2'; 
    ds.append<char>("c") = 'D'; 
    ds.append<int>("i") = 123; 
    ds.append<Foo>("object"); 
    bar(ds); 

} 

Và mã sau:

// 
// dynamic_struct.h 
// 
// Much omitted for brevity. 
// 


/** 
* For any type, determines the alignment imposed by the compiler. 
*/ 
template<class T> 
class alignment_of { 
private: 

    struct alignment { 

     char a; 
     T b; 

    }; // struct alignment 

public: 

    enum { value = sizeof(alignment) - sizeof(T) }; 

}; // class alignment_of 


/** 
* A dynamically-created structure, whose fields are indexed by keys of 
* some type K, which can be substituted at runtime for any structure 
* with identical members and packing. 
*/ 
template<class K> 
class dynamic_struct { 
public: 


    // Default maximum structure size. 
    static const int DEFAULT_SIZE = 32; 


    /** 
    * Create a structure with normal inter-element padding. 
    */ 
    dynamic_struct(int size = DEFAULT_SIZE) : max(size) { 

     data.reserve(max); 

    } // dynamic_struct() 


    /** 
    * Copy structure from another structure with the same key type. 
    */ 
    dynamic_struct(const dynamic_struct& structure) : 
     members(structure.members), max(structure.max) { 

     data.reserve(max); 

     for (iterator i = members.begin(); i != members.end(); ++i) 
      i->second.copy(&data[0] + i->second.offset, 
       &structure.data[0] + i->second.offset); 

    } // dynamic_struct() 


    /** 
    * Destroy all members of the structure. 
    */ 
    ~dynamic_struct() { 

     for (iterator i = members.begin(); i != members.end(); ++i) 
      i->second.destroy(&data[0] + i->second.offset); 

    } // ~dynamic_struct() 


    /** 
    * Get a value from the structure by its key. 
    */ 
    template<class T> 
    T& get(const K& key) { 

     iterator i = members.find(key); 

     if (i == members.end()) { 

      std::ostringstream message; 
      message << "Read of nonexistent member \"" << key << "\"."; 
      throw dynamic_struct_access_error(message.str()); 

     } // if 

     return *reinterpret_cast<T*>(&data[0] + i->second.offset.offset); 

    } // get() 


    /** 
    * Append a member to the structure. 
    */ 
    template<class T> 
    T& append(const K& key, int alignment = alignment_of<T>::value) { 

     iterator i = members.find(key); 

     if (i != members.end()) { 

      std::ostringstream message; 
      message << "Add of already existing member \"" << key << "\"."; 
      throw dynamic_struct_access_error(message.str()); 

     } // if 

     const int modulus = data.size() % alignment; 
     const int delta = modulus == 0 ? 0 : sizeof(T) - modulus; 

     if (data.size() + delta + sizeof(T) > max) { 

      std::ostringstream message; 

      message << "Attempt to add " << delta + sizeof(T) 
       << " bytes to struct, exceeding maximum size of " 
       << max << "."; 

      throw dynamic_struct_size_error(message.str()); 

     } // if 

     data.resize(data.size() + delta + sizeof(T)); 

     new (static_cast<void*>(&data[0] + data.size() - sizeof(T))) T; 

     std::pair<iterator, bool> j = members.insert 
      ({key, member(data.size() - sizeof(T), destroy<T>, copy<T>)}); 

     if (j.second) { 

      return *reinterpret_cast<T*>(&data[0] + j.first->second.offset); 

     } else { 

      std::ostringstream message; 
      message << "Unable to add member \"" << key << "\"."; 
      throw dynamic_struct_access_error(message.str()); 

     } // if 

    } // append() 


    /** 
    * Implicit checked conversion operator. 
    */ 
    template<class T> 
    operator T&() { return as<T>(); } 


    /** 
    * Convert from structure to real structure. 
    */ 
    template<class T> 
    T& as() { 

     // This naturally fails more frequently if changed to "!=". 
     if (sizeof(T) < data.size()) { 

      std::ostringstream message; 

      message << "Attempt to cast dynamic struct of size " 
       << data.size() << " to type of size " << sizeof(T) << "."; 

      throw dynamic_struct_size_error(message.str()); 

     } // if 

     return *reinterpret_cast<T*>(&data[0]); 

    } // as() 


private: 


    // Map from keys to member offsets. 
    map_type members; 

    // Data buffer. 
    std::vector<unsigned char> data; 

    // Maximum allowed size. 
    const unsigned int max; 


}; // class dynamic_struct 
+0

Tôi lấy phiếu bầu để đóng vì SO không chịu trách nhiệm về pe xem xét lại mã của tôi và bất kỳ câu hỏi nào có chứa mã yêu cầu cuộn tự động nhận được một ghi chú. Ah, tốt. –

+0

Điều gì về việc viết bài này lên như một bài viết về CodeProject? Hoặc, nếu nó không được đóng cửa ở đây (có đóng cửa sẽ là một kết quả không may IMHO) thì bạn hoặc người khác có thể đính kèm một tiền thưởng cho nó. – John

+0

Vâng, tôi chỉ có thể phải tìm kiếm các địa điểm thay thế, mặc dù tôi đã cân nhắc việc thêm tiền thưởng, và nếu có bất kỳ lợi ích nào vẫn còn trong câu hỏi, tôi chắc chắn sẽ. –

Trả lời

1

Không có gì vốn sai với loại mã này là. Trì hoãn kiểm tra kiểu cho đến khi thời gian chạy là hoàn toàn hợp lệ, mặc dù bạn sẽ phải làm việc chăm chỉ để đánh bại hệ thống kiểu thời gian biên dịch. Tôi đã viết một lớp ngăn xếp đồng nhất, nơi bạn có thể chèn bất kỳ loại nào, hoạt động theo cách tương tự.

Tuy nhiên, bạn phải tự hỏi mình - bạn thực sự sẽ sử dụng điều này để làm gì? Tôi đã viết một ngăn xếp đồng nhất để thay thế ngăn xếp C++ cho một ngôn ngữ thông dịch, một thứ tự khá cao đối với bất kỳ lớp cụ thể nào. Nếu bạn không làm một cái gì đó quyết liệt, điều này có lẽ không phải là điều đúng đắn để làm.

Tóm lại, bạn có thể làm điều đó, và nó không phải là bất hợp pháp hoặc xấu hoặc không xác định và bạn có thể làm cho nó hoạt động - nhưng bạn chỉ nên nếu bạn có nhu cầu tuyệt đối để làm những việc ngoài phạm vi ngôn ngữ bình thường. Ngoài ra, mã của bạn sẽ chết một cách khủng khiếp khi C++ 0x trở thành Standard và bây giờ bạn cần phải di chuyển và tất cả phần còn lại của nó.

Cách dễ nhất để nghĩ mã của bạn thực sự là một vùng được quản lý có kích thước thu nhỏ. Bạn đặt trên nhiều loại đối tượng khác nhau .. chúng được lưu trữ liên tục, v.v.

Chỉnh sửa: Chờ, bạn không quản lý để thực thi an toàn loại khi chạy? Bạn chỉ cần thổi an toàn kiểu thời gian biên dịch nhưng không thay thế nó? Hãy để tôi đăng một số mã cao cấp hơn (đó là hơi chậm hơn, có lẽ).

Edit: Oh wait. Bạn muốn chuyển đổi dynamic_struct của bạn thành toàn bộ các cấu trúc khác chưa biết, lúc chạy? Oh. Trời ơi. Ồ, nghiêm túc. Gì. Không. Chỉ cần không. Thực sự, thực sự, không. Điều đó thật sai, không thể tin được. Nếu bạn đã phản ánh, bạn có thể thực hiện công việc này, nhưng C++ không cung cấp điều đó. Bạn có thể thực thi an toàn kiểu tại thời gian chạy cho mỗi thành viên riêng lẻ bằng cách sử dụng dynamic_cast và loại xóa bằng thừa kế. Không phải cho toàn bộ cấu trúc, bởi vì được đưa ra một kiểu T, bạn không thể biết được kiểu hoặc bố cục nhị phân là gì.

+0

Đây là một bài tập khác trong việc vượt qua C++ để giống như ngôn ngữ tôi đang viết, bởi vì tôi quá lười biếng để thực sự chỉ * làm việc * trên ngôn ngữ. Thành thật.Dù sao, một trong những ưu điểm của lớp này là nó có thể được sử dụng để làm cho chuyển đổi tự động đơn giản hơn giữa các cấu trúc tương thích ngữ nghĩa nhưng không tương thích nhị phân, mà tôi đoán sẽ là một lợi ích khi trộn các API C cũ. –

+0

Nó sẽ giúp ích như thế nào? Bạn chỉ có thể chuyển đổi ở cấp độ nhị phân. Sẽ dễ dàng hơn khi viết các toán tử dàn diễn viên của riêng bạn - họ không phải là thành viên, bạn biết đấy. – Puppy

+0

Vâng, hãy cân nhắc an toàn khi đi ra ngoài cửa sổ; rất tệ. Tôi đoán cách đơn giản nhất để chăm sóc nó là chỉ lưu trữ và kiểm tra kết quả 'typeid' cho mỗi thành viên. Các truy cập không được kiểm soát sau đó sẽ tương đương với 'reinterpret_cast' cũ, nếu bạn cần điều đó. –

1

Tôi nghĩ rằng việc kiểm tra loại có thể được cải thiện. Ngay bây giờ nó sẽ tự reinterpret_cast cho bất kỳ loại nào có cùng kích thước.

Có thể tạo giao diện để đăng ký cấu trúc máy khách khi khởi động chương trình, vì vậy chúng có thể được xác minh từng thành viên - hoặc thậm chí sắp xếp lại khi đang bay hoặc được xây dựng một cách thông minh hơn ngay từ đầu.

#define REGISTER_DYNAMIC_STRUCT_CLIENT(STRUCT, MEMBER) \ 
    do dynamic_struct::registry<STRUCT>() // one registry obj per client type \ 
     .add(# MEMBER, &STRUCT::MEMBER, offsetof(STRUCT, MEMBER)) while(0) 
     // ^name as str^ptr to memb^check against dynamic offset 
+0

Tôi chỉ đang nghĩ về các giải pháp dựa trên vĩ mô cho điều này, bản thân tôi và của bạn là một cách tiếp cận tốt. Nó sẽ liên quan đến việc tách * mô tả * của một 'dynamic_struct' từ thể hiện * *, nhưng đó là thứ cần phải làm. –

+0

@Jon: Không có sự tách biệt cần phải xảy ra ở tất cả ... Tôi đã nghĩ rằng phương pháp 'as', một khi nó biết đích của diễn viên, sẽ tham khảo ý kiến ​​của cơ quan đăng ký. Có thể con trỏ thông minh được tính tham chiếu có thể đạt được sự tách biệt một phần và tốt nhất của cả hai thế giới. – Potatoswatter

+0

Suy nghĩ của tôi chính xác: bạn đã nhầm lẫn ý nghĩa của tôi. Khi nó đứng, thành viên 'member' của' dynamic_struct' là, tốt, một thành viên. Nó nên được sống tĩnh trong registry và được chia sẻ giữa các thể hiện 'dynamic_struct'. Đó là tất cả. –

0

Tôi có một câu hỏi: bạn thoát khỏi điều gì?

tôi muốn nói đó là một mảnh thông minh mã nhưng:

  • bạn đang loay hoay với nhớ, nguy cơ thổi lên là rất lớn
  • nó khá phức tạp quá, tôi đã không nhận được tất cả mọi thứ và tôi chắc chắn sẽ phải đặt ra còn ...

những gì tôi thực sự băn khoăn là những gì bạn thực sự muốn ...

Ví dụ, sử dụng Boost.Fusion

struct a_key { typedef char type; }; 
struct object_key { typedef Foo type; }; 

typedef boost::fusion< 
    std::pair<a_key, a_key::type>, 
    std::pair<object_key, object_key::type> 
> data_type; 

int main(int argc, char* argv[]) 
{ 
    data_type data; 
    boost::fusion::at_key<a_key>(data) = 'a'; // compile time checked 
} 

Sử dụng Boost.Fusion bạn nhận được phản ánh thời gian biên dịch cũng như đóng gói chính xác.

Tôi không thực sự thấy cần phải chọn "thời gian chạy" ở đây (sử dụng giá trị làm khóa thay vì loại) khi bạn cần chuyển đúng loại cho bài tập (charFoo).

Cuối cùng, lưu ý rằng điều này có thể được tự động, nhờ Preprocessor lập trình:

DECLARE_ATTRIBUTES(
    mData, 
    (char, a) 
    (char, b) 
    (char, c) 
    (int, i) 
    (Foo, object) 
) 

Không có nhiều dài dòng hơn một tuyên bố điển hình, mặc dù a, b, vv ... sẽ được loại bên trong chứ không phải là tên thuộc tính .

này có một số ưu điểm so với giải pháp của bạn:

  • thời gian biên dịch kiểm tra
  • tuân thủ hoàn hảo với mặc định được tạo nhà xây dựng/copy constructor/etc ...
  • nhiều hơn nữa compact đại diện
  • không tra cứu thời gian chạy của thành viên "đúng"
Các vấn đề liên quan