Tại sao C cung cấp ngôn ngữ 'ràng buộc' trong khi C ++ bị thiếu?


60

Gần đây tôi đã tự hỏi khi nào nên sử dụng C trên C ++ và ngược lại? May mắn thay, ai đó đã đánh bại tôi và mặc dù phải mất một thời gian, tôi đã có thể tiêu hóa tất cả các câu trả lời và nhận xét cho câu hỏi đó.

Tuy nhiên, một mục trong bài đăng đó cứ được xử lý lặp đi lặp lại, mà không có bất kỳ loại ví dụ, xác minh hoặc giải thích nào:

"Mã C phù hợp khi bạn muốn có nhiều ràng buộc ngôn ngữ cho thư viện của mình"

Đó là một cách diễn đạt. Tôi nên lưu ý rằng một số người chỉ ra rằng có thể có nhiều ràng buộc ngôn ngữ trong C ++ (thông qua một số externchức năng), tuy nhiên, nếu bạn đọc toàn bộ bài đăng đó, thì rõ ràng C là lý tưởng cho tính liên kết ngôn ngữ / tính di động. Câu hỏi của tôi là: tại sao?

Ai đó có thể vui lòng cung cấp lý do cụ thể tại sao viết thư viện bằng C cho phép liên kết dễ dàng hơn và / hoặc tính di động trong các ngôn ngữ khác không?


6
Chủ yếu là vì lý do lịch sử và xã hội. Hầu hết các triển khai ngôn ngữ và hệ thống thời gian chạy được xây dựng ở trên C. Ví dụ, các thời gian chạy của Ocaml, SBCL, Haskell được mã hóa bằng C (và sẽ không thu được nhiều bằng cách được mã hóa lại trong C ++)
Basile Starynkevitch

8
Trong thực tế, việc dán các thư viện C ++ chính hãng (nghĩ về Qt) vào các thời gian chạy này là đau đớn.
Basile Starynkevitch

3
@Basile: Qt không phải là thư viện C ++ chính hãng. Ngoài ra, ngay cả đối với các thư viện đau đớn hơn, nó chỉ đau đớn vì chúng thể hiện ngữ nghĩa thực sự hữu ích.
DeadMG 04/05/2015


2
Đọc COM thiết yếu của Don Box. Nó giải thích quá trình tạo ABI trong C ++. COM là cách mà Microsoft tạo ra ABI. tức là các thư viện COM C ++ có thể được sử dụng từ C hoặc VB hoặc các ngôn ngữ khác.
user93353

Câu trả lời:


69

C có giao diện đơn giản hơn nhiều, và các quy tắc của nó để chuyển đổi giao diện mã nguồn thành giao diện nhị phân đủ đơn giản để tạo ra các giao diện bên ngoài để liên kết được thực hiện theo cách được thiết lập tốt. Mặt khác, C ++ có giao diện cực kỳ phức tạp và các quy tắc ràng buộc ABI hoàn toàn không được tiêu chuẩn hóa, cả về mặt hình thức lẫn thực tế. Điều này có nghĩa là hầu như mọi trình biên dịch cho bất kỳ ngôn ngữ nào cho bất kỳ nền tảng nào đều có thể liên kết với giao diện C bên ngoài và biết chính xác những gì mong đợi, nhưng đối với giao diện C ++, về cơ bản là không thể vì các quy tắc thay đổi tùy thuộc vào trình biên dịch, phiên bản nào và phiên bản nào nền tảng mã C ++ được xây dựng với.

Trong C, cũng không có quy tắc triển khai ngôn ngữ nhị phân tiêu chuẩn nào, nhưng đó là một thứ tự đơn giản hơn và trong trình biên dịch thực tế sử dụng cùng một quy tắc. Một lý do khác khiến mã C ++ khó gỡ lỗi là ngữ pháp phức tạp đã đề cập ở trên, vì trình gỡ lỗi thường không thể xử lý nhiều tính năng ngôn ngữ (đặt điểm dừng trong mẫu, lệnh phân tích cú pháp con trỏ trong cửa sổ hiển thị dữ liệu, v.v.).

Việc thiếu ABI tiêu chuẩn (giao diện nhị phân ứng dụng) có một hậu quả khác - nó khiến việc chuyển giao diện C ++ cho các nhóm / khách hàng khác trở nên không thực tế do mã người dùng sẽ không hoạt động trừ khi được biên dịch với cùng các công cụ và tùy chọn xây dựng. Chúng ta đã thấy một nguồn khác của vấn đề này - sự không ổn định của giao diện nhị phân do thiếu đóng gói thời gian biên dịch.

