2013-05-24 34 views
8

Tôi đã có một cuộc thảo luận với một đồng nghiệp ngày hôm nay. Ông tuyên bố rằng việc viết một DLL trong C sẽ cho phép bất kỳ ứng dụng nào khác, được viết bằng bất kỳ ngôn ngữ nào, để sử dụng DLL đó. NHƯNG, nếu DLL đó được viết bằng C++, số lượng ứng dụng có thể sử dụng DLL đó bị giới hạn (có thể do các ràng buộc về ngôn ngữ).DLL viết bằng C so với cùng được viết bằng C++

  1. Ông ấy có đúng không?
  2. Nếu bạn đã viết một DLL nên được sử dụng bởi tất cả các loại ứng dụng được viết bằng tất cả các loại ngôn ngữ (nhưng trên cùng một nền tảng; hãy quên tính di động một chút), bạn sẽ viết nó trong C/C++ và tại sao ?

Tôi hy vọng câu hỏi này không phải là câu hỏi loại Gorilla vs. Shark. Nếu có, hãy đóng nó lại.

+1

Thư viện OneJoker của tôi được viết bằng C và có thể gọi từ C, C++, Java và Python trên Linux và Windows. Điều này là hợp lý đơn giản, bởi vì JNI của Java và mô-đun ctypes của Python đều được thiết kế để làm việc với C, chứ không phải C++. C++ kéo trong rất nhiều phụ thuộc, mangles các tên hàm bên ngoài, v.v. Chỉ là kinh nghiệm của tôi. –

+1

Khá chắc chắn bạn có thể 'extern" C "' nếu bạn cần xuất một giao diện C. Liên quan đến câu hỏi thứ hai, bạn muốn xem các đối tượng COM (bất cứ thứ gì có thể sử dụng chúng và đó là điểm). –

Trả lời

11

Hầu hết các ngôn ngữ cung cấp một cách dễ dàng để gọi hàm C từ một DLL. Nó không phải là trường hợp với C++, vì C++ ABI (giao diện nhị phân của hàm C++) là nhà cung cấp cụ thể.

Thêm vào đó, nó sẽ gần như không thể giao tiếp với C++ DLL sử dụng nâng cao cấu trúc C++ như templating hoặc STL.

Tuy nhiên, nội dung của DLL của bạn có thể được viết bằng C++, bạn chỉ cần đảm bảo rằng giao diện của bạn tuân thủ C. Để làm điều đó, không sử dụng cấu trúc C++ trong giao diện của bạn và bao quanh các khai báo của bạn với:

#ifdef __cpluscplus 
extern "C" { 
#endif 

/* You declarations here */ 

#ifdef __cpluscplus 
} 
#endif 

... theo cách này, bạn bọc thư viện C++ bằng giao diện C.

EDIT: Như Mats Petersson đã viết, đừng quên đảm bảo rằng bạn xử lý mọi ngoại lệ C++ có thể có trong trình bao bọc của bạn.

+0

Trình bao bọc C này có bị coi là lộn xộn không? Bởi vì trừ khi các mã C++ cơ bản là cực kỳ phức tạp bằng cách sử dụng STL và các mẫu và như vậy, tôi có một cảm giác nó sẽ là tốt hơn để chỉ refactor mã để C. Tôi có phải không? –

+1

Không, tôi không nghĩ vậy.Mã cơ bản của bạn có thể sử dụng các khái niệm C++ nâng cao, triển khai các thuật toán, sử dụng các thư viện C++ cấp cao, nhưng giao diện của nó có thể rất đơn giản, như chức năng khởi động/dừng đơn giản và chức năng lấy một số bộ đệm làm tham số và trả về một số bộ đệm. trong các khối dữ liệu JSON, XML hoặc protobuf. –

+1

Nó nghĩ rằng đó là vai trò rất của đóng gói. Ẩn chế độ nâng cao với giao diện đơn giản ... Tuy nhiên, nó phụ thuộc vào thư viện của bạn. Tôi bạn DLL cần phải cung cấp * tiện ích * chức năng và có một API rất phong phú để được xuất khẩu ra thế giới bên ngoài, bạn có thể cần phải suy nghĩ lại về cách sử dụng DLL này một cách cross-language hoặc thử các thư viện như SWIG (http: // www .swig.org /). –

0

Mỗi ngôn ngữ có các đặc điểm riêng như quy ước gọi, thiết lập ngăn xếp và các nội dung khác. Bất cứ khi nào bạn cố gắng để đối phó với các cuộc gọi chức năng vượt qua ranh giới ngôn ngữ, bạn phải đối phó với điều này. Các trình biên dịch hỗ trợ thường là một sự đa dạng của các quy ước gọi, vì vậy bạn phải đảm bảo rằng các định nghĩa và biên dịch là chính xác và sau đó bạn có thể sử dụng bất kỳ ngôn ngữ nào để giao tiếp với các mô-đun từ những người khác. Tuyên bố như vậy, dễ dàng hơn hoặc khó hơn để làm, từ ngôn ngữ này hay ngôn ngữ khác như vậy, là không đúng, bởi vì bạn luôn phải đối phó với điều này tại một số điểm.

Tôi chọn ngôn ngữ phù hợp nhất với công việc của mình, không phải theo cách khác. Khi tôi viết các ứng dụng GUI, tôi thường sử dụng Java, vì nó cho phép tôi tập trung vào giải pháp thay vì theo dõi bộ nhớ. :) Khi tôi muốn hiệu suất, tôi sử dụng C hoặc C++ hoặc thậm chí assembler, do đó, việc lựa chọn ngôn ngữ phụ thuộc vào những gì tôi định làm với nó hoặc môi trường của tôi trông như thế nào.

