Có phải thực tế xấu khi sử dụng trình biên dịch C ++ chỉ để nạp chồng hàm?


60

Vì vậy, tôi đang làm việc trên một thiết kế phần mềm sử dụng C cho một bộ xử lý nhất định. Bộ công cụ bao gồm khả năng biên dịch C cũng như C ++. Đối với những gì tôi đang làm, không có phân bổ bộ nhớ động có sẵn trong môi trường này và chương trình nói chung khá đơn giản. Chưa kể rằng thiết bị gần như không có sức mạnh hoặc tài nguyên của bộ xử lý. Thực sự không có nhu cầu mạnh mẽ để sử dụng bất kỳ C ++ nào.

Điều đó đang được nói, có một vài nơi tôi thực hiện chức năng nạp chồng (một tính năng của C ++). Tôi cần gửi một vài loại dữ liệu khác nhau và không cảm thấy muốn sử dụng printfđịnh dạng kiểu với một số loại đối số %s(hoặc bất cứ điều gì). Tôi đã thấy một số người không có quyền truy cập vào trình biên dịch C ++ printf, nhưng trong trường hợp của tôi, hỗ trợ C ++ có sẵn.

Bây giờ tôi chắc chắn rằng tôi có thể nhận được câu hỏi tại sao tôi cần quá tải một chức năng để bắt đầu. Vì vậy, tôi sẽ cố gắng trả lời điều đó ngay bây giờ. Tôi cần truyền các loại dữ liệu khác nhau ra một cổng nối tiếp để tôi có một vài tình trạng quá tải truyền các loại dữ liệu sau:

unsigned char*
const char*
unsigned char
const char

Tôi chỉ muốn không có một phương thức xử lý tất cả những điều này. Khi tôi gọi hàm tôi chỉ muốn nó truyền ra cổng nối tiếp, tôi không có nhiều tài nguyên nên tôi không muốn thực hiện BẤT K but việc truyền tải nào.

Một người khác đã xem chương trình của tôi và hỏi tôi, "tại sao bạn lại sử dụng tệp CPP?" Vì vậy, đó là lý do duy nhất của tôi. Đó có phải là thực hành xấu?

Cập nhật

Tôi muốn giải quyết một số câu hỏi được hỏi:

Một câu trả lời khách quan cho tình huống khó xử của bạn sẽ phụ thuộc vào:

  1. Liệu kích thước của tệp thực thi có tăng đáng kể hay không nếu bạn sử dụng C ++.

Tính đến thời điểm hiện tại, kích thước của tệp thực thi sẽ tiêu tốn 4,0% bộ nhớ chương trình (trong số 5248 byte) và 8,3% bộ nhớ dữ liệu (trong số 342 byte). Đó là, biên dịch cho C ++ ... Tôi không biết nó sẽ như thế nào đối với C vì tôi đã không sử dụng trình biên dịch C. Tôi biết rằng chương trình này sẽ không phát triển nữa, vì vậy, giới hạn tài nguyên như thế nào tôi sẽ nói rằng tôi ổn ở đó ...

  1. Cho dù có bất kỳ tác động tiêu cực đáng chú ý nào đến hiệu suất nếu bạn sử dụng C ++.

Chà, nếu có, tôi đã không nhận thấy bất cứ điều gì ... nhưng một lần nữa, đó có thể là lý do tại sao tôi hỏi câu hỏi này vì tôi không hiểu đầy đủ.

  1. Liệu mã có thể được sử dụng lại trên một nền tảng khác không, chỉ có trình biên dịch C có sẵn.

Tôi biết rằng câu trả lời cho điều này chắc chắn là không . Chúng tôi thực sự đang xem xét chuyển sang một bộ xử lý khác, nhưng chỉ những bộ xử lý dựa trên ARM mạnh hơn (tất cả những gì tôi biết thực tế đều có chuỗi công cụ biên dịch C ++).


59
Tôi đã được biết là sử dụng C ++ cho một dự án chỉ sử dụng các tính năng C để tôi có thể có //ý kiến. Nếu nó hoạt động, tại sao không?
Jules

76
Thực tiễn xấu sẽ hạn chế bạn đến C khi bạn sử dụng tốt các tính năng mà nó không cung cấp.
Jerry Coffin

35
Khi bạn nói "sử dụng trình biên dịch C ++", bạn có nghĩa là "sử dụng C ++". Cứ nói ra. Bạn không thể biên dịch C bằng trình biên dịch C ++, nhưng bạn có thể dễ dàng chuyển từ C sang C ++, đó là những gì bạn thực sự đang làm.
dùng253751

4
"Đối với những gì tôi đang làm, không có phân bổ bộ nhớ động trên bộ xử lý và chương trình nói chung khá đơn giản. Chưa kể rằng thiết bị gần như không có sức mạnh hoặc tài nguyên của bộ xử lý. Thực sự không cần sử dụng bất kỳ C ++ nào." Tôi hy vọng câu đầu tiên trong hai câu đó được cho là lý do để không sử dụng C ++, vì chúng là những câu khá tệ nếu có. C ++ hoàn toàn tốt để sử dụng với các hệ thống nhúng.
Pharap

