2009-07-13 71 views
5

Tôi đã làm việc với một codebase lớn được viết chủ yếu bởi các lập trình viên không còn làm việc tại công ty nữa. Một trong những lập trình viên dường như có một vị trí đặc biệt trong trái tim của mình cho các macro dài rất dài. Lợi ích duy nhất tôi có thể nhìn thấy khi sử dụng macro là có thể viết các hàm không cần phải chuyển qua trong tất cả các tham số của chúng (được đề xuất trong hướng dẫn thực hành tốt nhất mà tôi đã đọc). Khác hơn là tôi thấy không có lợi ích hơn một chức năng nội tuyến.Macro lớn C. Lợi ích là gì?

Một số macro quá phức tạp, tôi khó có thể tưởng tượng được ai đó thậm chí còn viết chúng. Tôi đã cố gắng tạo ra một trong tinh thần đó và đó là một cơn ác mộng. Gỡ lỗi là vô cùng khó khăn, vì phải mất N + dòng mã vào 1 trong trình gỡ rối (ví dụ: có một segfault ở đâu đó trong khối mã lớn này. Chúc may mắn!). Tôi đã thực sự kéo macro ra và chạy nó un-macro-tized để gỡ lỗi nó. Cách duy nhất tôi có thể thấy người viết là bằng cách tự động tạo ra chúng bằng mã được viết trong một hàm sau khi anh ấy đã sửa lỗi (hoặc thông minh hơn tôi và viết nó hoàn hảo lần đầu tiên, điều này luôn luôn có thể tôi đoán) .

Tôi có thiếu gì đó không? Tôi có điên không? Có các thủ thuật gỡ lỗi mà tôi không biết? Xin vui lòng điền vào tôi. Tôi thực sự muốn nghe từ những người yêu thích vĩ mô trong khán giả. :)

Trả lời

1

Một phần của lợi ích là mã sao chép mà không cần chi phí bảo trì cuối cùng - có nghĩa là, thay vì mã sao chép ở nơi khác bạn tạo macro từ nó và chỉ phải chỉnh sửa nó một lần ...

Tất nhiên, bạn cũng có thể chỉ cần thực hiện một phương pháp để được gọi nhưng đó là loại công việc nhiều hơn ... Tôi chống lại nhiều macro sử dụng bản thân mình, chỉ cần cố gắng trình bày một lý do tiềm năng.

8

Để tôi sử dụng tốt nhất các macro là để nén mã và giảm lỗi. Nhược điểm là rõ ràng trong gỡ lỗi, vì vậy họ phải được sử dụng cẩn thận.

Tôi có xu hướng nghĩ rằng nếu mã kết quả không phải là thứ tự độ lớn nhỏ hơn và ít bị lỗi hơn (có nghĩa là các macro xử lý một số chi tiết sổ kế toán) thì không đáng.

Trong C++, nhiều công dụng như thế này có thể được thay thế bằng mẫu, nhưng không phải tất cả. Một ví dụ đơn giản về các macro hữu dụng trong các macro xử lý sự kiện của MFC - không có chúng, việc tạo các bảng sự kiện sẽ khó hơn nhiều và mã bạn phải viết (và đọc) sẽ phức tạp hơn nhiều.

+0

Tôi có thể hiểu một số macro ngắn vì những lý do đó, nhưng có bất kỳ lợi ích nào để có chức năng xác thực san bằng được khai báo là macro so với hàm nội tuyến không? Tôi chạy vào rất nhiều lỗi bên trong một số macro X_VALIDATE tại nơi làm việc, nhưng chúng bao gồm nhiều câu lệnh khẳng định và vòng lặp. Một tập tin lõi sẽ không cho tôi biết khẳng định nào đã thất bại (do đó làm cho việc gỡ rối khó khăn hơn). Dường như với tôi điều này có thể là một chức năng nội tuyến mà không mất chi phí và rất nhiều lợi ích. Bạn có đồng ý không? – jdizzle

+2

Lợi ích chính để khẳng định trong macro (thay vì nội tuyến) là __FILE__ và __LINE__ sẽ chính xác. Vì bạn có một lõi, bạn có thể có được toàn bộ ngăn xếp, do đó, không phải là một việc lớn đối với bạn. Nếu nó có thể được viết mà không có macro, hãy thực hiện nó mà không cần macro. Nếu nó chỉ có thể được thực hiện với một vĩ mô và bạn nhận được một lợi ích, sau đó làm điều đó. Đối với tôi, lợi ích cần phải là mã đơn giản hơn nhiều hoặc ít dễ bị lỗi hơn. –

+0

__FILE__ và __LINE__ có nghĩa là '__FILE__' và' __LINE__' ở trên (không biết tôi cần phải thoát) –

4

