2010-02-07 24 views
6

Tôi thích kiểu dáng mẫu phù hợp với Haskell.Kiểu kết hợp mẫu trong C++?

Tôi có C++ mã của tôi như sau:

ObjectPtr ptr; 
if(ptr.isType<Foo>()) { // isType returns a bool 
    Ptr<Foo> p = ptr.convertAs<Foo>(); // convertAs returns a Ptr<Foo> 
    ...... 
} 
if(ptr.isType<Bar>()) { 
    Ptr<Bar> p = ptr.convertAs<Bar>(); 
    ...... 
} 

Bây giờ, là có bất kỳ macro Toâi coù theå laøm xác định để đơn giản hóa này? Tôi đã cân nhắc điều này một thời gian, nhưng không thể đơn giản hóa nó thêm.

Cảm ơn!

+2

Tôi đoán bạn đang thiếu '()' sau 'isType'. – AndiDog

+0

Cuộc gọi tốt. +1 trên nhận xét. – anon

+0

Trong Mach7, mã của bạn sẽ trông giống như sau:

 Match(*ptr) { Case(Foo) return match0.foo_member(); Case(Bar) return match0.bar_member(); Otherwise() return 0; } EndMatch 
Xem https://github.com/solodon4/Mach7 và câu trả lời của tôi về Mach7 bên dưới – solodon

Trả lời

2

Tôi giả định rằng mẫu Ptr của bạn có khái niệm về một con trỏ NULL.

ObjectPtr ptr; 
if(Ptr<Foo> p = ptr.convertAs<Foo>()) { // convertAs returns a NULL pointer if the conversion can't be done. 
    ...... 
} 
if(Ptr<Bar> p = ptr.convertAs<Bar>()) { 
    ...... 
} 

Mặc dù, như những người khác đã lưu ý, việc bật kiểu thường là dấu hiệu bạn đang làm điều gì đó sai trong C++. Bạn nên cân nhắc việc sử dụng các hàm ảo thay thế.

+0

Điều này không chỉ đưa ra một câu hỏi khác - "làm thế nào để tôi viết convertAs() để làm điều đó?" –

+0

@Neil: Không thực sự - mã ban đầu của anh ấy giả định sự tồn tại của một chuyển đổi. Điều này có lẽ sẽ sử dụng một dynamic_cast, trong đó tạo ra một con trỏ null nếu nó không thể chuyển đổi một con trỏ đến loại mục tiêu. Như vậy, rất có thể là khá tốt mà nó đã có mặt, hoặc khá dễ thực hiện nếu không có mặt. –

+2

Phần tốt nhất là, trong C++ 0x bạn có thể thay thế 'Ptr 'bằng' tự động'. – Omnifarious

7

dynamic_cast sẽ xuất hiện để làm những gì bạn muốn

struct A { 
    virtual ~A() {} 
}; 

struct B : struct A { ... }; 
struct C : struct A { ... }; 

A * a = new C; 

if (C * c = dynamic_cast<C*>(a)) { 
    c->someCfunc(); 
} 
else if (B * b = dynamic_cast<B*>(a)) { 
    b->someBfunc(); 
} 
else { 
    throw "Don't know that type"; 
} 
+0

+1 cho nỗ lực. Tuy nhiên, tôi đang sử dụng con trỏ thông minh. – anon

+0

Sau đó thực hiện một phép đúc động trên con trỏ chứa - ví dụ, nếu bạn đang sử dụng std :: auto_ptr(), hãy thực hiện nó trên kết quả gọi get(). –

7

Tôi yêu Haskell kiểu mẫu phù hợp.

Sau đó viết chương trình của bạn trong Haskell.

Điều bạn đang cố gắng làm là chuyển đổi qua một loại. Đó là một điều phổ biến mà mọi người làm nếu họ muốn tránh các chức năng ảo. Bây giờ, sau này là một nền tảng của những gì OO trong C++ là tất cả về. Nếu bạn muốn tránh chúng, tại sao bạn lập trình bằng C++?


Còn về lý do này không được tán thành: Hãy tưởng tượng bạn có rất nhiều mã như thế này

if(ptr.isType<Foo>()) ... 
if(ptr.isType<Bar>()) ... 

bôi khắp nơi trên mã của bạn và sau đó có ai đó đến và cho biết thêm Baz cho các loại có thể là ptr sức đại diện. Bây giờ bạn đang tìm kiếm thông qua một cơ sở mã lớn, cố gắng tìm tất cả những nơi mà bạn chuyển qua một loại, và cố gắng tìm ra những người bạn cần phải thêm Baz đến.

Và, như Murphy đã có, ngay khi bạn hoàn thành, cũng có kèm theo Foz để được thêm làm loại. (Hoặc, suy nghĩ một lần nữa, nếu Murphy có cách của mình nó len lỏi trong trước bạn đã có một cơ hội quá hoàn chỉnh thêm Baz.)

+2

Kiểm soát bộ nhớ mức thấp của C. Constructors & Destructors of objects trên Stack <- điều này thật tuyệt vời, vì nó mang lại cho con trỏ thông minh, RAII, ...; thư viện mẫu chuẩn (có bất kỳ chức năng nào trong số này sử dụng các hàm ảo không?). 1 giả định câu trả lời là câu hỏi chính hãng, không phải troll. – anon

+1

Nó chắc chắn không phải là một câu hỏi thực sự, nhưng không phải nó là một troll. Đó là một câu hỏi tu từ. Nói một cách đơn giản: Bạn không nên làm điều này trong C++ _. Đó là những chức năng ảo được phát minh; họ đặt tất cả các mã liên quan đến các đối tượng thuộc loại 'Foo' vào' Foo', và mọi thứ liên quan đến 'Baz' thành' Baz'.Đây không phải lúc nào cũng tốt nhất bạn có thể có được (nhân chứng, ví dụ, động lực đằng sau mẫu khách truy cập), nhưng đó là mặc định bạn nên bắt đầu bằng C++. – sbi

