Ném ngoại lệ trong trò chơi C ++ của DLL? Ưu và nhược điểm


8

Những ưunhược điểm của việc sử dụng Ngoại lệ trong C ++ liên quan đến phát triển trò chơi.

Hướng dẫn về phong cách của Google nói rằng họ không sử dụng Ngoại lệ vì nhiều lý do. Là những lý do tương tự thích hợp cho phát triển trò chơi?

Chúng tôi không sử dụng ngoại lệ C ++ ... - google-styleguide.googlecode.com

Một số vấn đề cần suy nghĩ.

  • Làm thế nào nó liên quan đến sự phát triển của một thư viện được sử dụng thông qua nhiều dự án.
  • Làm thế nào nó ảnh hưởng đến thử nghiệm đơn vị, thử nghiệm tích hợp, vv?
  • Nó làm gì để sử dụng các thư viện bên thứ ba.

3
Điều kỳ lạ là không ai có bất kỳ ưu điểm nào
David Young

Câu trả lời:


7

Cũng có một số hậu quả khó chịu khi sử dụng ngoại lệ trong C ++ nếu bạn không biết tất cả các chi tiết về cách chúng hoạt động. Vì nhiều trò chơi được viết bằng C ++, đây có thể là một yếu tố.

Ví dụ, trong C ++, việc ném một ngoại lệ vào hàm hủy có thể gây ra hậu quả nghiêm trọng. Nó có thể gây ra tất cả các loại hành vi không xác định và sự cố, bởi vì bạn có thể bị mắc kẹt trong một tình huống mà bạn có nhiều hơn một ngoại lệ hoạt động trong chuyến bay. (Xem Mục số 8 của C ++ hiệu quả để biết thêm thông tin về điều này.)


4
+1 cho C ++ hiệu quả. Đọc ấn bản thứ 3 khi chúng tôi nói chuyện. Ngoại lệ là tốt với tôi, miễn là bạn rất nghiêm ngặt về nơi bạn sử dụng chúng, nơi bạn bắt chúng và bạn có thể đảm bảo rằng bất kỳ mã bên thứ 3 nào cũng làm như vậy.
Anthony

9

Tham gia vào quan điểm của Phần mềm về Ngoại lệ.

Quan điểm thú vị hiện không có trong bất kỳ câu trả lời,

Lý do là tôi coi các trường hợp ngoại lệ không tốt hơn "goto", được coi là có hại kể từ những năm 1960, trong đó chúng tạo ra một bước nhảy đột ngột từ điểm này sang điểm khác. Trên thực tế, chúng còn tệ hơn đáng kể so với goto:

  1. Chúng vô hình trong mã nguồn. Nhìn vào một khối mã, bao gồm các hàm có thể hoặc không thể ném ngoại lệ, không có cách nào để xem ngoại lệ nào có thể được ném và từ đâu. Điều này có nghĩa là ngay cả việc kiểm tra mã cẩn thận cũng không tiết lộ các lỗi tiềm ẩn.

  2. Chúng tạo quá nhiều điểm thoát có thể cho một hàm. Để viết mã chính xác, bạn thực sự phải suy nghĩ về mọi đường dẫn mã có thể thông qua chức năng của bạn. Mỗi khi bạn gọi một hàm có thể đưa ra một ngoại lệ và không bắt gặp tại chỗ, bạn sẽ tạo cơ hội cho các lỗi bất ngờ gây ra bởi các hàm bị chấm dứt đột ngột, khiến dữ liệu ở trạng thái không nhất quán hoặc các đường dẫn mã khác mà bạn không nghĩ về.

http://www.joelonsoftware.com/items/2003/10/13.html


Halleluja ... Tài liệu tham khảo tuyệt vời. Tôi đã có ác cảm với các ngoại lệ vì chính xác lý do này, nhưng rõ ràng đó là cách làm việc ưa thích hiện nay. Thật tuyệt khi thấy một bài viết hay mang đến một góc nhìn khác
cóc

