Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Làm chủ việc phân tích đối số trong phần mở rộng của Ruby C:Hướng dẫn từng bước

Ruby là một ngôn ngữ tuyệt vời, được tạo ra cho con người trước tiên và thứ hai cho máy móc. Nó rất dễ đọc và viết. Có rất nhiều cách để viết bất cứ thứ gì và bạn thường có thể đoán thư viện chuẩn của nó bằng cách nhập tên của phương thức mà bạn đã tự mình chọn.

Do đó, các đối số của Ruby rất linh hoạt, cho phép chúng tôi diễn đạt API của mình rất rõ ràng. Nhưng điều này có một nhược điểm:Ruby khá khó phân tích đối với các nhà phát triển Cextension!

Trong bài viết này, chúng ta sẽ thực hiện hai cách để thiết lập API Ruby phức tạp được viết bằng C:

  • với rb_define_method và phân tích nó bằng rb_scan_args
  • sử dụng giao diện Ruby

Hãy bắt đầu!

C và Ruby:Giới thiệu

Như đã đề cập, Ruby rất khó phân tích cú pháp đối với các nhà phát triển tiện ích mở rộng C.

Ví dụ:

 

Vẻ đẹp của C, ngôn ngữ viết của Ruby, bắt nguồn từ sự đơn giản của nó, bao gồm cả các tham số chức năng của nó:

  • <data-type> <variable-identifier>
  • ... cho các đối số đa dạng.

Những điều này sẽ giúp bạn duy trì acodebase không quá khó hiểu.

Đây là cách phức tạp nhất để xác định hàm C:

 

Khi bạn viết mã phần mở rộng C cho cơ sở mã Ruby của mình, bạn sẽ bắt đầu hiểu sự phức tạp bắt đầu từ đâu. Nhưng đừng lo lắng — các nhà phát triển Ruby MRI đã hỗ trợ chúng tôi.

Định nghĩa phương thức đơn giản trong phần mở rộng Ruby C

Chúng ta sẽ bắt đầu với một phương pháp mà bạn sẽ phải sử dụng vào một lúc nào đó, rb_define_method .

Bạn cũng có thể làm theo các ví dụ về mã trong kho lưu trữ này.

Đây là rb_define_method chữ ký của:

 

Và theo phần mở rộng.rdoc của Ruby:

argc là số lượng đối số. nếu argc là -1, hàm sẽ nhận được 3 đối số:argc, argv và self. nếu argc là -2 thì hàm sẽ nhận 2 đối số, self và args, trong đó args là mảng Ruby của các đối số của phương thức.

Tóm lại:

 

Vì vậy, nếu API của bạn chỉ bao gồm các phương thức có độ dài tham số cố định hoặc chỉ có một tham số biến thiên (def foo(*bar) ), không cần đọc thêm nữa - bạn đã hoàn tất! Nếu bạn muốn có một cách phong phú hơn để gọi API của mình, hãy trở thành khách của tôi.

Nhân tiện, nếu bạn muốn có thêm ví dụ liên quan về rb_define_method , Tôi khuyên bạn nên đọc bài viết về Phương pháp xác định của Peter Zhu.

Sử dụng nội bộ API Ruby C

Vì vậy, hãy quay lại trường hợp sử dụng của chúng ta:phân tích các đối số phức tạp. May mắn thay, một số công cụ có thể giúp chúng ta.

Nhưng trước tiên, hãy xem những hạn chế của việc chỉ sử dụng rb_define_method .

Hạn chế của rb_define_method

Không đề cập đến các đối số khối

Một hạn chế là rb_define_method không bao giờ đề cập đến các đối số khối. Những điều đó không được xem xét, vì dù sao thì nó cũng không thực sự quan trọng đối với Ruby nếu bạn chặn. Bạn vẫn có thể đảm bảo rằng một khối được chuyển bằng cách sử dụngrb_block_given_p hoặc rb_need_block . Có nhiều thông tin hơn về chủ đề đó trong bài viết của Peter Zhu.

