রুবি ম্যাজিকের এই সংস্করণে, আমরা আপনাকে দেখাব কিভাবে রুবি থেকে C তে লেখা কোড ব্যবহার করতে হয়। এটি আপনার কোডের পারফরম্যান্স সংবেদনশীল অংশগুলিকে অপ্টিমাইজ করতে বা সি লাইব্রেরি এবং রুবির মধ্যে একটি ইন্টারফেস তৈরি করতে ব্যবহার করা যেতে পারে। এটি এমন এক্সটেনশন তৈরি করে করা হয় যা C.
-এ লেখা লাইব্রেরিগুলিকে মোড়ানো হয়সি-তে লেখা অনেক পরিপক্ক এবং পারফরম্যান্ট লাইব্রেরি আছে। সেগুলোকে পোর্ট করে চাকাকে নতুন করে উদ্ভাবনের পরিবর্তে আমরা রুবি থেকে এই লাইব্রেরিগুলোকেও কাজে লাগাতে পারি। এইভাবে, রুবি ঐতিহ্যগতভাবে শক্তিশালী নয় এমন এলাকায় সি লাইব্রেরি ব্যবহার করার সময় আমরা আমাদের প্রিয় ভাষায় কোড করতে পারি। AppSignal-এ, আমরা rdkafka রত্ন তৈরিতে এই পদ্ধতি ব্যবহার করেছি।
তাহলে দেখা যাক কিভাবে একজন এটির কাছে যেতে পারে। আপনি যদি অনুসরণ করতে চান এবং নিজেকে পরীক্ষা করতে চান তবে উদাহরণ কোডটি দেখুন। শুরু করার জন্য, আসুন রুবি কোডের এই টুকরোটি একটি স্ট্রিং, একটি সংখ্যা এবং একটি বুলিয়ান (আপনি কেন C করবেন, শ্লেষের উদ্দেশ্যে) নিয়ে নিন এবং এটি একটি C লাইব্রেরিতে পোর্ট করুন:
module CFromRubyExample
class Helpers
def self.string(value)
"String: '#{value}'"
end
def self.number(value)
value + 1
end
def self.boolean(value)
!value
end
end
end
ক্রমে, দেখানো পদ্ধতিগুলি একটি স্ট্রিংকে সংযুক্ত করে, একটি সংখ্যাকে এক দ্বারা বৃদ্ধি করে এবং যথাক্রমে একটি বুলিয়ানের বিপরীতে ফেরত দেয়।
আমাদের লাইব্রেরি C-তে পোর্ট করা হয়েছে
নীচে, আপনি C-তে পোর্ট করা কোডটি দেখতে পারেন। C স্ট্যান্ডার্ড লাইব্রেরি এবং IO লাইব্রেরি অন্তর্ভুক্ত করা হয়েছে যাতে আমরা স্ট্রিং বিন্যাস ব্যবহার করতে পারি। আমরা char*
ব্যবহার করি একটি রুবি String
এর পরিবর্তে . char*
স্মৃতিতে কোথাও অক্ষরের বাফারের অবস্থান নির্দেশ করে।
# include <stdlib.h>
# include <stdio.h>
char* string_from_library(char* value) {
char* out = (char*)malloc(256 * sizeof(char));
sprintf(out, "String: '%s'", value);
return out;
}
int number_from_library(int value) {
return value + 1;
}
int boolean_from_library(int value) {
if (value == 0) {
return 1;
} else {
return 0;
}
}
আপনি দেখতে পাচ্ছেন, সাধারণ স্ট্রিং বিন্যাস করার জন্য আপনাকে কিছু হুপের মধ্য দিয়ে যেতে হবে। একটি স্ট্রিং সংযুক্ত করতে, আমাদের প্রথমে একটি বাফার বরাদ্দ করতে হবে। এটি হয়ে গেলে, sprintf
ফাংশন তারপর এটিতে ফর্ম্যাট করা ফলাফল লিখতে পারে। অবশেষে, আমরা বাফার ফেরত দিতে পারি।
উপরের কোডের সাথে, আমরা ইতিমধ্যে একটি সম্ভাব্য ক্র্যাশ বা নিরাপত্তা সমস্যা চালু করেছি। ইনকামিং স্ট্রিং 245 বাইটের বেশি হলে, ভয়ঙ্কর বাফার ওভারফ্লো ঘটবে। সি লেখার সময় আপনার অবশ্যই সতর্কতা অবলম্বন করা উচিত, নিজের পায়ে গুলি করা সহজ।
পরবর্তীতে একটি হেডার ফাইল রয়েছে:
char* string_from_library(char*);
int number_from_library(int);
int boolean_from_library(int);
এই ফাইলটি আমাদের C লাইব্রেরির পাবলিক API বর্ণনা করে। অন্যান্য প্রোগ্রাম লাইব্রেরিতে কোন ফাংশন কল করা যেতে পারে তা জানতে এটি ব্যবহার করে।
2018 উপায়:ffi
ব্যবহার করুন মণি
সুতরাং, আমাদের এখন একটি সি লাইব্রেরি আছে যা আমরা রুবি থেকে ব্যবহার করতে চাই। একটি রত্ন মধ্যে এই C কোড মোড়ানো দুটি উপায় আছে. আধুনিক উপায়ে ffi
ব্যবহার করা জড়িত মণি এটি অনেকগুলি হুপকে স্বয়ংক্রিয় করে যা আমাদের লাফ দিতে হবে। ffi
ব্যবহার করে সি কোড দিয়ে আমরা এইমাত্র লিখেছি:
module CFromRubyExample
class Helpers
extend FFI::Library
ffi_lib File.join(File.dirname(__FILE__), "../../ext/library.so")
attach_function :string, [:string], :string
attach_function :number, [:int], :int
attach_function :boolean, [:int], :int
end
end
এই নিবন্ধটির উদ্দেশ্যে, আমরা একটি C এক্সটেনশন দিয়ে সি কোডটি কীভাবে মোড়ানো যায় তা ব্যাখ্যা করতে যাচ্ছি। এটি রুবিতে কীভাবে এটি সব কাজ করে সে সম্পর্কে আমাদের আরও বেশি অন্তর্দৃষ্টি দেবে৷
৷একটি C এক্সটেনশনে আমাদের লাইব্রেরি মোড়ানো
তাই আমাদের কাছে এখন একটি সি লাইব্রেরি রয়েছে যা আমরা রুবি থেকে ব্যবহার করতে চাই। পরবর্তী ধাপ হল একটি রত্ন তৈরি করা যা এটিকে সংকলন করে এবং মোড়ানো। মণি তৈরি করার পরে, আমরা প্রথমে ext
যোগ করি require_paths
-এ রত্নবিশেষে:
Gem::Specification.new do |spec|
spec.name = "c_from_ruby_example"
# ...
spec.require_paths = ["lib", "ext"]
end
এটি রুবিজেমসকে জানায় যে একটি নেটিভ এক্সটেনশন রয়েছে যা তৈরি করা দরকার। এটি extconf.rb
নামে একটি ফাইলের সন্ধান করবে অথবা একটি Rakefile
. এই ক্ষেত্রে, আমরা extconf.rb
যোগ করেছি :
require "mkmf"
create_makefile "extension"
আমাদের mkmf
প্রয়োজন , যার অর্থ হল "মেকফাইল করুন"। এটি রুবির সাথে অন্তর্ভুক্ত সাহায্যকারীদের একটি সেট যা একটি C বিল্ড সেট আপ পাওয়ার সূক্ষ্ম অংশকে সরিয়ে দেয়। আমরা create_makefile
কল করি এবং এক্সটেনশনের জন্য একটি নাম সেট করুন। এটি একটি Makefile
তৈরি করে যেটিতে সি কোড তৈরি করার জন্য সমস্ত কনফিগারেশন এবং কমান্ড রয়েছে।
এর পরে, রুবির সাথে লাইব্রেরি সংযোগ করতে আমাদের কিছু C কোড লিখতে হবে। আমরা কিছু ফাংশন তৈরি করব যা C প্রকারকে রূপান্তর করে যেমন char*
রুবি প্রকারে যেমন String
. তারপর আমরা সি কোড সহ একটি রুবি ক্লাস তৈরি করব।
প্রথমত, আমরা রুবি থেকে কিছু হেডার ফাইল অন্তর্ভুক্ত করি। এগুলি আমাদের টাইপ রূপান্তর করতে প্রয়োজনীয় ফাংশনগুলি আমদানি করবে। আমরা library.h
ও অন্তর্ভুক্ত করি হেডার ফাইল যা আমরা আগে তৈরি করেছি যাতে আমরা আমাদের লাইব্রেরি কল করতে পারি।
#include "ruby/ruby.h"
#include "ruby/encoding.h"
#include "library.h"
তারপরে আমরা আমাদের লাইব্রেরিতে প্রতিটি ফাংশন মোড়ানোর জন্য একটি ফাংশন তৈরি করি। এটি স্ট্রিংয়ের জন্য একটি:
static VALUE string(VALUE self, VALUE value) {
Check_Type(value, T_STRING);
char* pointer_in = RSTRING_PTR(value);
char* pointer_out = string_from_library(pointer_in);
return rb_str_new2(pointer_out);
}
আমরা প্রথমে পরীক্ষা করি যে রুবি মানটি একটি স্ট্রিং কিনা, যেহেতু একটি নন-স্ট্রিং মান প্রক্রিয়াকরণের ফলে সব ধরণের বাগ হতে পারে। তারপর আমরা রুবি String
রূপান্তর করি একটি char*
এ RSTRING_PTR
এর সাথে সাহায্যকারী ম্যাক্রো যা রুবি প্রদান করে। আমরা এখন আমাদের সি লাইব্রেরি কল করতে পারি। ফিরে আসা char*
রূপান্তর করতে , আমরা অন্তর্ভুক্ত rb_str_new2
ব্যবহার করি ফাংশন আমরা সংখ্যা এবং বুলিয়ানের জন্য অনুরূপ মোড়ানো ফাংশন যোগ করব।
সংখ্যার জন্য, আমরা NUM2INT
ব্যবহার করে অনুরূপ কিছু করি এবং INT2NUM
সাহায্যকারী:
static VALUE number(VALUE self, VALUE value) {
Check_Type(value, T_FIXNUM);
int number_in = NUM2INT(value);
int number_out = number_from_library(number_in);
return INT2NUM(number_out);
}
বুলিয়ান সংস্করণও একই রকম। উল্লেখ্য যে C এর আসলে একটি বুলিয়ান টাইপ নেই। কনভেনশন হল এর পরিবর্তে 0 এবং 1 ব্যবহার করা।
static VALUE boolean(VALUE self, VALUE value) {
int boolean_in = RTEST(value);
int boolean_out = boolean_from_library(boolean_in);
if (boolean_out == 1) {
return Qtrue;
} else {
return Qfalse;
}
}
অবশেষে, আমরা সবকিছু তারে আপ করতে পারি যাতে আমরা এটিকে রুবি থেকে কল করতে পারি:
void Init_extension(void) {
VALUE CFromRubyExample = rb_define_module("CFromRubyExample");
VALUE NativeHelpers = rb_define_class_under(CFromRubyExample, "NativeHelpers", rb_cObject);
rb_define_singleton_method(NativeHelpers, "string", string, 1);
rb_define_singleton_method(NativeHelpers, "number", number, 1);
rb_define_singleton_method(NativeHelpers, "boolean", boolean, 1);
}
হ্যাঁ, আপনি ঠিকই পড়েছেন:আমরা সি-তে রুবি মডিউল, ক্লাস এবং পদ্ধতি তৈরি করতে পারি। আমরা এখানে আমাদের ক্লাস সেট আপ করি। আমরা তারপর ক্লাসে রুবি পদ্ধতি যোগ করি। আমাদের রুবি পদ্ধতির নাম দিতে হবে, C র্যাপার ফাংশনের নাম যাকে কল করা হবে এবং আর্গুমেন্টের সংখ্যা নির্দেশ করতে হবে।
এই সমস্ত কাজ করার পরে, আমরা অবশেষে আমাদের C কোড কল করতে পারি:
CFromRubyExample::NativeHelpers.string("a string")
উপসংহার
আমরা হুপ্সের মাধ্যমে লাফিয়েছি, ক্র্যাশ হয়নি এবং কাজ করার জন্য আমাদের সি এক্সটেনশন পেয়েছি। সি এক্সটেনশন লেখা হৃদয়ের অজ্ঞান জন্য নয়। এমনকি ffi
ব্যবহার করার সময়ও মণি আপনি এখনও বেশ সহজে আপনার রুবি প্রক্রিয়া ক্র্যাশ করতে পারেন. কিন্তু এটি সম্ভব এবং আপনার জন্য পারফরম্যান্স এবং স্থিতিশীল সি লাইব্রেরির একটি জগত খুলে দিতে পারে!