- Khiếm khuyết C ++


4
Bạn nên lưu ý rằng, mặc dù có thể xác định API giống như C được triển khai trong C ++ ( extern "C"nhưng vẫn còn nhiều việc phải làm, nên cân bằng với các tính năng được cung cấp bởi C ++)
jhominal

5
C ++ ABI không liên quan trong bối cảnh của câu hỏi, nơi các thư viện C ++ có thể dễ dàng xuất khẩu cùng extern "C".
DeadMG

12
@jhominal: Tốt hơn nhiều so với viết nó bằng C, trong đó bạn phải xác định giao diện C và sau đó bạn cũng phải thực hiện nó trong C, trong khi trong C ++, bạn có thể xác định giao diện C và sau đó bạn không phải thực hiện nó ở C. Bất kể bạn đang triển khai ngôn ngữ nào, bạn vẫn phải xác định giao diện C - điều này tất nhiên đúng trong C ++ vì nó là ngôn ngữ C hoặc bất kỳ ngôn ngữ nào khác có thể phơi bày các ràng buộc C.
DeadMG

9
Có thể thêm từ chối trách nhiệm vào liên kết đến tài liệu FQA đó không?
Martin Ba

2
@DocBrown: Chắc chắn đối với tôi giống như câu hỏi về ràng buộc ngôn ngữ C so với ràng buộc ngôn ngữ C ++.
Mason Wheeler

32

Nếu bạn đang cố gắng giao tiếp với một người nói ngôn ngữ khác, pidgin sẽ dễ hơn Shakespearean English.

Các khái niệm của C - các lệnh gọi hàm, con trỏ, chuỗi kết thúc NULL - rất đơn giản, vì vậy các ngôn ngữ khác có thể dễ dàng thực hiện chúng đủ tốt để gọi các hàm C. Vì lý do lịch sử, nhiều ngôn ngữ khác được triển khai trong C, điều này giúp việc gọi các hàm C trở nên dễ dàng hơn.

C ++ thêm khá nhiều thứ - các lớp, với sự kế thừa và vtables và các sửa đổi truy cập; ngoại lệ, với ngăn xếp ngăn xếp và thay đổi dòng kiểm soát; mẫu. Tất cả điều này làm cho các ngôn ngữ khác khó sử dụng các ràng buộc C ++ hơn: tốt nhất là có nhiều "keo" hoặc mã tương tác để thực hiện và tệ nhất là các khái niệm không dịch trực tiếp (do sự khác biệt trong mô hình lớp, xử lý ngoại lệ, Vân vân.). Đối với các mẫu cụ thể, chỉ cần sử dụng (khởi tạo) chúng thường yêu cầu một bước biên dịch với trình biên dịch C ++, điều này làm phức tạp thêm việc sử dụng chúng từ các môi trường khác.

Với tất cả những gì đã nói, có thể nói quá mức về khó khăn trong việc cung cấp các ràng buộc từ thư viện C ++ sang ngôn ngữ khác:

  • Các ràng buộc C ++ có thể tương thích như C, nếu bạn sẵn sàng làm việc với nó. Như @DeadMG chỉ ra, C ++ hỗ trợ extern "C", do đó bạn có thể xuất các ràng buộc ngôn ngữ kiểu C (với tất cả sự đơn giản và khả năng tương thích của các ràng buộc C) từ thư viện C ++ (với giới hạn là bạn không thể hiển thị bất kỳ chức năng cụ thể nào của C ++) .
  • Một sự phản đối phổ biến khác đối với các ràng buộc ngôn ngữ C ++ là sự thiếu ổn định ABI cho C ++, nhưng điều này cũng bị cường điệu hóa; C ++ ABI ít được tiêu chuẩn hóa hơn C ABI, nhưng các tiêu chuẩn và tiêu chuẩn thực tế vẫn tồn tại (Itanium C ++ ABI, cũng được sử dụng trên OS X ; tiêu chuẩn thực tế của GCC cho Linux). Windows tệ hơn, nhưng ngay cả trên Windows, nằm trong một phiên bản Visual C ++ sẽ hoạt động tốt.

1
Vấn đề khác với việc cung cấp một ràng buộc từ thư viện C ++ sang ngôn ngữ khác là ngôn ngữ khác có thể yêu cầu các ràng buộc được triển khai trong C. Hoặc, đối với các ngôn ngữ có thứ gì đó như (.NET) P / Invoke hoặc (python) có thể không cung cấp bất kỳ công cụ nào để sử dụng C ++ ABI.
Random832