+0

'' Bây giờ, sau này là nền tảng của OO trong C++ là gì.'' Sau đó, tại sao chúng ta nên sử dụng lập trình meta template khi nó cơ bản là FP và không phải OO, tất cả chúng ta nên đến Haskel thay vì sử dụng STL, thúc đẩy hoặc sử dụng các mẫu của riêng chúng ta ... _rolleyes_ Hơn nữa chỉ có một người nào đó hoàn toàn không biết gì về C++ sẽ làm cho một câu hỏi như vậy, [như thế này guy (link to pdf)] (http://www.stroustrup.com/OpenPatternMatching.pdf) hoàn toàn không biết gì! – Trinidad

2

Một nghĩ macro này không chính xác những gì bạn muốn:

#define DYN_IF(dest_type, dest_ptr, src_ptr)         \ 
    if((src_ptr).isType<dest_type>())          \ 
     if(int dest_type##dest_ptr = 1)          \ 
     for(Ptr<dest_type> dest_ptr = (src_ptr).convertAs<dest_type>();  \ 
      dest_type##dest_ptr;            \ 
      dest_type##dest_ptr=0)           

Cách sử dụng:

ObjectPtr ptr; 
DYN_IF(Foo, foo_ptr, ptr) { 
    // foo_ptr is Ptr<Foo> 
} 
DYN_IF(Bar, bar_ptr, ptr) // Works without braces too for single statement 
    // bar_ptr is Ptr<Bar> 

tôi sẽ không khuyên bạn nên loại công cụ này trong mã đó có nghĩa là để được đọc bởi người khác , nhưng kể từ khi bạn đề cập đến từ "vĩ mô" ...

Ngoài ra, tôi sẽ không giả vờ điều này có liên quan đến kiểu mẫu trong kiểu Haskell/OCaml. Kiểm tra Scala nếu bạn muốn một ngôn ngữ có ngữ nghĩa tương tự như C++ (tốt, loại) và khớp mẫu đúng.

5

Cố gắng mô phỏng kiểu đối sánh mẫu trong C++ bằng RTTI là một ý tưởng gọn gàng, nhưng nó có ràng buộc có những thiếu sót, vì có một số khác biệt đáng kể giữa các hàm tạo kiểu kiểu Haskell và Standard ML và lớp con C++. (Lưu ý: dưới đây, tôi sử dụng cú pháp Standard ML vì tôi cảm thấy thoải mái hơn với nó.)

  • Trong Haskell và Standard ML, khớp mẫu có thể ràng buộc giá trị lồng nhau để biến mẫu cho bạn (ví dụ như mô hình a::b::c::ds liên kết với các ba thành phần đầu tiên của danh sách là a, bc và phần còn lại của danh sách là ds). Trong C++, bạn sẽ vẫn phải đào sâu xung quanh trong các cấu trúc lồng nhau thực tế, trừ khi bạn hoặc ai đó đi kèm với các macro phức tạp hơn nhiều so với đã được đề xuất ở đây.
  • Trong Haskell và ML tiêu chuẩn, khai báo kiểu dữ liệu của hàm tạo kiểu như datatype 'a option = NONE | SOME of 'a xác định một loại mới: 'a option. Constructors NONESOME không phải là loại, chúng là các giá trị với các loại 'a option'a -> 'a option, tương ứng. Trong C++, khi bạn xác định các lớp con như FooBar để mô phỏng các trình tạo kiểu, bạn sẽ nhận được các kiểu mới.
  • Trong Haskell và ML tiêu chuẩn, các nhà thầu như SOME là các hàm hạng nhất tạo ra các giá trị của kiểu dữ liệu mà chúng thuộc về. Ví dụ: map SOME có loại 'a list -> 'a option list. Trong C++, sử dụng các lớp con để mô phỏng các hàm tạo kiểu, bạn không nhận được khả năng này.
  • Trong Haskell và ML tiêu chuẩn, các kiểu dữ liệu được đóng lại, vì vậy không ai có thể thêm các hàm tạo kiểu khác mà không thay đổi khai báo ban đầu, và trình biên dịch có thể xác minh tại thời gian biên dịch. Trong C++, bạn phải đi ra khỏi con đường của bạn để hạn chế những người có thể phân lớp lớp cơ sở của bạn.

Cuối cùng, bạn có nhận được đủ lợi ích từ kết hợp mẫu mô phỏng so với sử dụng đa hình C++ theo cách điển hình hơn không? Sử dụng macro để làm cho mô hình mô phỏng phù hợp hơn một chút ngắn gọn (trong khi làm xáo trộn nó cho những người khác đọc mã của bạn) đáng giá?

4

Chúng tôi đồng tác giả thư viện khớp mẫu cho C++, cho phép bạn thực hiện phân tích mẫu và phân tích kiểu rất hiệu quả. Thư viện, được gọi là Mach7, đã được phát hành theo giấy phép BSD và có sẵn trên GitHub: https://github.com/solodon4/Mach7. Bạn có thể tìm thấy video, áp phích, trang trình bày, giấy tờ cũng như mã nguồn ở đó. Nó hiện hỗ trợ GCC 4.4+, Clang 3.4+ và Visual C++ 2010+. Vui lòng đặt câu hỏi về thư viện bằng cách gửi một vấn đề GitHub chống lại kho lưu trữ của nó.

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