Các đối số có thể thay đổi

Một hạn chế quan trọng khác là các đối số có thể khác nhau, nhưng bản thân lệnh gọi phương thức không quá hạn chế. Do đó, nếu bạn muốn API của mình giống def foo(bar, *baz) , bạn sẽ phải phân tích các đối số của mình. Có một số phương pháp có thể giúp bạn đi theo con đường đó. rb_check_arity là phiên bản bạn có thể sử dụng, cùng với phiên bản -1 của rb_define_method .

Đây là chữ ký hàm:

 

Đối số từ khóa

Một hạn chế cuối cùng mà chúng ta sẽ đề cập đến là việc sử dụng các đối số từ khóa. Và tôi đã giữ điều tốt nhất ở phần cuối vì đó sẽ là cốt lõi của bài viết này.

Chúng ta phải truy xuất các đối số từ khóa trước khi có thể phân tích chúng một cách chính xác. May mắn thay, API Ruby C có sẵn một phương thức cho việc này,rb_scan_args .

Đây là rb_scan_args chữ ký:

 

Bạn vượt qua rb_scan_args argcargv được đưa ra bởi rb_define_method — một chuỗi cho biết cách phân tích cú pháp các đối số (fmt ) và người nhận các đối số đó. Vậy là xong, tất cả độ phức tạp của đối số của Ruby được phân tích cú pháp trong một dòng! À, gần như vậy.

Bạn có thể tham khảo phần mở rộng.rdoc để biết cách trình bày chính thức về cách fmt nên được viết ra, mặc dù chúng tôi sẽ đề cập một phần đến nó trong các ví dụ bên dưới.

Viết và phân tích hàm:Một ví dụ

Trong phần còn lại của bài viết này, hãy xem xét rằng chúng ta muốn viết hàm này:

 

Phân tích cú pháp này bằng rb_scan_args sẽ trông như thế này:

 

"1*:" vô nghĩa có nghĩa là:

  • 1 :một đối số vị trí bắt buộc
  • * :không cần bất kỳ số lượng đối số vị trí nào
  • : :đối số từ khóa ở cuối

Phân tích đối số từ khóa

Bây giờ chúng tôi đã hạn chế phương pháp này, thật không may, chúng tôi vẫn chưa hoàn thành. API hiện tại của chúng tôi là def voronoi_diagram(envelope, *polygons, **kwargs) .

Cuối cùng, chúng ta cần phân tích các đối số từ khóa đó bằng cách sử dụng rb_get_kwargs .

 

Bạn phải chọn một số đối số bắt buộc và tùy chọn. Khi đã xong, bạn sử dụng table để cho Ruby biết tên của các đối số đó và bạn lưu kết quả vào một mảng (values ).

 

Ở đó bạn có nó! Một phương thức Ruby phức tạp, được phân tích cú pháp chỉ bằng C. Tuy nhiên, nếu điều này quá phức tạp đối với bạn thì vẫn có một lựa chọn khác.

Sử dụng giao diện Ruby

Một cách khác để xử lý vấn đề là sử dụng trực tiếp cú pháp của Ruby và thực hiện phân tích cú pháp ở giai đoạn Ruby.

 

Với điều này, bạn có thể trực tiếp sử dụng dạng thứ ba của rb_define_method , đối với phương thức C trông như thế này:

 

Và thế là xong - bạn hoàn toàn tránh được vấn đề với một giải pháp thanh lịch thực sự được sử dụng cho một số phương thức trong Rubyimplementation (với Primitive lớp).

Mặc dù lớp được MRI sử dụng khá phức tạp và tự tạo ra C, nhưng chúng ta có thể lấy cảm hứng từ nó.

Hãy tạo một đối tượng và cắm các phương thức của chúng ta để tránh có c_voronoi_diagram hiển thị dành cho người dùng API của chúng tôi:

 
 