6
@ Random832: Điều này hoàn toàn không liên quan khi phía C ++ có thể đơn giản cung cấp giao diện C. Bạn không phải thực hiện ràng buộc trong C để cung cấp ràng buộc C.
DeadMG

21

C là một trong những ngôn ngữ lâu đời nhất vẫn còn tồn tại. ABI của nó rất đơn giản và hầu như mọi hệ điều hành vẫn còn được sử dụng ngày nay đã được viết trong đó . Mặc dù một số trong các hệ điều hành đó có thể đã thêm nội dung, ví dụ như trong C # /. NET hoặc bất cứ thứ gì ở trên, phía dưới chúng rất dốc trong C.

Điều đó có nghĩa rằng, để sử dụng các chức năng được cung cấp bởi hệ điều hành, hầu như mọi ngôn ngữ lập trình ra có cần một cách để giao tiếp với các thư viện C nào . Perl, Java, C ++, tất cả họ đều natively cung cấp cách để "nói chuyện C", bởi vì họ đã phải nếu họ không muốn tái tạo lại mỗi bánh xe duy nhất có thể.

Điều này làm cho C là tiếng Latin của ngôn ngữ lập trình. (Có bao nhiêu năm internet trước phép ẩn dụ đó phải là "tiếng Anh của ngôn ngữ progamming"?)


Khi bạn viết thư viện bằng C, bạn sẽ có giao diện tương thích C miễn phí (rõ ràng). Nếu bạn đang viết thư viện của mình bằng C ++, bạn có thể nhận được các ràng buộc C, thông qua các extern "C"khai báo như bạn đã đề cập.

Tuy nhiên , bạn có thể nhận được những cam kết ràng buộc chỉ cho các chức năng có thể được bày tỏ trong C .

Vì vậy, API thư viện của bạn không thể sử dụng ...

  • mẫu,
  • các lớp học,
  • ngoại lệ,
  • bất kỳ chức năng lấy hoặc trả lại đối tượng.

Một ví dụ đơn giản, bạn sẽ cần làm cho các hàm xuất của mình mấttrả về mảng ( []) thay vì std::vector(hoặc std::stringcho vấn đề đó).

Vì vậy, bạn không chỉ không thể cung cấp bất kỳ điều tốt nào mà C ++ cung cấp cho khách hàng của thư viện, mà bạn còn phải nỗ lực thêm để "dịch" API thư viện của bạn từ C ++ sang "Tương thích C" ( extern "C").

Đó là lý do tại sao thể đưa ra rằng C là lựa chọn tốt hơn để triển khai thư viện. Cá nhân, tôi nghĩ rằng lợi ích của C ++ vẫn vượt xa nỗ lực cần thiết cho extern "C"API, nhưng đó chỉ là tôi.


Windows dường như đang cố gắng dựa trên .NET, Android dựa trên Java (cũng là C như một chi tiết triển khai cho một số API) và iOS / OSX dựa trên Objective-C.
user253751 04/05/2015

1
Tiếng Anh đã là ngôn ngữ chính trong thế giới lập trình. Nổi trội hơn so với các ngành nghề khác.
Siyuan Ren

3
@immibis: Windows, Linux / Android và BSD / OSX đều là các hạt nhân được viết bằng C, với các giao diện được viết bằng (và cho) C. Không có vấn đề gì được xây dựng trên đó, Java cần JNI, Perl cần các cuộc gọi C ,. NET cần các cuộc gọi C, Python cần các cuộc gọi C, Objective-C cần các cuộc gọi C. Không ai trong số họ cần các cuộc gọi C ++, đó là điểm mà tôi đang cố gắng thực hiện.
DevSolar

@DevSolar Rất nhiều nội dung của Android được viết nguyên bản bằng Java và không sử dụng JNI (bạn có thể sử dụng "ngược" để gọi mã Java từ C, nhưng điều đó chỉ xác nhận thêm rằng đó là Java nguyên bản). Chưa có kinh nghiệm với iOS / OSX nhưng tôi nghe những điều đó giống với Objective-C.
dùng253751

3
@immibis: Nhưng bạn biết sự khác biệt giữa nhân hệ điều hành và không gian người dùng của nó không? Tôi thực sự nghi ngờ một không gian người dùng chủ yếu là Java làm cho Android không còn là nhân Linux với các cuộc gọi hệ thống kiểu C so với nhân Linux chạy trên máy tính để bàn của tôi. Tôi cũng nghi ngờ họ đang sử dụng Java trong kernel hoặc trong phần mềm trung gian. Thật ra, tôi biết họ đang sử dụng C ở đó. Đó là vấn đề trứng gà, ngược lại. Nó được thực hiện trong C trước, và do đó nó rất dễ dàng hơn để vẫn làm điều đó trong C.
DevSolar