21
@Jules Tôi chắc chắn bạn biết điều này và đã suy nghĩ lại một thời gian, nhưng trong trường hợp ai đó đọc nó thì không: các //bình luận đã ở trong tiêu chuẩn C kể từ C99.
Davislor

Câu trả lời:


77

Tôi sẽ không đi xa như vậy để gọi nó là "thực hành xấu" cho mỗi gia nhập , nhưng không phải là tôi thuyết phục nó thực sự là giải pháp đúng cho vấn đề của bạn. Nếu tất cả những gì bạn muốn là bốn hàm riêng biệt để thực hiện bốn loại dữ liệu của bạn, tại sao không phải là những gì lập trình viên C đã làm từ thời xa xưa:

void transmit_uchar_buffer(unsigned char *buffer);
void transmit_char_buffer(char *buffer);
void transmit_uchar(unsigned char c);
void transmit_char(char c);

Đó thực sự là những gì trình biên dịch C ++ đang thực hiện phía sau hậu trường và nó không phải là vấn đề quá lớn đối với lập trình viên. Tránh tất cả các vấn đề về "tại sao bạn viết không hoàn toàn bằng trình biên dịch C ++" và có nghĩa là không ai khác trong dự án của bạn sẽ bị nhầm lẫn bởi các bit nào của C ++ được "cho phép" và bit nào không được phép.


8
Tôi muốn nói tại sao không phải là vì loại được truyền là (có thể) là một chi tiết triển khai, vì vậy việc ẩn nó và để trình biên dịch xử lý việc chọn triển khai cũng có thể dẫn đến mã dễ đọc hơn. Và nếu sử dụng tính năng C ++ giúp cải thiện khả năng đọc thì tại sao bạn không làm điều đó?
Jules

29
Một người thậm chí có thể #definetruyền () bằng C11_Generic
Ded repeatator

16
@Jules Bởi vì nó rất khó hiểu về những tính năng C ++ nào được phép sử dụng trong dự án. Một thay đổi tiềm năng có chứa các đối tượng sẽ bị từ chối? Còn mẫu thì sao? Ý kiến ​​phong cách C ++? Chắc chắn, bạn có thể làm việc xung quanh điều đó với một tài liệu kiểu mã hóa, nhưng nếu tất cả những gì bạn đang làm là một trường hợp đơn giản là quá tải chức năng, thì chỉ cần viết C thay thế.
Philip Kendall

25
@Phillip "Nhận xét kiểu C ++" đã có hiệu lực C trong hơn một thập kỷ.
David Conrad

12
@Jules: trong khi loại được truyền có thể là một chi tiết triển khai cho phần mềm tạo báo giá bảo hiểm, ứng dụng OP có vẻ là một hệ thống nhúng thực hiện giao tiếp nối tiếp, trong đó loại và dữ liệu có tầm quan trọng đáng kể.
whatsisname

55

Chỉ sử dụng một số tính năng của C ++ trong khi nếu không coi nó là C không thực sự phổ biến, nhưng cũng không chính xác. Trên thực tế, một số người thậm chí không sử dụng bất kỳ tính năng nào trong C ++, ngoại trừ việc kiểm tra loại nghiêm ngặt hơn và mạnh mẽ hơn. Họ chỉ đơn giản viết C (chú ý chỉ viết trong giao điểm chung của C ++ và C), sau đó biên dịch với trình biên dịch C ++ để kiểm tra kiểu và trình biên dịch C để tạo mã (hoặc chỉ gắn bó với trình biên dịch C ++).

Linux là một ví dụ về một cơ sở mã, nơi mọi người thường hỏi rằng các định danh như classđược đổi tên thành klasshoặc kclassđể họ có thể biên dịch Linux bằng trình biên dịch C ++. Rõ ràng, theo ý kiến ​​của Linus về C ++ , họ luôn bị bắn hạ :-D GCC là một ví dụ về một cơ sở mã lần đầu tiên được chuyển đổi thành "C ++ - C sạch", và sau đó dần dần được tái cấu trúc để sử dụng nhiều tính năng C ++ hơn.

Không có gì sai với những gì bạn đang làm. Nếu bạn thực sự hoang tưởng về chất lượng tạo mã của trình biên dịch C ++, bạn có thể sử dụng trình biên dịch như Comeau C ++, biên dịch thành C làm ngôn ngữ đích, sau đó sử dụng trình biên dịch C. Bằng cách đó, bạn cũng có thể thực hiện kiểm tra tại chỗ mã để xem liệu sử dụng C ++ có tiêm bất kỳ mã nhạy cảm hiệu năng không lường trước nào không. Tuy nhiên, đó không phải là trường hợp quá tải, mà theo nghĩa đen chỉ là tự động tạo ra các hàm được đặt tên khác nhau - IOW, chính xác là những gì bạn sẽ làm trong C anyway.


15

