Ruby একটি চমৎকার ল্যাঙ্গুয়েজ, মানবের জন্য প্রথম এবং মেশিনের জন্য দ্বিতীয়। এটি পড়া ও লিখা সহজ। যেকোনো কিছুর লিখার অনেক পথ আছে, এবং আপনি মেথডের নাম গেস করে স্ট্যান্ডার্ড লাইব্রেরি গেস করতে পারেন।
এই কারণে Ruby-এর আর্গুমেন্ট খুব ফ্লেক্সিবল, যা আমাদের API খুব স্পষ্টভাবে এক্সপ্রেস করতে দেয়। কিন্তু এর একটি ড্রব্যাক: C extension ডেভেলপারদের জন্য Ruby পার্স করা কঠিন!
এই আর্টিকেলে আমরা দুটি পদ্ধতি দেখব জটিল Ruby API যা C-তে লেখা হয় সেটআপ করার:
rb_define_methodএবংrb_scan_argsদিয়ে পার্স করা- Ruby interface ব্যবহার করা
C এবং Ruby: পরিচিতি
উল্লিখিতølge, Ruby C extension ডেভেলপারদের জন্য পার্স করা কঠিন।
উদাহরণ:
def voronoi_diagram(envelope, *polygons, **kwargs)
# complex logic
end
C-র সৌন্দর্য এর সাদगीয়ে, ফাংশন প্যারামিটারে:
<data-type> <variable-identifier>...variadic arguments-এর জন্য
সবচেয়ে কমপ্লেক্স C ফাংশন ডেফিনিশন:
void complex_func(int a, double b, char *c, ...)
Ruby C extension লিখতে গিয়ে জটিলতা শুরু হয় কোথায় বুঝতে পারবেন। কিন্তু চিন্তা নয় — Ruby MRI ডেভেলপাররা আমাদের কভার করেছে।
Ruby C Extension-এ সিম্পল মেথড ডেফিনিশন
rb_define_method থেকে শুরু করি।
Repo-র কোড উদাহরণ ফলো করুন।
rb_define_method-এর সিগনেচার:
void rb_define_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc)
Ruby-এর extension.rdoc অনুযায়ী:
- argc = -1 হলে ফাংশন ৩ আর্গুমেন্ট পাবে: argc, argv, self
- argc = -2 হলে ফাংশন ২ আর্গুমেন্ট পাবে: self এবং args (Ruby array of method arguments)
সংক্ষেপে:
// argc = -1
VALUE method(int argc, VALUE *argv, VALUE self)
// argc = -2
VALUE method(VALUE self, VALUE args)
যদি আপনার API শুধু ফিক্সড প্যারামিটার লেথ বা একটু variadic parameter (def foo(*bar)) দিয়ে থাকে, তবে আর পড়ার দরকার নেই — আপনি ডান! কিন্তু রিচার API চাইলে পড়তে থাকুন।
বিস্তারিত rb_define_method উদাহরণের জন্য Peter Zhu-এর 'Defining Methods' আর্টিকেল পড়ুন।
Ruby C API Internals ব্যবহার
জটিল আর্গুমেন্ট পার্স করার জন্য কিছু টুল আছে।
প্রথমে rb_define_method সোলো ব্যবহার করার লিমিটেশন দেখি।
Block Arguments মেনশন নেই
rb_define_method কখনো block arguments মেনশন করে না। Ruby-কে তো কেই মানে নয় আপনি ব্লক পাস করলেন কিনা। rb_block_given_p বা rb_need_block দিয়ে এনসার করুন ব্লক পাস হয়েছে কিনা।
Args ভ্যারি করতে পারে
Args ভ্যারি করতে পারে কিন্তু মেথড কল সেটা কনস্ট্রেইন করে না। API def foo(bar, *baz) চাইলে আর্গুমেন্ট পার্স করতে হয়। rb_check_arity একটি হেল্পার, rb_define_method-এর -1 ভার্সনের সাথে।
void rb_check_arity(int argc, int min, int max)
Keyword Arguments
এটি এই আর্টিকেলের কোর। Keyword arguments রিট্রিভ করতে হয় পার্স করার আগে। Ruby C API-তে এর জন্য মেথড আছে: rb_scan_args।
int rb_scan_args(int argc, VALUE *argv, const char *fmt, ...)
rb_scan_args-এ argc এবং argv পাস করুন (যে rb_define_method দিয়ে মিলে), একটি স্ট্রিং fmt যা বলে আর্গুমেন্ট কীভাবে পার্স হবে, এবং রিসিভার। এক লাইনে Ruby-এর আর্গুমেন্ট কমপ্লেক্সিটি পার্স! প্রায়।
fmt কীভাবে লিখতে হয় সেটা extension.rdoc-এ আছে, যদিও নিচে আংশিক কভার করা হয়েছে।
ফাংশন লিখুন এবং পার্স করুন: একটি উদাহরণ
বাকি আর্টিকেলে ধরে নিই আমরা এই ফাংশন লিখতে চাই:
def voronoi_diagram(envelope, *polygons, **kwargs)
# ...
end
rb_scan_args দিয়ে পার্স করা:
VALUE voronoi_diagram(int argc, VALUE *argv, VALUE self) {
VALUE envelope, *polygons, kwargs;
rb_scan_args(argc, argv, "1*:", &envelope, &polygons, &kwargs);
// ...
}
"1*:" গিবberish-এর অর্থ:
1: one required positional argument*: any amount of positional arguments not required:: keyword arguments at the end
Keyword Arguments পার্স করুন
মেথড কনস্ট্রেইন করলেও আমরা শেষ নই। বর্তমান API: def voronoi_diagram(envelope, *polygons, **kwargs)।
শেষে keyword arguments পার্স করতে rb_get_kwargs ব্যবহার করুন।
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
Required এবং optional arguments বেছে নিন। table দিয়ে Ruby-কে আর্গুমেন্টের নাম বলুন, এবং রেজাল্ট values array-এ স্টোর করুন।
ID table[2];
table[0] = rb_intern("option1");
table[1] = rb_intern("option2");
VALUE values[2];
rb_get_kwargs(kwargs, table, 1, 1, values);
// values[0] = option1 (required), values[1] = option2 (optional)
এভাবেই! একটি কমপ্লেক্স Ruby মেথড, শুধু C ব্যবহার করে পার্স করা হয়েছে। কিন্তু যদি এটি খুব convoluted লাগে, আরেকটি অপশন আছে।
Ruby Interface ব্যবহার করুন
সমস্যা হ্যান্ডল করার আরেকটি পদ্ধতি: Ruby সিনট্যাক্স সরাসরি ব্যবহার করুন এবং পার্সিং Ruby স্টেজে করুন।
# Ruby-এ wrapper
def voronoi_diagram(envelope, *polygons, **kwargs)
c_voronoi_diagram(envelope, polygons, kwargs)
end
এখন rb_define_method-এর থার্ড ফর্ম ব্যবহার করুন, C মেথড যেন এভাবে দেখায়:
VALUE c_voronoi_diagram(VALUE self, VALUE args) {
// args is a Ruby array: [envelope, polygons, kwargs]
VALUE envelope = RARRAY_AREF(args, 0);
VALUE polygons = RARRAY_AREF(args, 1);
VALUE kwargs = RARRAY_AREF(args, 2);
// ...
}
এবং সমস্যা আউট! একটি elegante solution যা Ruby ইমপ্লিমেন্টেশনে কিছু মেথডের জন্য ব্যবহৃত হয় (Primitive class-এ)।
MRI-তে ব্যবহৃত ক্লাস খুব কমপ্লেক্স এবং C জেনারেট করে, কিন্তু আমরা সেটা থেকে অনুপ্রাণিত হইতে পারি।
এক অবজেক্ট বানান এবং মেথডগুলো প্লাগ করুন যাতে c_voronoi_diagram ইউজারদের কাছে ভিজিবল না থাকে:
class Voronoi
primitive :voronoi_diagram
end
RGeo কোডবেসে এই ক্লাসের রিয়েল ইউজ কেস চেক করুন।
আর্গুমেন্ট পারসিং: কোন মেথড ব্যবহার করব?
এই রিপো দুটি পদ্ধতি শোকোস করে Ruby C extension-এ কমপ্লেক্স API সেটআপ করার — রিক্যাপ:
rb_define_method+rb_scan_args- Ruby interface
পারফরম্যান্স তুলনা করলে মोटামুটি সমান (Ruby parsing 1.02x faster on average on my M1)। বড় ডিফারেন্স নেই।
RGeo-তে আমাদের প্রথম ডিজাইন চয়েস ছিল variadic length arguments only API। No keywords, no blocks। এটি limiting, এবং এখন আমরা Primitive way ব্যবহার করছি আরও কনভোলিউটেড আর্গুমেন্টের জন্য।
Ruby Interface ব্যবহারের সুবিধা
আমার পরামর্শ: Ruby interface ব্যবহার করুন কারণ:
- কোড সাইজ ছোট
- চেঞ্জ করা সহজ
মোটামুটি এই কোডবেস পড়ার এক্সপেরিয়েন্স সিম্পল করে। Ruby ইউজারদের gem-এর সোর্স কোড পড়তে engage করা গুরুত্বপূর্ণ। Ruby সহজ পড়া, তাই gems-ও সায়বা সহজ হওয়া উচিত।
rb_define_method ব্যবহারের সুবিধা
C ভার্সন কিছু খুব útil ইন্টারনাল মেথডের স্বাদ দেয়। rb_check_arity খুব useful। ব্লক হ্যান্ডলিং মেথডও great। Ruby facade ব্লকর জন্য লাগতে পারে না।
সেটা আপনার ইউজ কেসের জন্য best choice খুঁজে বের করার ব্যাপার।
উপসংহার
এই পোস্টে আমরা এক্সপ্লোর করেছি Ruby C extension-এ আর্গুমেন্ট পার্স করার দুটি মেথড — rb_define_method (লিমিটেশনসহ) এবং rb_scan_args দিয়ে পার্সিং, এবং Ruby interface ব্যবহার।
C extension সম্পর্কে আরও পড়তে চাইলে:
- Building a Ruby C Extension From Scratch
- A Rubyist's Walk Along the C-side
- Working with ruby C extensions on Mac
আমার চূড়ান্ত পরামর্শ: RGeo চেক আউট করুন। এটি широко ব্যবহৃত এক্টিভ ডেভেলপমেন্ট C extension কোডবেস। আমার অধিকাংশ উদাহরণ সেখান থেকে।
Happy coding!
P.S. Ruby Magic পোস্টগুলি প্রকাশ होते সাথে সাথে পেতে আমাদের Ruby Magic নিউজলেটার সাবস্ক্রাইব করুন!
Ulysse Buonomo
আমাদের অতিথি লেখক Ulysse একজন ফর্মার ইন্ডাস্ট্রি Ruby ডেভেলপার যারা অধিকাংশ সময় বিশ্ব ভ্রমণে ব্যতিত করেন। স্পেয়ার টাইম RGeo এবং Ruby-এ, এবং Ruby-এর ইন্টারনালস সাথে টিংকার করা পছন্দ।
Ulysse Buonomo-এর সব আর্টিকেল