6

Bỏ qua các chi tiết câu trả lời khác đã cung cấp:

Lý do rất nhiều ngôn ngữ cung cấp ràng buộc C là vì tất cả các hệ điều hành * nix và Windows đều phơi bày hầu hết API hệ điều hành của chúng thông qua giao diện C. Vì vậy, việc thực hiện ngôn ngữ đã cần phải giao tiếp với C để có thể chạy trên các Oses chính. Do đó, thật đơn giản để cung cấp giao tiếp trực tiếp với bất kỳ giao diện C nào từ chính ngôn ngữ.


5

Không có lý do. Nếu ngữ nghĩa mà bạn đang cố gắng diễn đạt về cơ bản là tương thích với C và không giống như các mẫu, thì không có lý do gì bạn có thể ràng buộc dễ dàng hơn nếu việc triển khai được viết bằng C. Trên thực tế, theo định nghĩa thì giao diện C có thể khá nhiều được điền bởi bất kỳ triển khai nào có thể đáp ứng hợp đồng nhị phân - bao gồm cả việc thực hiện bằng ngôn ngữ khác. Có những ngôn ngữ khác ngoài C ++ có thể thực hiện các hợp đồng nhị phân C có thể hoạt động theo kiểu này.

Những gì nó thực sự sôi nổi là những người không muốn học ngôn ngữ hoặc ý tưởng mới có ngữ nghĩa thực sự hữu ích hoặc các tính năng tuyệt vọng cố gắng chọn bất kỳ lý do nào để ở trong kỷ nguyên khủng long.


4
Tôi nghĩ bạn đã đúng và những người downvoters đã hiểu nhầm câu hỏi, nhưng thực ra câu trả lời của bạn bỏ lỡ để làm nổi bật sự hiểu lầm tiềm ẩn đó: câu hỏi không phải là về "một thư viện có giao diện C" so với "một thư viện có giao diện C ++", đó là về "một thư viện được viết hoàn toàn bằng C" so với "một thư viện có giao diện C được viết bằng C ++".
Doc Brown

@Snowman: Có các ràng buộc C ++ có vấn đề hoàn toàn không có gì để làm với bất kỳ vấn đề nào với việc phơi bày các ràng buộc C.
DeadMG

2
Vì vậy, bạn sẽ chọn một ngôn ngữ có ngữ nghĩa và tính năng hữu ích, sau đó tự ép mình dịch những ngôn ngữ đó sang giao diện C? Mặc dù các lớp và mẫu có thể tránh được (nhưng đặt câu hỏi tại sao bạn sử dụng C ++ ở nơi đầu tiên), mọi chức năng với liên kết C phải bắt ngoại lệ thay vì để chúng thoát vào mã C. Chưa kể rằng bất cứ ai sử dụng thư viện tương thích C của bạn bây giờ đều phải đối phó với C ++ và các xung đột ABI và thư viện không khớp, ngoài các xung đột ABI và thư viện không khớp của thư viện.
prosfilaes

2
@prosfilaes: Việc chuyển đổi ngoại lệ thành mã trả lại là chuyện nhỏ. Bạn không cần tránh sử dụng các lớp vì chúng có thể dễ dàng hạ xuống thành API C và bạn không cần tránh sử dụng các mẫu trong triển khai của mình. Và người dùng của bạn không cần phải nói xấu về xung đột C ++ ABI trừ khi họ xây dựng từ nguồn, trong trường hợp đó, bạn phải xử lý ngôn ngữ nguồn cho dù đó là gì.
DeadMG

4
Tôi đánh giá thấp không phải vì người ta nhất thiết không nên viết thư viện với API C trong C ++, mà bởi vì có những lý do chính đáng để sử dụng C ngoài việc là một con khủng long. Nếu bạn đang viết cho một API bằng ngôn ngữ X, thì đó là C, FORTRAN, COBOL, BLISS hoặc Java, sẽ luôn luôn đơn giản hơn, dễ dàng hơn và chính xác hơn để viết bằng ngôn ngữ đó sau đó viết bằng một fancier , ngôn ngữ thú vị hơn và giao diện hai.
prosfilaes

2

Có hai trục chính khi giao tiếp với ngôn ngữ khác:

  • các khái niệm mà giao diện có thể thực hiện: chỉ các giá trị? người giới thiệu? thuốc generic?
  • giao diện được triển khai như thế nào trong "nhị phân" (được gọi là ABI)

C có lợi thế hơn C ++ trên hai mặt trận đó:

  • C chỉ có các khái niệm đơn giản, xuất hiện trong hầu hết các ngôn ngữ khác 1
  • Các nhị phân ABI của C được quyết định bởi OS 2