1
+1 vì các ngoại lệ có rủi ro quá cao để thất bại muộn, tức là khi trò chơi đã được xuất xưởng.
martinpi

Tôi hoàn toàn đồng ý với điểm số 2. Tôi sẽ không đấu tranh chống lại các ngôn ngữ sử dụng ngoại lệ xuyên suốt, nhưng tôi cũng không coi chúng là cách 'đúng' để xử lý các điều kiện lỗi.
Kylotan

5

Các ngoại lệ không được hỗ trợ và do đó không được khuyến khích trong ít nhất một môi trường phát triển giao diện điều khiển hiện đại.

Hầu hết các nhà phát triển giao diện điều khiển mà tôi biết không muốn sử dụng chúng vì dù sao đã thêm chi phí thực thi và triết lý mà họ chỉ đơn giản là không cần thiết. RTTI được xem theo cùng một cách.


Bàn điều khiển nào không được hỗ trợ?
David Young

Tôi đang cố gắng tỏ ra nhút nhát vì NDA, mặc dù trước đó nó đã được tiết lộ cụ thể hơn về SO.
Dan Olson

2

Trong tất cả các dự án tôi đã làm trong các trò chơi, không ai trong số họ sử dụng ngoại lệ. Chức năng gọi qua đầu là lý do chính. Như đã đề cập trước đây, như RTTI, ở hầu hết các hãng phim, không phải là một chủ đề thảo luận. Không phải vì hầu hết các lập trình viên đến từ nền tảng Java / học thuật, bởi vì thật lòng mà nói, hầu hết không. Xin lưu ý bạn, điều này xuất phát từ một người không bao giờ sử dụng xử lý ngoại lệ (cũng được, một hoặc hai lần trong các công cụ - nó không làm điều đó cho tôi, tôi đoán đó là những gì bạn lớn lên).
Nó không ảnh hưởng đến thử nghiệm đơn vị thực sự như bạn chỉ khẳng định về các điều kiện. Đối với các thư viện, bạn tốt hơn hết là không có ngoại lệ vì bạn sẽ buộc mọi người sử dụng nó.
Mặc dù nó làm cho một số tình huống khó xử lý hơn một chút, nhưng nó cũng buộc bạn phải có một thiết lập được kiểm soát nhiều hơn, vì các trường hợp ngoại lệ có thể dẫn đến lạm dụng. Lỗi dung sai là tốt, nhưng không nếu nó dẫn đến mã hóa cẩu thả.


1
Bạn đã đúng, nhưng thật không may thay vì sử dụng xử lý lỗi thay thế, hầu hết các lập trình viên trò chơi chỉ quay lại gặp sự cố hoặc trả lại NULL (điều này có thể sẽ dẫn đến sự cố dù sao). Chúng tôi đã không đưa ra bất kỳ thay thế phong nha.
tenpn

Trả lại NULL, hoặc mã lỗi, là một sự thay thế hoàn toàn hợp lý miễn là bạn cũng kiểm tra mã trả lại.
JasonD

Vâng, nhưng hầu hết các lập trình viên trò chơi thì không. Đó là quan điểm của tôi.
tenpn

1
Đó là vấn đề của ngôn ngữ C ++ nhiều như mọi thứ - nếu bạn thấy một dòng mã nói rằng function();bạn không biết ngay lập tức liệu mã lỗi có bị bỏ qua hay không. Đây có lẽ là lý do tại sao các ngôn ngữ cho phép bạn bỏ qua giá trị trả về hãy thử và ép buộc bạn thích ngoại lệ.
Kylotan

1
Việc thực thi các đối số lỗi không còn là một vấn đề. Mã hiện đại không sử dụng ngoại lệ sẽ dựa vào một Errorlớp nhẹ và nếu bị bỏ qua sẽ dẫn đến một xác nhận bị loại bỏ. Vì vậy, nó thực sự không thể bị bỏ qua không quá một ngoại lệ có thể được bỏ qua.
Samaursa

1