Nếu các macro cực kỳ dài, chúng có thể làm cho mã ngắn nhưng hiệu quả. Thực tế, anh ta có thể đã sử dụng các macro để mã nội dòng một cách rõ ràng hoặc loại bỏ các điểm quyết định khỏi đường dẫn mã thời gian chạy. Nó có thể là quan trọng để hiểu rằng, trong quá khứ, tối ưu hóa như vậy đã không được thực hiện bởi nhiều trình biên dịch, và một số điều mà chúng ta cho là ngày hôm nay, như các cuộc gọi chức năng nhanh, không hợp lệ sau đó.

3

Đối với tôi, macro là điều xấu. Với rất nhiều tác dụng phụ của họ, và thực tế là trong C++ bạn có thể đạt được cùng một lợi nhuận perf với nội tuyến, họ không có giá trị rủi ro.

Ví dụ: thấy vĩ mô ngắn này:

#define max(a, b) ((a)>(b)?(a):(b)) 

sau đó thử cuộc gọi này:

max(i++, j++) 

More.Giả sử bạn có

#define PLANETS 8 
#define SOCCER_MIDDLE_RIGHT 8 

nếu có lỗi xảy ra, nó sẽ tham chiếu đến '8' nhưng không phải là một trong những biểu diễn trung bình của nó.

+6

Đó không phải là sử dụng tốt các macro, như bạn chỉ ra. Có nhiều lý do để sử dụng chúng - Tôi không tin vào các tính năng độc ác, chỉ các tính năng được sử dụng evilly :) - được sử dụng đúng cách, các macro rất hữu ích. –

3

Tôi chỉ biết hai lý do để làm những gì bạn mô tả.

Đầu tiên là buộc các chức năng được gạch chân. Điều này là khá nhiều vô nghĩa, vì từ khóa nội tuyến thường làm điều tương tự, và chức năng inlining thường là một vi-tối ưu hóa sớm anyway.

Thứ hai là mô phỏng các hàm được lồng trong C hoặc C++. Điều này có liên quan đến "các hàm viết của bạn không cần phải được truyền trong tất cả các tham số của chúng" nhưng thực sự có thể mạnh hơn thế một chút. Walter Bright gives ví dụ về nơi các hàm lồng nhau có thể hữu ích.

Có nhiều lý do khác để sử dụng các macro, chẳng hạn như sử dụng chức năng tiền xử lý cụ thể (như bao gồm __FILE____LINE__ trong các thông báo lỗi được tạo tự động) hoặc giảm mã boilerplate theo những cách mà các chức năng và các mẫu không thể (thư viện Boost.Preprocessor vượt trội ở đây ; xem Boost.ScopeExit hoặc this sample enum code để biết ví dụ), nhưng những lý do này dường như không áp dụng để thực hiện những gì bạn mô tả.

+0

Từ khóa nội tuyến không buộc các chức năng được gạch chân, mặc dù bạn nói đúng rằng thường thì đó là một sai lầm đối với lập trình viên khi cố gắng đưa ra các quyết định đó. –

+0

Đã sửa lỗi. Cảm ơn. –

0

Tôi không sử dụng macro. Hàm nội tuyến phục vụ mọi mục đích hữu ích mà macro có thể thực hiện. Macro cho phép bạn làm những việc rất kỳ lạ và phản trực giác như tách các số nhận dạng (Làm cách nào để ai đó tìm kiếm số nhận dạng?).

2

Macro rất dài sẽ có những hạn chế về mặt hiệu suất, như kích thước nhị phân được biên dịch tăng lên và chắc chắn có các lý do khác để không sử dụng chúng.

Đối với các macro có vấn đề nhất, tôi sẽ xem xét chạy mã thông qua bộ tiền xử lý và thay thế đầu ra macro bằng các cuộc gọi hàm (inline if possible) hoặc LOC thẳng. Nếu các macro tồn tại để tương thích với các kiến ​​trúc/hệ điều hành khác, bạn có thể bị mắc kẹt.

0

Tôi cũng đã làm việc trên một sản phẩm mà một lập trình viên kế thừa (người may mắn đã qua đời) cũng có một mối tình đặc biệt với Macros. Ngôn ngữ kịch bản 'tùy chỉnh' của anh ta là chiều cao của sự cẩu thả. Điều này được kết hợp bởi thực tế là ông đã viết các lớp C++ của mình trong C, có nghĩa là tất cả các hàm và biến của lớp đều được công khai. Dù sao, ông đã viết gần như tất cả mọi thứ trong chức năng vĩ mô và variadic (Một quái vật gớm ghiếc foisted trên thế giới). Vì vậy, thay vì viết một lớp mẫu thích hợp, ông sẽ sử dụng một Macro thay vào đó! Anh ta cũng dùng đến các macro để tạo ra các lớp nhà máy, thay vì mã bình thường ... Mã của anh ta khá là không thể tin được. Từ những gì tôi đã thấy, macro có thể được sử dụng khi chúng nhỏ và được sử dụng theo kiểu khai báo và không chứa các phần chuyển động như vòng lặp và các biểu thức luồng chương trình khác. OK nếu macro là một hoặc nhiều nhất là hai dòng và nó khai báo và thể hiện một thứ gì đó. Cái gì đó sẽ không phá vỡ trong thời gian chạy. Ngoài ra, macro không được chứa định nghĩa lớp hoặc định nghĩa chức năng. Nếu macro chứa mã cần được bước vào bằng cách sử dụng trình gỡ lỗi hơn macro cần được loại bỏ và thay thế bằng macro khác.