Bây giờ, tại sao hầu hết các ngôn ngữ có một tập hợp các khái niệm tương tự như C có thể là do nó "đơn giản" hoặc "tồn tại trước"; mặc dù điều đó không quan trọng, vấn đề là họ làm.

Ngược lại, C ++ có các khái niệm phức tạp và ABI được quyết định bởi mỗi trình biên dịch (mặc dù nhiều người tuân thủ ABI Itanimum, ngoại trừ trên Windows ...). Thực sự đã có một đề xuất của Herb Sutter về việc các HĐH sửa lỗi C ++ ABI (trên cơ sở mỗi HĐH) để giải quyết một phần vấn đề này. Ngoài ra, người ta cần lưu ý rằng C ++ FFI là có thể, D đang thử nó 3 .

1 Ngoại trừ matrixdics ( ...), những thứ đó không đơn giản

2 C có ABI chuẩn không?

3 Giao diện mã D với Legacy C ++


0

Về cơ bản, nó đi xuống tiêu chuẩn hóa ABI. Mặc dù C và C ++ không có ABI được tiêu chuẩn hóa mà các ngôn ngữ khác có thể sử dụng để giao tiếp giữa các nhị phân được viết, C đã trở thành một tiêu chuẩn thực tế, mọi người đều biết nó và mọi người khác có thể sử dụng các quy tắc đơn giản, giống nhau mà ngôn ngữ này có các loại và chức năng gọi.

C ++ có thể có ABI tiêu chuẩn, nhưng Stroustrup đã nói rằng anh ta không thấy cần thiết. Ông cũng nói rằng rất khó để có được sự đồng thuận từ các nhà văn trình biên dịch (mặc dù tôi nghi ngờ rằng - ủy ban tiêu chuẩn C ++ sẽ phát hành ABI tương tự như các trình biên dịch hiện tại và các trình biên dịch trình biên dịch sẽ đơn giản thay đổi phiên bản tiếp theo của trình biên dịch, đôi khi không tương thích với nhị phân được xây dựng với các phiên bản cũ của trình biên dịch của họ - tôi nhớ lại việc biên dịch lại một vài thư viện với trình biên dịch Sun mới và thấy chúng không hoạt động với các trình biên dịch cũ)

Bạn sẽ lưu ý rằng một số công ty đã chuyển sang sử dụng ABI tiêu chuẩn, Microsoft đã bắt đầu quá trình này bằng cách COM trở lại vào những năm 90 và hôm nay họ đã tinh chỉnh điều này thành WinRT ABI (không bị nhầm lẫn với WinRT khác đề cập đến đến một loại HĐH bảng) cho phép các chương trình được viết bằng C # giao tiếp với các thư viện được viết bằng C hoặc C ++ (tức là lớp HĐH của chính Microsoft được viết bằng C ++, được hiển thị bằng WinRT và được các ứng dụng C # sử dụng khi họ gọi bất kỳ thói quen chạy hệ điều hành nào)

Không có nhiều người có thể làm trừ khi một cơ quan tiêu chuẩn bước lên và khắc phục tình trạng này. Microsoft rõ ràng nhìn thấy giá trị trong đó và đã thực hiện các bước để giải quyết nó cho nền tảng của họ.

Vì vậy, câu trả lời là C thực sự không cung cấp các ràng buộc ngôn ngữ. Nó xảy ra rằng không ai đã lắng nghe và tiêu thụ chúng bất kể.


-2

Tất cả các câu trả lời đều thiếu vấn đề thực sự: Biên dịch C ++ giới thiệu "xáo trộn tên", do đó các nhị phân không tương thích với các lệnh gọi hàm "đơn giản".

Tất cả các công cụ ABI không chỉ là một nỗ lực để chuẩn hóa nó.

Nói chung, không đảm bảo bạn có thể liên kết chéo các hàm được biên dịch với các trình biên dịch khác nhau, ngay cả khi bạn sử dụng C ++ đơn giản. Trước đây chắc chắn rằng chúng không tương thích, nhưng ngày nay tiêu chuẩn hóa đang lan rộng;)

OTOH C được thiết kế chính xác để trở thành một "hội cấp cao" và cho phép tất cả các loại giao thoa dễ dàng. Không nên ngạc nhiên khi nó phù hợp hơn với ý thích ngôn ngữ chéo.

Lưu ý bên lề: trình biên dịch C ++ gốc (cfront) thực sự tạo ra nguồn C phải được biên dịch, giống hệt như gcc tạo ra hội "dưới mui xe".

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.