Điều quan trọng nhất đối với tôi với tư cách là một nhà phát triển trò chơi chuyên nghiệp là những trường hợp ngoại lệ phải được viết bởi những người hiểu cách thức hoạt động của ngoại lệ (có rất ít trong ngành công nghiệp trò chơi) được gỡ lỗi bởi họ và mã cơ bản chạy chúng (đó là một mớ hỗn độn dù sao và sẽ giết bộ xử lý theo thứ tự của bạn) phải được viết bởi những người cung cấp trình biên dịch / trình liên kết của bạn. Vấn đề với điều đó là chúng chỉ có một lượng tài nguyên hữu hạn, vì vậy chúng tập trung vào việc viết các tối ưu hóa và sửa lỗi tốt hơn cho mã mà các nhà phát triển trò chơi sử dụng ... thường không phải là ngoại lệ. Nếu bạn gặp lỗi ngoại lệ trên phần cứng hiện đại với trình biên dịch mới, bạn sẽ gọi ai? chuyên gia ngoại lệ của bạn, người nghĩ rằng mọi thứ đều ổn với mã, hoặc nhà cung cấp nói, vâng, sớm thôi, chúng tôi '

Không có gì sai với ngoại lệ, chỉ là chúng không tốt bởi vì thực tế cản trở lý thuyết.


0

Đối với tôi, xử lý ngoại lệ có thể tuyệt vời nếu mọi thứ phù hợp với RAII và bạn bảo lưu ngoại lệ cho các đường dẫn thực sự đặc biệt (ví dụ: một tệp bị hỏng được đọc) và bạn không bao giờ cần phải vượt qua ranh giới mô-đun và toàn bộ nhóm của bạn đang ở trên chúng với chúng ......

EH chi phí không

Hầu hết các trình biên dịch ngày nay thực hiện xử lý ngoại lệ chi phí bằng 0, điều này có thể làm cho nó thậm chí rẻ hơn so với việc phân nhánh thủ công các điều kiện lỗi trong các đường dẫn thực thi thông thường, mặc dù đổi lại nó làm cho các đường dẫn đặc biệt rất tốn kém (cũng có một số mã phình to từ nó, mặc dù có lẽ không nhiều hơn hơn nếu bạn xử lý triệt để mọi lỗi có thể bằng tay). Thực tế là việc ném rất tốn kém không quan trọng mặc dù nếu các trường hợp ngoại lệ được sử dụng theo cách họ dự định trong C ++ (đối với các trường hợp thực sự đặc biệt không nên xảy ra trong điều kiện bình thường).

Tác dụng phụ đảo ngược

Điều đó nói rằng, làm cho mọi thứ phù hợp với RAII nói dễ hơn nhiều so với thực hiện. Thật dễ dàng cho các tài nguyên cục bộ được lưu trữ trong một hàm để bọc chúng vào các đối tượng C ++ với các hàm hủy tự dọn sạch, nhưng không dễ để viết logic hoàn tác / khôi phục cho mọi hiệu ứng phụ có thể xảy ra trong toàn bộ phần mềm. Chỉ cần xem xét việc viết một container như thế std::vectornào là trình tự truy cập ngẫu nhiên đơn giản nhất để hoàn toàn ngoại lệ an toàn. Bây giờ nhân lên khó khăn đó trên tất cả các cấu trúc dữ liệu của toàn bộ phần mềm quy mô lớn.

Như một ví dụ cơ bản, hãy xem xét một ngoại lệ gặp phải trong quá trình chèn các mục vào biểu đồ cảnh. Để khôi phục chính xác từ ngoại lệ đó, bạn có thể cần hoàn tác những thay đổi đó trong biểu đồ cảnh và khôi phục hệ thống trở lại trạng thái như thể hoạt động không bao giờ xảy ra. Điều đó có nghĩa là tự động loại bỏ những đứa trẻ được chèn vào biểu đồ cảnh khi gặp một ngoại lệ, có lẽ từ một người bảo vệ phạm vi.