Một câu trả lời khách quan cho tình huống khó xử của bạn sẽ phụ thuộc vào:

  1. Liệu kích thước của tệp thực thi có tăng đáng kể hay không nếu bạn sử dụng C ++.
  2. Cho dù có bất kỳ tác động tiêu cực đáng chú ý nào đến hiệu suất nếu bạn sử dụng C ++.
  3. Liệu mã có thể được sử dụng lại trên một nền tảng khác không, chỉ có trình biên dịch C có sẵn.

Nếu câu trả lời cho bất kỳ câu hỏi nào là "có", bạn có thể tốt hơn là tạo các hàm được đặt tên khác nhau cho các loại dữ liệu khác nhau và gắn bó với C.

Nếu câu trả lời cho tất cả các câu hỏi là "không", tôi thấy không có lý do gì bạn không nên sử dụng C ++.


9
Tôi phải nói rằng tôi chưa bao giờ gặp phải trình biên dịch C ++ tạo mã kém hơn đáng kể so với trình biên dịch C cho mã được viết trong tập hợp con của hai ngôn ngữ. Trình biên dịch C ++ có một đại diện xấu cho cả kích thước và hiệu suất, nhưng kinh nghiệm của tôi là việc sử dụng các tính năng C ++ không phù hợp gây ra sự cố ... Đặc biệt, nếu bạn quan tâm đến kích thước, đừng sử dụng iostream và không sử dụng mẫu, nhưng nếu không bạn sẽ ổn.
Jules

@Jules: Chỉ với giá trị của nó (không nhiều, IMO) Tôi đã thấy những gì được bán dưới dạng một trình biên dịch duy nhất cho C và C ++ (Turbo C ++ 1.0, nếu bộ nhớ phục vụ) tạo ra kết quả khác nhau đáng kể cho đầu vào giống hệt nhau. Tuy nhiên, theo tôi hiểu, đây là trước khi họ hoàn thành trình biên dịch C ++ của riêng mình, vì vậy mặc dù nó trông giống như một trình biên dịch ở bên ngoài, nhưng nó thực sự có hai trình biên dịch hoàn toàn riêng biệt - một cho C, một cho C ++.
Jerry Coffin

1
@JerryCoffin Nếu bộ nhớ phục vụ, các sản phẩm Turbo chưa bao giờ có danh tiếng lớn. Và nếu đó là phiên bản 1.0, bạn có thể được miễn là không được tinh chỉnh cao. Vì vậy, nó có thể không đại diện lắm.
Barmar

10
@Barmar: Thật ra, họ đã có một danh tiếng khá tốt trong một thời gian dài. Danh tiếng kém của họ bây giờ chủ yếu là do tuổi tuyệt đối của họ. Họ cạnh tranh với các trình biên dịch khác cùng loại - nhưng không ai đăng câu hỏi về cách thực hiện với gcc 1.4 (hoặc bất cứ điều gì). Nhưng bạn nói đúng - nó không mang tính đại diện.
Jerry Coffin

1
@Jules Tôi cho rằng ngay cả các mẫu cũng tốt. Mặc dù lý thuyết phổ biến, việc khởi tạo một mẫu không hoàn toàn tăng kích thước mã, kích thước mã thường sẽ không tăng cho đến khi một chức năng từ một mẫu được sử dụng (trong trường hợp tăng kích thước sẽ tỷ lệ thuận với kích thước chức năng) hoặc trừ khi mẫu là khai báo biến tĩnh. Các quy tắc nội tuyến vẫn được áp dụng, do đó có thể viết các mẫu trong đó tất cả các hàm được trình biên dịch nội tuyến.
Pharap

13

Bạn đặt ra câu hỏi như thể biên dịch với C ++ chỉ đơn giản là cung cấp cho bạn quyền truy cập vào nhiều tính năng hơn. Đây không phải là trường hợp. Biên dịch với trình biên dịch C ++ có nghĩa là mã nguồn của bạn được hiểu là nguồn C ++ và C ++ là một ngôn ngữ khác với C. Cả hai có một tập hợp con đủ lớn để lập trình một cách hữu ích, nhưng mỗi cái đều có các tính năng mà cái kia thiếu, và nó là có thể viết mã được chấp nhận trong cả hai ngôn ngữ nhưng được giải thích khác nhau.

Nếu thực sự tất cả những gì bạn muốn là quá tải chức năng, thì tôi thực sự không thấy vấn đề gây nhầm lẫn bằng cách đưa C ++ vào đó. Thay vì các hàm khác nhau có cùng tên, hãy phân biệt danh sách tham số của chúng, chỉ cần viết các hàm với các tên khác nhau.

Đối với tiêu chí khách quan của bạn,

  1. Liệu kích thước của tệp thực thi có tăng đáng kể hay không nếu bạn sử dụng C ++.