4

1) Nếu INTERFACE được cung cấp bởi DLL thực sự là giao diện C++, thì có, nó làm cho nó khó (nếu không phải là không thể) đối với các ngôn ngữ khác để giao tiếp với DLL.C++ là phức tạp hơn để giao tiếp với hơn C, vì cấu trúc phức tạp hơn của lớp (this con trỏ được truyền cùng, con trỏ hàm ảo/bố cục VTABLE) và xử lý ngoại lệ (trong đó mã được gọi là để xử lý thực tế là ngoại lệ xảy ra theo cách nào đó, và để làm điều đó, mã cần có khả năng "bung" cuộc gọi ngăn xếp mã đã ném ngoại lệ và phá hủy bất kỳ đối tượng nào được tạo trên đường cho đến khi tìm thấy catch - nếu không trong thư viện DLL, bạn sẽ gặp vấn đề - việc giải nén này không phải là một phần của tiêu chuẩn C++, vì tiêu chuẩn không muốn giới hạn kiến ​​trúc nào và các tính năng cần xử lý/cần phải thực hiện C++ nhiều hơn mức cần thiết). Bắt ngoại lệ trong DLL sẽ giải quyết vấn đề ở đây.

Nói cách khác, nếu ngôn ngữ không phải là C++ [và có thể từ cùng một nhà cung cấp] đang gọi mã, thì cần phải xử lý C++ cho bất kỳ ngôn ngữ nào đang gọi nó. Điều này có thể khá phức tạp.

Bất kỳ đối tượng C++ nào cũng cần được dịch từ/sang ngôn ngữ có liên quan trong mã gọi. Đối với các loại cơ bản chung với C, điều này thường không phải là một vấn đề, nhưng các lớp, cấu trúc, vv, sẽ phải được so khớp với một cái gì đó tương thích trong ngôn ngữ địa phương.

Mặt khác: Chức năng C rất dễ giao tiếp với: đặt đối số trên ngăn xếp, gọi hàm và dọn sạch đối số khi trả về. Không có gì lạ xảy ra, không có thông số chức năng ẩn, không cần thư giãn ngăn xếp. Biến chứng nhẹ duy nhất là các hàm trả về struct (lớn hơn một kích thước nhất định) - nhưng đó là một trường hợp khá đặc biệt trong "objets" của C. C đơn giản hơn nhiều, và hầu hết các ngôn ngữ đều có loại tương ứng tốt với các loại ngôn ngữ C cơ bản (nhưng kiểu C struct vẫn có thể gây ra một số vấn đề thú vị, và union có thể là một thách thức thực sự nếu nó được sử dụng "khéo léo").

2) Chọn ngôn ngữ là một doanh nghiệp phức tạp, và phần lớn phụ thuộc vào cách DLL được sử dụng hoặc loại giao diện mà nó được cho là cung cấp, và giao diện nào sẽ kết nối với - nếu DLL của bạn giao tiếp với một DLL C++ khác (hoặc một số mã C++ khác), sau đó bạn có thể muốn sử dụng C++. Nhưng có nhiều cách để tạo ra một C++ DLL có giao diện C, bằng cách sử dụng extern "C" cho các chức năng giao diện (và đảm bảo rằng không có gì throws trên tường đến "C", bởi vì điều đó chắc chắn sẽ gây ra vấn đề).

Kết luận: Rõ ràng, bằng cách hạn chế các giao diện để "chỉ sử dụng C++", sau đó là một biến chứng hơn nữa trong bất cứ ai rằng việc sử dụng thư viện từ, nói, C, Python hay Lisp [tất cả trong số đó có lẽ có thể gọi Các hàm C khá dễ dàng], người dùng đó sẽ phải bọc mã C++ trong trình bao bọc ngôn ngữ C. Và có, điều này có thể được thực hiện, và được sử dụng khá thường xuyên khi một số thư viện thực sự tốt có sẵn trong C++, rằng ai đó muốn kết nối với một ngôn ngữ có giao diện kiểu C có sẵn. Đó là khá nhiều giải pháp tương tự như "cung cấp một giao diện C đến C + + trong DLL", ngoại trừ nó không được cung cấp bởi nhà sản xuất của DLL.

+0

+1 cho * đảm bảo rằng không có gì ném ... *. Quên điều đó. –

+0

Wow Tôi không có ý tưởng rằng ngoại lệ có thể tạo ra các vấn đề như vậy! Điều gì sẽ xảy ra nếu ngoại lệ bị bắt bên trong hàm DLL? Trong trường hợp đó, người gọi sẽ không cần phải làm bất cứ điều gì đúng? –

+0

IMO câu trả lời này là một chút sai lầm. Stack unwinding là một responsibillity của chức năng được gọi, không phải là người gọi, như người gọi không bao giờ có bất kỳ thông tin về những gì đã xảy ra bên trong chức năng. Việc truyền các đối tượng xung quanh các ngôn ngữ, không hoạt động, bởi vì bạn phải dịch các đối tượng giữa các ngôn ngữ. C giao dịch với các đối tượng ít phức tạp hơn, do đó, chi phí thấp hơn, nhưng nó vẫn phải được thực hiện tuy nhiên. Có một tham số "ẩn" trong C++ là một phần của quy ước gọi. Bạn phải biết về điều này, giống như bạn phải biết rằng tên trong C nhận được một '_' prepended. – Devolus

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