Làm điều này kỹ lưỡng trong một phần mềm gây ra nhiều tác dụng phụ và xử lý nhiều trạng thái liên tục thì nói dễ hơn nhiều so với thực hiện. Thông thường tôi nghĩ rằng giải pháp thực dụng là không bận tâm đến nó vì lợi ích của việc vận chuyển. Với loại codebase phù hợp có hệ thống hoàn tác trung tâm nào đó và có thể là cấu trúc dữ liệu liên tục và số lượng chức năng tối thiểu gây ra tác dụng phụ, bạn có thể đạt được sự an toàn ngoại lệ trên bảng ... nhưng nó rất nhiều những thứ bạn cần nếu tất cả những gì bạn sẽ làm là cố gắng làm cho mã của bạn trở nên an toàn ở mọi nơi.

Thực tế có điều gì đó không đúng vì sự đảo ngược tác dụng phụ về mặt khái niệm là một vấn đề khó khăn, nhưng nó trở nên khó khăn hơn trong C ++. Đôi khi thực sự dễ dàng đảo ngược các tác dụng phụ trong C khi phản hồi mã lỗi hơn là thực hiện để đáp ứng với một ngoại lệ trong C ++. Một trong những lý do là bởi vì bất kỳ điểm đã cho nào của bất kỳ chức năng nào cũng có thể có khả năng throwtrong C ++. Nếu bạn đang cố gắng viết một thùng chứa chung làm việc với loại T, bạn thậm chí không thể lường trước được các điểm thoát đó ở đâu, vì mọi thứ liên quan đều Tcó thể ném. Thậm chí so sánh cái này Tvới cái khác cho sự bình đẳng có thể ném. Mã của bạn phải xử lý mọi khả năng và số lượng khả năng nhân lên theo cấp số nhân trong C ++ trong khi ở C, chúng có số lượng ít hơn nhiều.

Thiếu tiêu chuẩn

Một vấn đề khác của xử lý ngoại lệ là thiếu các tiêu chuẩn trung tâm. Có một số người hy vọng tất cả mọi người đều đồng ý rằng kẻ hủy diệt không bao giờ nên ném vì những lần quay trở lại không bao giờ thất bại, nhưng đó chỉ là một trường hợp bệnh lý thường làm hỏng phần mềm nếu bạn vi phạm quy tắc.

Cần có một số tiêu chuẩn hợp lý hơn như không bao giờ ném từ toán tử so sánh (tất cả các hàm không thay đổi về mặt logic), không bao giờ ném từ một ctor di chuyển, v.v. Những đảm bảo như vậy sẽ giúp việc viết mã an toàn ngoại lệ dễ dàng hơn nhiều, nhưng chúng tôi không có đảm bảo như vậy. Chúng ta phải đi theo quy tắc rằng mọi thứ đều có thể ném - nếu không phải bây giờ, thì có thể trong tương lai.

Tồi tệ hơn, những người từ các nền tảng ngôn ngữ khác nhau nhìn các ngoại lệ rất khác nhau. Trong Python, họ thực sự có triết lý "bước nhảy vọt trước khi bạn nhìn" liên quan đến việc kích hoạt các ngoại lệ trong các đường dẫn thực thi thông thường! Khi bạn nhận được các nhà phát triển như vậy viết mã C ++, bạn có thể xem xét các ngoại lệ bị ném sang trái và phải cho những thứ không thực sự đặc biệt và xử lý tất cả có thể là một cơn ác mộng nếu bạn đang cố gắng viết mã chung, ví dụ:

Xử lý lỗi

Xử lý lỗi có nhược điểm là bạn phải kiểm tra mọi lỗi có thể xảy ra. Nhưng nó có một ưu điểm là đôi khi bạn có thể kiểm tra các lỗi hơi muộn trong nhận thức muộn, như glGetError, để giảm đáng kể các điểm thoát trong một chức năng cũng như làm cho chúng rõ ràng. Đôi khi bạn có thể tiếp tục chạy mã lâu hơn một chút trước khi kiểm tra và lan truyền và phục hồi từ một lỗi, và đôi khi điều đó thực sự có thể dễ dàng hơn xử lý ngoại lệ (đặc biệt là đảo ngược hiệu ứng phụ). Nhưng bạn thậm chí có thể không bận tâm đến lỗi nhiều trong một trò chơi, có thể chỉ tắt trò chơi bằng một thông báo nếu bạn gặp phải những thứ như tệp bị hỏng, lỗi bộ nhớ, v.v.