Tệp thực thi có thể lớn hơn một chút khi được biên dịch thành C ++, liên quan đến mã C tương tự được xây dựng như vậy. Tối thiểu, tệp thực thi C ++ sẽ có lợi ích đáng ngờ của việc xáo trộn tên C ++ cho tất cả các tên hàm, đến mức mà bất kỳ ký hiệu nào được giữ lại trong tệp thực thi. (Và đó thực tế là những gì cung cấp cho sự quá tải của bạn ở nơi đầu tiên.) Sự khác biệt có đủ lớn để trở nên quan trọng đối với bạn hay không là điều bạn sẽ phải xác định bằng thử nghiệm.

  1. Cho dù có bất kỳ tác động tiêu cực đáng chú ý nào đến hiệu suất nếu bạn sử dụng C ++.

Tôi nghi ngờ bạn sẽ thấy một sự khác biệt hiệu suất đáng chú ý cho mã bạn mô tả so với một tương tự thuần C-giả thuyết.

  1. Liệu mã có thể được sử dụng lại trên một nền tảng khác không, chỉ có trình biên dịch C có sẵn.

Tôi sẽ đưa ra một chút khác biệt về điều này: nếu bạn muốn liên kết mã bạn đang xây dựng với C ++ với mã khác , thì mã đó cũng sẽ cần phải được viết bằng C ++ và được xây dựng bằng C ++, hoặc bạn sẽ cần để cung cấp đặc biệt trong mã của riêng bạn (khai báo liên kết "C"), ngoài ra, bạn hoàn toàn không thể thực hiện các chức năng quá tải của mình. Mặt khác, nếu mã của bạn được viết bằng C và được biên dịch thành C, thì những người khác có thể liên kết nó với cả hai mô-đun C và C ++. Loại vấn đề này thường có thể được khắc phục bằng cách xoay các bảng, nhưng vì bạn thực sự không cần C ++, tại sao lại chấp nhận vấn đề như vậy ngay từ đầu?


4
"Tệp thực thi có thể lớn hơn một chút khi được biên dịch thành C ++ ... đến mức bất kỳ ký hiệu nào được giữ lại trong tệp thực thi." Mặc dù vậy, công bằng mà nói, bất kỳ công cụ tối ưu hóa tốt nào cũng nên có tùy chọn liên kết để loại bỏ các biểu tượng này trong các bản dựng "phát hành", điều đó có nghĩa là chúng chỉ làm mờ các bản dựng gỡ lỗi của bạn. Tôi không nghĩ đó là một mất mát lớn. Trong thực tế, nó thường là một lợi ích.
Cody Grey

5

Ngày trước, sử dụng trình biên dịch C ++ như một Cv tốt hơn đã được quảng bá như một trường hợp sử dụng. Trên thực tế, C ++ ban đầu chính xác là như vậy. Nguyên tắc thiết kế cơ bản là bạn chỉ có thể sử dụng các tính năng bạn muốn và sẽ không phải chịu chi phí cho các tính năng bạn không sử dụng. Vì vậy, bạn có thể quá tải các hàm (bằng cách khai báo ý định thông qua overloadtừ khóa!) Và phần còn lại của dự án của bạn không chỉ sẽ biên dịch tốt mà còn tạo mã không tệ hơn trình biên dịch C sẽ tạo ra.

Kể từ đó, các ngôn ngữ đã chuyển hướng phần nào.

Mỗi mallocmã C của bạn sẽ là một lỗi không khớp loại - không phải là vấn đề trong trường hợp của bạn không có bộ nhớ động! Tương tự như vậy, tất cả các voidcon trỏ của bạn trong C sẽ làm bạn vấp ngã, vì bạn sẽ phải thêm các diễn viên rõ ràng. Nhưng tại sao bạn lại làm điều đó theo cách mà bạn sẽ được dẫn xuống con đường sử dụng các tính năng C ++ ngày càng nhiều.

Vì vậy, nó có thể là có thể với một số công việc thêm. Nhưng nó sẽ đóng vai trò là cửa ngõ cho việc áp dụng C ++ quy mô lớn hơn. Trong một vài năm, mọi người sẽ phàn nàn về mã kế thừa của bạn trông giống như được viết vào năm 1989, khi họ thay thế các malloccuộc gọi của bạn bằng cách newtách ra các khối mã của các vòng lặp để chỉ gọi một thuật toán thay vào đó, và điều chỉnh tính đa hình giả không an toàn sẽ xảy ra đã được tầm thường khi trình biên dịch được phép làm điều đó.

Mặt khác, bạn biết rằng tất cả đều giống như bạn đã viết nó bằng C, vậy có bao giờ sai khi viết bằng C thay vì C ++ tồn tại không? Nếu câu trả lời là Số không, thì việc sử dụng các tính năng được chọn từ C ++ cũng không thể sai .


2

Nhiều ngôn ngữ lập trình có một hoặc nhiều nền văn hóa phát triển xung quanh chúng, và có những ý tưởng cụ thể về ngôn ngữ được cho là "được". Mặc dù có thể, thực tế và hữu ích để có một ngôn ngữ cấp thấp phù hợp với lập trình hệ thống, làm tăng thêm một phương ngữ C phù hợp với mục đích đó với một số tính năng từ C ++ cũng phù hợp, các nền văn hóa xung quanh hai ngôn ngữ không ' t đặc biệt ủng hộ việc sáp nhập như vậy.