Chúng cũng có thể hữu ích cho việc gói chức năng theo dõi/gỡ lỗi tùy chỉnh. Ví dụ: bạn muốn theo dõi tùy chỉnh trong bản dựng gỡ lỗi nhưng không phát hành bản dựng.

Dù sao khi bạn đang làm việc trong mã cũ như vậy, chỉ cần chắc chắn loại bỏ một chút lộn xộn macro một chút tại một thời điểm. Nếu bạn giữ nó lên, với đủ thời gian cuối cùng bạn sẽ loại bỏ tất cả chúng và làm cho cuộc sống dễ dàng hơn một chút cho chính mình. Tôi đã làm điều này trong quá khứ, với đặc biệt là lộn xộn vĩ mô của.Những gì tôi làm là bật công tắc trình biên dịch để có bộ tiền xử lý tạo ra một tệp đầu ra. Sau đó, tôi tấn công tệp đó và sao chép mã, thụt lại nó và thay thế macro bằng mã được tạo. Cảm ơn lòng tốt cho tính năng trình biên dịch đó.

0

Một số mã cũ tôi đã làm việc với các macro đã sử dụng rất rộng rãi ở vị trí của các phương pháp. Lý do là máy tính/hệ điều hành/thời gian chạy có một ngăn xếp cực kỳ nhỏ, để ngăn xếp ngăn xếp là một vấn đề phổ biến. Sử dụng macro thay vì phương pháp có nghĩa là có ít phương pháp hơn trên ngăn xếp.

May mắn thay, hầu hết mã đó đã lỗi thời, vì vậy hiện tại (hầu hết) đã biến mất.

+0

Tôi đoán các trình biên dịch trở lại sau đó không phải là quá tốt tại nội tuyến. Ngày nay, các hàm nội tuyến sẽ giải quyết vấn đề này. – ugoren

+0

@ugoren Vâng, điều này chắc chắn là không cần thiết nữa. Mặt khác, tôi cũng không thể chỉ đơn giản là thay đổi các macro thành các phương thức, vì các macro thường sử dụng các biến được khai báo trong các macro "bên ngoài" hoặc trong phương thức sử dụng chúng ... * facepalm * –

1

Có một số lý do chính đáng để viết macro trong C.

Một số trong những quan trọng nhất là để tạo bảng cấu hình sử dụng x-macro, để làm chức năng giống như các macro có thể chấp nhận nhiều loại tham số như đầu vào và chuyển đổi các bảng từ các giá trị có thể đọc/định cấu hình/dễ hiểu của con người sang các giá trị được sử dụng trên máy tính.

Tôi thực sự không thể thấy lý do để mọi người viết các macro rất dài, ngoại trừ chức năng tự động lịch sử nội dòng.

tôi sẽ nói rằng khi gỡ lỗi macro phức tạp, (khi viết X macro vv) Tôi có xu hướng xử lý trước các file nguồn và thay thế các tập tin xử lý trước cho bản gốc.

Điều này cho phép bạn xem mã C được tạo và cung cấp cho bạn các dòng thực để làm việc với trong trình gỡ rối.

0

C89 không có chức năng nội dòng. Nếu sử dụng một trình biên dịch với các phần mở rộng bị vô hiệu hóa (đó là một điều mong muốn để làm vì nhiều lý do), thì macro có thể là tùy chọn duy nhất.

Mặc dù C99 xuất hiện vào năm 1999 nhưng đã có sự kháng cự trong một thời gian dài; các nhà cung cấp trình biên dịch thương mại không cảm thấy nó xứng đáng với thời gian của họ để triển khai C99. Một số (ví dụ: MS) vẫn chưa có. Vì vậy, đối với nhiều công ty nó không phải là một quyết định thực tế khả thi để sử dụng chế độ phù hợp C99, thậm chí đến ngày hôm nay trong trường hợp của một số trình biên dịch.

Tôi đã sử dụng các trình biên dịch C89 có phần mở rộng cho các hàm nội tuyến, nhưng phần mở rộng bị lỗi (ví dụ: nhiều lỗi định nghĩa khi không có), những thứ như vậy có thể ngăn cản một lập trình viên sử dụng các hàm nội tuyến.

Một điều nữa là phiên bản macro có hiệu lực buộc rằng chức năng thực sự sẽ được gạch chân. Từ khóa C99 inline chỉ là một gợi ý của trình biên dịch và trình biên dịch vẫn có thể quyết định tạo ra một cá thể duy nhất của mã chức năng được liên kết như một hàm không nội tuyến. (Một trình biên dịch mà tôi vẫn sử dụng sẽ làm điều này nếu hàm này không nhỏ và trả về void).

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