Làm thế nào nó liên quan đến sự phát triển của một thư viện được sử dụng thông qua nhiều dự án.

Một phần khó chịu của các ngoại lệ trong ngữ cảnh DLL là bạn không thể ném ngoại lệ từ mô-đun này sang mô-đun khác một cách an toàn trừ khi bạn có thể đảm bảo rằng chúng được xây dựng bởi cùng một trình biên dịch, sử dụng cùng các cài đặt, v.v. Vì vậy, nếu bạn viết như một kiến ​​trúc plugin dành cho mọi người được sử dụng từ tất cả các loại trình biên dịch và thậm chí có thể từ các ngôn ngữ khác nhau như Lua, C, C #, Java, v.v., thường các ngoại lệ bắt đầu trở thành một phiền toái lớn vì bạn phải nuốt mọi ngoại lệ và dịch chúng thành lỗi mã nào cũng ở khắp mọi nơi

Nó làm gì để sử dụng các thư viện bên thứ ba.

Nếu họ là dylib, thì họ không thể ném ngoại lệ một cách an toàn vì những lý do nêu trên. Họ sẽ phải sử dụng mã lỗi, điều đó cũng có nghĩa là bạn, sử dụng thư viện, sẽ phải liên tục kiểm tra mã lỗi. Họ có thể bọc dylib của họ bằng trình bao bọc C ++ được liên kết tĩnh mà bạn tự xây dựng để dịch mã lỗi từ dylib thành ngoại lệ ném (về cơ bản ném từ nhị phân của bạn vào nhị phân của bạn). Nói chung, tôi nghĩ rằng hầu hết các lib của bên thứ ba thậm chí không nên bận tâm và chỉ dính vào mã lỗi nếu họ sử dụng dylibs / libs được chia sẻ.

Làm thế nào nó ảnh hưởng đến thử nghiệm đơn vị, thử nghiệm tích hợp, vv?

Tôi thường không gặp phải các nhóm kiểm tra đường dẫn đặc biệt / lỗi mã của họ rất nhiều. Tôi đã từng làm điều đó và thực sự có mã có thể phục hồi đúng cách bad_allocvì tôi muốn làm cho mã của mình cực kỳ mạnh mẽ, nhưng không thực sự hữu ích khi tôi là người duy nhất làm điều đó trong một nhóm. Cuối cùng tôi đã ngừng làm phiền. Tôi tưởng tượng về một phần mềm quan trọng mà toàn bộ các đội có thể kiểm tra để đảm bảo mã của họ phục hồi mạnh mẽ từ các ngoại lệ và lỗi trong tất cả các trường hợp có thể, nhưng đó là rất nhiều thời gian để đầu tư. Thời gian có ý nghĩa trong phần mềm quan trọng nhưng có thể không phải là một trò chơi.

Yêu ghét

Ngoại lệ cũng là loại tính năng yêu / ghét của tôi đối với C ++ - một trong những tính năng có ảnh hưởng sâu sắc nhất của ngôn ngữ khiến RAII giống như một yêu cầu hơn là sự tiện lợi, vì nó thay đổi cách bạn phải viết mã lộn ngược , C để xử lý thực tế là bất kỳ dòng mã đã cho nào cũng có thể là điểm thoát ngầm cho hàm. Đôi khi tôi gần như muốn ngôn ngữ không có ngoại lệ. Nhưng có những lúc tôi thấy các ngoại lệ thực sự hữu ích, nhưng chúng quá nhiều yếu tố chi phối đối với tôi trong việc tôi chọn viết một đoạn mã bằng C hay C ++.