Tôi đã đọc một trình biên dịch C nhúng được tích hợp một số tính năng từ C ++, bao gồm khả năng có

foo.someFunction(a,b,c);

được hiểu là, về cơ bản

typeOfFoo_someFunction(foo, a, b, c);

cũng như khả năng quá tải các hàm tĩnh (vấn đề xáo trộn tên chỉ phát sinh khi xuấtchức năng bị quá tải). Không có lý do gì trình biên dịch C không thể hỗ trợ chức năng đó mà không áp đặt bất kỳ yêu cầu bổ sung nào đối với môi trường liên kết và thời gian chạy, nhưng nhiều nhà soạn thảo C từ chối phản xạ gần như mọi thứ từ C ++ vì muốn tránh gánh nặng môi trường từ chối ngay cả các tính năng sẽ áp đặt không có chi phí như vậy. Trong khi đó, văn hóa của C ++ ít quan tâm đến bất kỳ môi trường nào không thể hỗ trợ tất cả các tính năng của ngôn ngữ đó. Trừ khi hoặc cho đến khi văn hóa C thay đổi để chấp nhận các tính năng đó hoặc văn hóa của C ++ thay đổi để khuyến khích triển khai các tập hợp con nhẹ, mã "lai" có thể chịu nhiều hạn chế của cả hai ngôn ngữ trong khi bị hạn chế về khả năng gặt hái những lợi ích của việc vận hành trong một superset kết hợp.

Nếu một nền tảng hỗ trợ cả C và C ++, mã thư viện bổ sung cần thiết để hỗ trợ C ++ có thể sẽ khiến phần thực thi có phần lớn hơn, mặc dù hiệu ứng sẽ không đặc biệt lớn. Việc thực thi "tính năng C" trong C ++ có thể sẽ không bị ảnh hưởng đặc biệt. Một vấn đề lớn hơn có thể là cách tối ưu hóa C và C ++ xử lý các trường hợp góc khác nhau. Hành vi của các kiểu dữ liệu trong C hầu hết được xác định bởi nội dung của các bit được lưu trữ trong đó và C ++ có một loại cấu trúc được công nhận (PODS - Cấu trúc dữ liệu cũ đơn giản) có ngữ nghĩa được xác định tương tự bởi các bit được lưu trữ. Tuy nhiên, cả hai tiêu chuẩn C và C ++, đôi khi cho phép hành vi trái với hành vi được ngụ ý bởi các bit được lưu trữ và ngoại lệ đối với hành vi "bit được lưu trữ" khác nhau giữa hai ngôn ngữ.


1
Ý kiến ​​thú vị nhưng không giải quyết câu hỏi của OP.
R Sahu

@RSahu: Mã phù hợp với "văn hóa" xung quanh một ngôn ngữ có thể được hỗ trợ tốt hơn và dễ dàng thích nghi hơn với nhiều cách triển khai hơn so với mã không có. Các nền văn hóa của C và C ++ đều không thích cách sử dụng mà OP gợi ý, tôi sẽ thêm một số điểm cụ thể hơn liên quan đến các câu hỏi cụ thể.
supercat

1
Lưu ý rằng có một nhóm nhúng / tài chính / trò chơi trong ủy ban tiêu chuẩn C ++ nhằm giải quyết chính xác loại tình huống "ít quan tâm" mà bạn cho là bị loại bỏ.
Yakk

@Yakk: Tôi sẽ thú nhận rằng tôi đã không theo dõi thế giới C ++ một cách khủng khiếp, vì sở thích của tôi chủ yếu nằm ở C; Tôi biết đã có những nỗ lực đối với các phương ngữ được nhúng nhiều hơn, nhưng tôi đã không nghĩ rằng chúng thực sự đã đi đến bất cứ đâu.
supercat

2

Một yếu tố quan trọng khác để xem xét: bất cứ ai sẽ kế thừa mã của bạn.

Có phải người đó sẽ luôn là một lập trình viên C với trình biên dịch C? Tôi cho là như vậy.

Người đó cũng sẽ là một lập trình viên C ++ với trình biên dịch C ++ chứ? Tôi muốn chắc chắn một cách hợp lý về điều này trước khi làm cho mã của tôi phụ thuộc vào những thứ cụ thể của C ++.


2

Đa hình là một tính năng thực sự tốt mà C ++ cung cấp miễn phí. Tuy nhiên, có những khía cạnh khác mà bạn có thể cần xem xét khi chọn trình biên dịch. Có một sự thay thế cho đa hình trong C nhưng việc sử dụng nó có thể gây ra một số lông mày mọc lên. Đó là chức năng matrixdic , vui lòng tham khảo hướng dẫn chức năng Variadic .

Trong trường hợp của bạn, nó sẽ giống như

enum serialDataTypes
{
 INT =0,
 INT_P =1,
 ....
 }Arg_type_t;