Hãy xem trường hợp sử dụng thực tế của lớp này trong cơ sở mã của RGeo.

Đối số phân tích cú pháp:Tôi nên sử dụng phương pháp nào?

Kho lưu trữ này giới thiệu hai cách để thiết lập API Ruby phức tạp được viết bằng C — tóm tắt lại:

  • với rb_define_method và phân tích nó bằng rb_scan_args
  • sử dụng giao diện Ruby

Khi chúng tôi so sánh cả hai giải pháp về mặt hiệu suất, chúng gần như bằng nhau (phân tích cú pháp Ruby trung bình nhanh hơn 1,02 lần trên M1 của tôi). Điều đó không tạo ra nhiều khác biệt.

Trong RGeo lib, lựa chọn thiết kế đầu tiên của chúng tôi là có một API chỉ sử dụng các đối số có độ dài thay đổi. Không có từ khóa, không có khối. Điều này có thể rất hạn chế và chúng tôi hiện đang sử dụng Primitive cách cho phép tranh luận phức tạp hơn.

Lợi ích của việc sử dụng giao diện Ruby

Lời khuyên của tôi là hãy sử dụng giao diện Ruby vì nhiều lý do, bao gồm:

  • kích thước mã nhỏ hơn
  • thực hiện các thay đổi dễ dàng hơn

Nhìn chung, điều này giúp trải nghiệm đọc codebase của bạn trở nên đơn giản hơn.

Đối với tôi, việc thu hút người dùng Ruby đọc mã nguồn của những viên đá quý mà họ sử dụng là điều quan trọng. Vì Ruby rất dễ đọc , đá quý cũng vậy.

Lợi ích của việc sử dụng rb_define_method

Tuy nhiên, phiên bản C cung cấp cho chúng ta một số phương thức nội bộ rất hữu ích. Ví dụ:rb_check_arity vẫn thực sự hữu ích. Các phương pháp xử lý khối cũng rất tuyệt vời và bạn có thể không cần sử dụng các khối mặt tiền Ruby.

Điều quan trọng nhất là tìm ra lựa chọn tốt nhất cho trường hợp sử dụng của bạn.

Kết thúc

Trong bài đăng này, chúng tôi đã khám phá hai phương pháp phân tích các đối số trong tiện ích mở rộng Ruby C của bạn — sử dụng rb_define_method (đồng thời xem xét ngắn gọn các hạn chế của nó) và phân tích cú pháp bằng rb_scan_args và sử dụng giao diện Ruby.

Nếu bạn muốn đọc thêm về tiện ích mở rộng C, tôi khuyên bạn nên:

  • Xây dựng tiện ích mở rộng Ruby C từ đầu
  • Chuyến đi của một người chơi Rubyist dọc theo phía C
  • Làm việc với tiện ích mở rộng Ruby C trên máy Mac

Và lời khuyên cuối cùng của tôi dành cho bạn:hãy xem RGeo. Nó là một cơ sở mã mở rộng C được sử dụng rộng rãi trong quá trình phát triển tích cực. Hầu hết các ví dụ của tôi đều đến từ đây.

Chúc bạn viết mã vui vẻ!

Tái bút. Nếu bạn muốn đọc các bài đăng của Ruby Magic ngay khi chúng được đăng tải, hãy đăng ký nhận bản tin Ruby Magic của chúng tôi và không bao giờ bỏ lỡ một bài đăng nào!

Làm chủ việc phân tích đối số trong phần mở rộng của Ruby C:Hướng dẫn từng bước

Ulysse Buônomo

Tác giả khách mời Ulysse của chúng tôi là một cựu nhà phát triển Ruby trong ngành, người dành phần lớn thời gian của mình để đi du lịch vòng quanh thế giới. Thời gian rảnh rỗi của anh ấy dành cho RGeo và Ruby, đồng thời anh ấy thích mày mò nghiên cứu nội bộ của Ruby.

Tất cả bài viết của Ulysse Buonomo