Chúng sẽ hữu ích hơn theo cấp số nhân đối với tôi nếu ủy ban tiêu chuẩn thực sự tập trung vào việc thiết lập các tiêu chuẩn ABI cho phép các ngoại lệ được ném an toàn qua các mô-đun, ít nhất là từ mã C ++ đến C ++. Sẽ thật tuyệt vời nếu bạn có thể lướt qua các ngôn ngữ một cách an toàn, mặc dù đó là một giấc mơ xa vời. Một điều khác sẽ làm cho các ngoại lệ trở nên hữu ích hơn theo cấp số nhân đối với tôi là nếu các noexcepthàm thực sự gây ra lỗi trình biên dịch nếu chúng cố gắng gọi bất kỳ chức năng nào có thể ném thay vì chỉ std::terminategặp phải một ngoại lệ. Đó là bên cạnh vô dụng (cũng có thể gọi đó là icantexceptthay vì noexcept). Sẽ dễ dàng hơn nhiều để đảm bảo rằng tất cả các tàu khu trục đều ngoại lệ - an toàn, ví dụ, nếunoexcept thực sự gây ra lỗi trình biên dịch nếu hàm hủy làm bất cứ điều gì có thể ném. Nếu quá đắt để xác định triệt để tại thời điểm biên dịch, thì chỉ cần làm cho nó noexcepthoạt động như vậy (mà tất cả các hàm hủy đều phải được) chỉ được phép gọi tương tựnoexcept các hàm / toán tử và biến nó thành một lỗi biên dịch từ bất kỳ một trong số chúng.


-2

Theo như tôi thấy thì có hai lý do chính mà nó không được sử dụng theo truyền thống trong phát triển trò chơi:

  • Hầu hết các lập trình viên trò chơi đều là sinh viên tốt nghiệp, những người đã học trên Java và Haskell hoặc lập trình viên C, những người không có bất kỳ kinh nghiệm nào về ngoại lệ. Do đó, họ khá sợ chúng và không biết cách sử dụng chúng một cách chính xác.
  • Ngăn xếp thư giãn khi một ngoại lệ được ném có ý nghĩa về hiệu suất (mặc dù đây là loại trí tuệ nhận được, và một số tài liệu tham khảo sẽ rất tuyệt).

3
Lập trình viên Java không biết cách sử dụng Ngoại lệ? Java dựa trên xử lý ngoại lệ. Ngay cả những lập trình viên Java cơ bản nhất cũng hiểu các khối Thử, Bắt, Cuối cùng. Bạn thực sự không thể lập trình trong Java mà không ném Ngoại lệ hoặc xử lý chúng.
David Young

4
Nhiều hơn thì chỉ cần xếp chồng lên nhau. Ngoại lệ thêm chi phí cho mỗi cuộc gọi chức năng. codeproject.com/KB/cpp/exceptionhandler.aspx
Jonathan Fischoff

@David Young Tôi đang nói nhiều hơn về những người bảo vệ mạnh / yếu, và mặt phức tạp hơn của việc sử dụng ngoại lệ, sinh viên tốt nghiệp đại học có thể không có kinh nghiệm. Nói chung, nhiều trường đại học quan tâm nhiều hơn đến cách bạn cấu trúc OOP của mình ít chú trọng đến việc học cách sử dụng các ngoại lệ Java đúng cách. Nhưng bạn nói đúng tôi không rõ ràng.
tenpn

Ngoài ra, có thể "sợ" là từ sai. :)
tenpn

3
Tôi sẽ nói rằng họ thường trái ngược với "sợ" - nếu bạn không có quy tắc "không ngoại lệ, không ngoại lệ" nghiêm ngặt, hầu hết sinh viên tốt nghiệp Java gần đây sẽ ném ngoại lệ thường xuyên hơn là quay lại, giống như những lập trình viên C mới làm quen sử dụng goto và phá vỡ hơn là viết bất biến vòng lặp tốt.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.