....
....
void transmit(Arg_type_t serial_data_type, ...)
{
  va_list args;
  va_start(args, serial_data_type);

  switch(serial_data_type)
  {
    case INT: 
    //Send integer
    break;

    case INT_P:
    //Send data at integer pointer
    break;
    ...
   }
 va_end(args);
}

Tôi thích cách tiếp cận của Phillips nhưng nó tạo ra thư viện của bạn với rất nhiều cuộc gọi. Với các giao diện trên là sạch sẽ. Nó có nhược điểm của nó và cuối cùng là vấn đề lựa chọn.


Các hàm biến thể chủ yếu phục vụ trường hợp sử dụng trong đó cả số lượng đối số kiểu của chúng là biến. Mặt khác, bạn không cần nó, và đặc biệt, nó là quá mức cần thiết cho ví dụ cụ thể của bạn. Sẽ đơn giản hơn khi sử dụng một hàm thông thường chấp nhận Arg_type_tvoid *trỏ vào dữ liệu. Như đã nói, có, đưa ra hàm (đơn) một đối số chỉ ra kiểu dữ liệu thực sự là một sự thay thế khả thi.
John Bollinger

1

Đối với trường hợp cụ thể của bạn về việc quá tải tĩnh một "hàm" cho một vài loại khác nhau, bạn có thể cân nhắc thay vì chỉ sử dụng C11 với máy móc _Genericmacro của nó . Tôi cảm thấy nó có thể là đủ cho nhu cầu hạn chế của bạn.

Sử dụng câu trả lời của Philip Kendall, bạn có thể xác định:

#define transmit(X) _Generic((X),      \
   unsigned char*: transmit_uchar_buffer, \
   char*: transmit_char_buffer,           \
   unsigned char: transmit_uchar,         \
   char: transmit_char) ((X))

và mã transmit(foo) bất cứ điều gì là loại foo(trong số bốn loại được liệt kê ở trên).

Nếu bạn chỉ quan tâm đến trình biên dịch GCC (và tương thích, ví dụ Clang ), bạn có thể xem xét phần mở rộng __builtin_types_compatible_pcủa nó typeof.


1

Có phải thực tế xấu khi sử dụng trình biên dịch C ++ chỉ để nạp chồng hàm?

Quan điểm của IMHO, vâng, và tôi sẽ cần phải trở thành tâm thần phân liệt để trả lời câu hỏi này vì tôi yêu cả hai ngôn ngữ nhưng nó không liên quan gì đến hiệu quả, mà giống như sử dụng ngôn ngữ an toàn và thành ngữ.

Bên C

Từ quan điểm C, tôi thấy thật lãng phí khi làm cho mã của bạn yêu cầu C ++ chỉ để sử dụng chức năng nạp chồng. Trừ khi bạn đang sử dụng nó cho đa hình tĩnh với các mẫu C ++, thì đó là loại đường cú pháp tầm thường như vậy để đổi lấy việc chuyển sang một ngôn ngữ hoàn toàn khác. Hơn nữa nếu bạn muốn xuất các chức năng của mình sang một dylib (có thể hoặc không phải là mối quan tâm thực tế), bạn không còn có thể thực hiện điều đó một cách thực tế để tiêu thụ rộng rãi với tất cả các biểu tượng được xáo trộn.

Bên C ++

Từ quan điểm C ++, bạn không nên sử dụng C ++ như C với chức năng nạp chồng. Đây không phải là chủ nghĩa giáo điều cách điệu mà là một liên quan đến việc sử dụng thực tế C ++ hàng ngày.

Loại mã C thông thường của bạn chỉ hợp lý và "an toàn" để viết nếu bạn đang làm việc chống lại hệ thống loại C cấm những thứ như sao chép trong structs. Khi bạn đang làm việc trong hệ thống loại phong phú hơn nhiều của C ++, các chức năng hàng ngày có giá trị rất lớn memsetmemcpykhông trở thành chức năng bạn nên dựa vào mọi lúc. Thay vào đó, chúng là các hàm mà bạn thường muốn tránh như bệnh dịch hạch, vì với các loại C ++, bạn không nên coi chúng như các bit và byte thô được sao chép và xáo trộn xung quanh và giải phóng. Ngay cả khi mã của bạn chỉ sử dụng những thứ như memsettrên nguyên thủy và POD UDT tại thời điểm này, thời điểm này, bất cứ ai cũng thêm một ctor vào bất kỳ UDT nào bạn sử dụng (bao gồm cả việc thêm một thành viên cần một, nhưstd::unique_ptrthành viên) chống lại các chức năng đó hoặc một chức năng ảo hoặc bất cứ thứ gì thuộc loại đó, nó làm cho tất cả các mã hóa kiểu C thông thường của bạn dễ bị hành vi không xác định. Lấy nó từ Herb Sutter mình:

memcpymemcmpvi phạm hệ thống loại. Sử dụng memcpyđể sao chép các đối tượng cũng giống như kiếm tiền bằng máy photocopy. Sử dụng memcmpđể so sánh các đối tượng cũng giống như so sánh báo đốm bằng cách đếm các điểm của chúng. Các công cụ và phương pháp có thể xuất hiện để thực hiện công việc, nhưng chúng quá thô để có thể chấp nhận nó. Các đối tượng C ++ là tất cả về việc che giấu thông tin (được cho là nguyên tắc có lợi nhất trong công nghệ phần mềm; xem Mục 11): Các đối tượng ẩn dữ liệu (xem Mục 41) và đưa ra các tóm tắt chính xác để sao chép dữ liệu đó thông qua các hàm tạo và toán tử gán (xem Mục 52 đến 55) . Việc xóa bỏ tất cả những gì memcpyvi phạm nghiêm trọng về việc che giấu thông tin và thường dẫn đến rò rỉ bộ nhớ và tài nguyên (tốt nhất), sự cố (tệ hơn) hoặc hành vi không xác định (tệ nhất) - Tiêu chuẩn mã hóa C ++.

Vì vậy, nhiều nhà phát triển C sẽ không đồng ý với điều này và đúng như vậy, vì triết lý chỉ áp dụng nếu bạn đang viết mã bằng C ++. Bạn rất có thể được viết mã rất có vấn đề nếu bạn sử dụng các chức năng như memcpytất cả thời gian trong mã đó được xây dựng như C ++ , nhưng nó hoàn toàn tốt đẹp nếu bạn làm điều đó trong C . Hai ngôn ngữ rất khác nhau về vấn đề này vì sự khác biệt trong hệ thống loại. Thật là hấp dẫn khi nhìn vào tập hợp các tính năng mà hai tính năng này có điểm chung và tin rằng một cái có thể được sử dụng như cái kia, đặc biệt là về phía C ++, nhưng mã C + (hoặc mã C--) nói chung có vấn đề hơn nhiều so với cả C và Mã C ++.

Tương tự như vậy, bạn không nên sử dụng, malloctrong bối cảnh kiểu C (ngụ ý không có EH) nếu nó có thể gọi bất kỳ hàm C ++ nào có thể ném trực tiếp, từ đó bạn có một điểm thoát ngầm trong hàm do kết quả của ngoại lệ mà bạn không thể bắt được bằng cách viết mã kiểu C, trước khi có thể vào freebộ nhớ đó. Vì vậy, bất cứ khi nào bạn có một tập tin đó được xây dựng như C ++ với một .cppphần mở rộng hoặc bất cứ điều gì và nó làm tất cả các loại của những thứ như malloc, memcpy, memset,qsort, v.v., sau đó, nó sẽ yêu cầu các vấn đề tiếp theo nếu không phải là chi tiết triển khai của một lớp chỉ hoạt động với các kiểu nguyên thủy, tại thời điểm đó nó vẫn cần xử lý ngoại lệ để an toàn ngoại lệ. Nếu bạn đang viết mã C ++ bạn thay vì muốn thường dựa vào RAII và sử dụng những thứ như vector, unique_ptr, shared_ptr, vv, và tránh tất cả các C-phong cách bình thường mã hóa khi có thể.

Lý do bạn có thể chơi với lưỡi dao cạo trong các loại dữ liệu C và tia X và chơi với bit và byte của chúng mà không dễ gây ra thiệt hại tài sản thế chấp trong một đội (mặc dù bạn vẫn có thể thực sự làm tổn thương chính mình) không phải vì C loại có thể làm, nhưng vì những gì họ sẽ không bao giờ có thể làm. Khoảnh khắc bạn mở rộng hệ thống loại C để bao gồm các tính năng C ++ như ctor, dtor và vtables, cùng với xử lý ngoại lệ, tất cả mã C thành ngữ sẽ được hiển thị xa hơn, nguy hiểm hơn nhiều so với hiện tại và bạn sẽ thấy một loại mới triết lý và tư duy phát triển sẽ khuyến khích một phong cách mã hóa hoàn toàn khác, như bạn thấy trong C ++, hiện đang xem xét ngay cả việc sử dụng một con trỏ thô sơ cho một lớp quản lý bộ nhớ trái ngược với tài nguyên tuân thủ RAII như unique_ptr. Tư duy đó đã không phát triển từ một cảm giác an toàn tuyệt đối. Nó phát triển từ những gì C ++ cần đặc biệt an toàn trước các tính năng như xử lý ngoại lệ dựa trên những gì nó chỉ cho phép thông qua hệ thống loại của nó.

Ngoại lệ-An toàn

Một lần nữa, thời điểm bạn ở trong vùng đất C ++, mọi người sẽ mong đợi mã của bạn là ngoại lệ an toàn. Mọi người có thể duy trì mã của bạn trong tương lai, vì nó đã được viết và biên dịch bằng C ++ và chỉ cần sử dụng std::vector, dynamic_cast, unique_ptr, shared_ptr, v.v. trong mã được gọi trực tiếp hoặc gián tiếp bởi mã của bạn, tin rằng mã đó là vô hại vì mã của bạn đã "được cho là" C ++ mã. Tại thời điểm đó, chúng ta phải đối mặt với cơ hội rằng mọi thứ sẽ ném, và sau đó khi bạn lấy mã C hoàn toàn tốt và đáng yêu như thế này:

int some_func(int n, ...)
{
    int* x = calloc(n, sizeof(int));
    if (x)
    {
        f(n, x); // some function which, now being a C++ function, may 
                 // throw today or in the future.
        ...
        free(x);
        return success;
    }
    return fail;
}

... Bây giờ nó đã bị hỏng. Nó cần phải được viết lại để được an toàn ngoại lệ:

int some_func(int n, ...)
{
    int* x = calloc(n, sizeof(int));
    if (x)
    {
        try
        {
            f(n, x); // some function which, now being a C++ function, may 
                     // throw today or in the future (maybe someone used
                     // std::vector inside of it).
        }
        catch (...)
        {
            free(x);
            throw;
        }
        ...
        free(x);
        return success;
    }
    return fail;
}

Tổng! Đó là lý do tại sao hầu hết các nhà phát triển C ++ sẽ yêu cầu điều này thay vào đó:

void some_func(int n, ...)
{
    vector<int> x(n);
    f(x); // some function which, now being a C++ function, may throw today
          // or in the future.
}

Trên đây là mã an toàn ngoại lệ tuân thủ RAII thuộc loại mà các nhà phát triển C ++ thường chấp thuận vì hàm này sẽ không rò rỉ dòng mã nào kích hoạt lối thoát ngầm do kết quả của a throw.

Chọn một ngôn ngữ

Bạn nên nắm lấy hệ thống và triết lý loại của C ++ với RAII, ngoại lệ, an toàn, mẫu, OOP, v.v. hoặc nắm lấy C, chủ yếu xoay quanh các bit và byte thô. Bạn không nên hình thành một cuộc hôn nhân không lành mạnh giữa hai ngôn ngữ này, và thay vào đó tách chúng thành các ngôn ngữ riêng biệt để được đối xử rất khác nhau thay vì làm mờ chúng lại với nhau.

Những ngôn ngữ muốn kết hôn với bạn. Bạn thường phải chọn một thay vì hẹn hò và lừa dối cả hai. Hoặc bạn có thể là một người đa thê như tôi và kết hôn với cả hai, nhưng bạn phải thay đổi hoàn toàn suy nghĩ của mình khi dành thời gian cho nhau và giữ họ cách xa nhau để họ không đánh nhau.

Kích thước nhị phân

Vì tò mò, tôi đã thử thực hiện danh sách miễn phí và điểm chuẩn của mình ngay bây giờ và chuyển nó sang C ++ vì tôi thực sự tò mò về điều này:

[...] Không biết nó trông như thế nào đối với C vì tôi chưa sử dụng trình biên dịch C.

... Và muốn biết liệu kích thước nhị phân có tăng lên hay không chỉ là xây dựng như C ++. Nó yêu cầu tôi phải rắc các diễn viên rõ ràng ở khắp nơi, nơi mà tôi thích viết những thứ cấp thấp như cấp phát và cấu trúc dữ liệu trong C tốt hơn) nhưng chỉ mất một phút.

Đây chỉ là so sánh bản dựng phát hành 64 bit MSVC cho một ứng dụng bảng điều khiển đơn giản và với mã không sử dụng bất kỳ tính năng C ++ nào, thậm chí không quá tải toán tử - chỉ là sự khác biệt giữa xây dựng nó với C và sử dụng, <cstdlib>thay vì <stdlib.h>và những thứ như vậy, nhưng tôi đã ngạc nhiên khi thấy nó không khác biệt gì so với kích thước nhị phân!

Nhị phân là 9,728byte khi nó được xây dựng trong C và tương tự 9,278byte khi được biên dịch dưới dạng mã C ++. Tôi thực sự không mong đợi điều đó. Tôi nghĩ những thứ như EH ít nhất sẽ thêm một chút ở đó (nghĩ rằng ít nhất nó sẽ giống như một trăm byte khác nhau), mặc dù có lẽ nó có thể hình dung rằng không cần thêm các hướng dẫn liên quan đến EH vì tôi chỉ cần sử dụng thư viện chuẩn C và không có gì ném. Tôi đã nghĩ một cái gì đósẽ thêm một chút vào kích thước nhị phân, như RTTI. Dù sao, thật tuyệt khi thấy điều đó. Tất nhiên tôi không nghĩ bạn nên khái quát từ kết quả này, nhưng ít nhất nó đã gây ấn tượng với tôi một chút. Nó cũng không ảnh hưởng đến điểm chuẩn, và một cách tự nhiên, vì tôi tưởng tượng kích thước nhị phân kết quả giống hệt nhau cũng có nghĩa là hướng dẫn máy giống hệt nhau.

Điều đó nói rằng, ai quan tâm đến kích thước nhị phân với các vấn đề an toàn và kỹ thuật được đề cập ở trên? Vì vậy, một lần nữa, chọn một ngôn ngữ và nắm lấy triết lý của nó thay vì cố gắng để khốn khổ nó; đó là những gì tôi khuyên bạn nên

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.