কম্পিউটার টিউটোরিয়াল

রুবি C এক্সটেনশন মাস্টার করা: স্ক্র্যাচ থেকে হাই-পারফরম্যান্স মডিউল বানান

রুবি ম্যাজিকের এই সংস্করণে, আমরা দেখাবো কীভাবে C-তে লেখা কোড রুবি থেকে ব্যবহার করা যায়। এটি পারফরম্যান্স-সেনসিটিভ প্যার্টস অপ্টিমাইজ করতে বা একটি C লাইব্রেরি এবং রুবির মধ্যে ইন্টারফেস তৈরি করতে ব্যবহার করা যায়। এটি এক্সটেনশন তৈরি করে যেগুলো C-তে লেখা লাইব্রেরি র‍্যাপ করে।

প্রচুর পরিপক্ব এবং পারফরম্যান্ট লাইব্রেরি 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; } }

যেন আপনি দেখতে পাচ্ছেন, সিম্পল স্ট্রিং ফরম্যাটিং করার জন্য কিছু হুপ জাম্প করতে হয়। একটি স্ট্রিং কনক্যাটেনেট করতে, আমাদের প্রথমে একটি বাফার alok করতে হয়। এটার পর, sprintf ফাংশনটি ফরম্যাটেড রেজাল্ট সেটাতে লিখতে পারে। অবশেষে, আমরা বাফারটি রিটার্ন করতে পারি।

উপরের কোড দিয়ে, আমরা ইতিমধ্যে একটি সম্ভাব্য ক্র্যাশ বা সিকিউরিটি ইস্যু ইন্ট্রোডিউস করেছি। যদি ইনকামিং স্ট্রিং ২৪৫ বাইটের চেয়ে বড় হয়, ভয়ঙ্কর বাফার ওভারফ্লো হবে। আপনি অবশ্যই সাবধান থাকবেন C লেখার সময়, আপনার পায়ে গুলি দিয়ে নিতে সহজ।

পরবর্তী হচ্ছে একটি হেডার ফাইল:

char* string_from_library(char*); int number_from_library(int); int boolean_from_library(int);

এই ফাইলটি আমাদের C লাইব্রেরির পাবলিক API ডেসক্রাইব করে। অন্যান্য প্রোগ্রাম এটি ব্যবহার করে জানতে পারে লাইব্রেরির কোন ফাংশনগুলো কল করা যাবে।

২০১৮-এর উপায়: ffi জেম ব্যবহার করা

তাহলে, এখন আমাদের একটি C লাইব্রেরি আছে যা আমরা রুবি থেকে ব্যবহার করতে চাই। এই C কোডকে একটি জেমে র‍্যাপ করার দুটি উপায় আছে। মডার্ন উপায় হলো ffi জেম ব্যবহার করা। এটি আমাদের যে হুপগুলো জাম্প করতে হয় সেগুলো অটোমেট করে। ffi ব্যবহার করে আমাদের lige লেখা C কোডের সাথে এরকম দেখাবে:

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 এক্সটেনশন দিয়ে র‍্যাপ করা যায়। এটি আমাদের রুবিতে হুডের নিচে কীভাবে সব কাজ করে সেটা সম্পর্কে আরও গভীর ইনসাইট দেবে।

আমাদের লাইব্রেরিকে একটি C এক্সটেনশনে র‍্যাপ করা

তাহলে এখন আমাদের একটি C লাইব্রেরি আছে যা আমরা রুবি থেকে ব্যবহার করতে চাই। পরবর্তী ধাপ হলো একটি জেম তৈরি করা যা এটি কম্পাইল এবং র‍্যাপ করে। জেম তৈরি করার পর, আমরা প্রথমে ext-কে require_paths-এ যোগ করি gemspec-এ:

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 রিকোয়ার করি, যেটি অর্থ "Make Makefile"। এটি রুবি দিয়ে ইনক্লুড করা হেল্পারসের একটি সেট যা C বিল্ড সেট আপ করার ঝঞ্জাল দূর করে। আমরা create_makefile কল করি এবং এক্সটেনশনের জন্য একটি নাম সেট করি। এটি একটি Makefile তৈরি করে যেটি সকল কনফিগারেশন এবং কমান্ড ধারণ করে C কোড বিল্ড করার জন্য।

পরবর্তী, আমাদের কিছু C কোড লিখতে হবে লাইব্রেরিকে রুবির সাথে সংযোগ করার জন্য। আমরা কিছু ফাংশন তৈরি করব যেগুলো C টাইপ যেমন char* কনভার্ট করবে রুবি টাইপ যেমন String-এ। তারপর আমরা একটি রুবি ক্লাস তৈরি করব C কোড দিয়ে।

প্রথমে, আমরা রুবি থেকে কিছু হেডার ফাইল ইনক্লুড করি। এগুলো আমাদের টাইপ কনভার্সনের জন্য দরকারি ফাংশন ইম্পোর্ট করবে। আমরা 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 হেল্পার ম্যাক্রো দিয়ে যা রুবি প্রদান করে। এখন আমরা আমাদের C লাইব্রেরি কল করতে পারি। রিটার্ন করা 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-তে আসলে বুলিয়ান টাইপ নেই। কনভেনশন হলো ০ এবং ১ ব্যবহার করা।

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 র‍্যাপার ফাংশনটি কল হবে এবং আর্গুমেন্টের সংখ্যা ইন্ডিকেট করতে হবে।

এতক্ষণ পরিশ্রমের পর, আমরা অবশেষে আমাদের C কোড কল করতে পারি:

CFromRubyExample::NativeHelpers.string("a string")

উপসংহার

আমরা হুপ জাম্প করলাম, ক্র্যাশ করলাম না এবং আমাদের C এক্সটেনশন কাজ করলো। C এক্সটেনশন লিখতে হলে মনের কাঁটা দরকার। এমনকি ffi জেম ব্যবহার করলেও আপনি সহজেই আপনার রুবি প্রসেস ক্র্যাশ করতে পারেন। কিন্তু এটি করা সম্ভব এবং এটি আপনার জন্য পারফরম্যান্ট এবং স্টেবল C লাইব্রেরির একটি বিশ্ব খুলে দিতে পারে!

  1. রুবি দিয়ে নিজের টয় ল্যাঙ্গুয়েজ তৈরি করুন: ধাপে ধাপে গাইড

  2. রুবি ইন্টারপ্রেটার তৈরি: পার্ট ২ – একটি কাস্টম প্রোগ্রামিং ভাষা তৈরি

  3. স্টফল ভাষার জন্য রুবি ইন্টারপ্রেটার গড়ে তোলা

  4. ধাপে ধাপে: USB ড্রাইভ ব্যবহার করে উইন্ডোজ ১১ স্ক্র্যাচ থেকে ইনস্টল করুন