সি -তে জেনেরিক পাত্রে: নিরাপদ বিভাগ ব্যবহার করে।
মার্টিন ইউকার, 2025-08-10
আমি সি -তে টাইপ এবং সীমানা নিরাপদ জেনেরিক পাত্রে প্রয়োগের বিষয়ে আলোচনা করি পূর্বে, আমি একটি স্প্যান টাইপ নিয়ে আলোচনা করেছি, অ্যারে ব্যবহার করে সীমানা চেকিং করছি। এবং একটি ভেক্টর টাইপ।
এবার আমি আলোচনা করব maybe
হাস্কেল দ্বারা অনুপ্রাণিত। এই ধরণের এমন কোনও মান ফেরত দিতে ব্যবহৃত হতে পারে যা বিদ্যমান নাও থাকতে পারে, যেমন গণনার সময় একটি ত্রুটি দেখা হয়েছিল। নিম্নলিখিত উদাহরণগুলি একটি জন্য দেখায় divide
ফাংশন যা শূন্য দ্বারা বিভাগকে ধরা দেয়।
static maybe(int) divide(int a, int b)
return (b != 0) ? maybe_just(int, a / b) : maybe_nothing(int);
তবে সাবধানতা অবলম্বন করুন, এখানে আরও একটি ত্রুটি কেস চেক করা হয়নি! এটা কোনটি?
যথারীতি, আমরা এটিকে কেবল ম্যাক্রো হিসাবে সংজ্ঞায়িত করতে পারি যা কোনও কাঠামোতে প্রসারিত হয় এবং সাধারণ ধরণের নির্মাণকারীকে সংজ্ঞায়িত করতে পারে।
#define maybe(T) struct maybe_##T bool ok; T value;
#define maybe_just(T, x) (maybe(T)) .value = (x), .ok = true
#define maybe_nothing(T) (maybe(T)) .value = (T) , .ok = false
কলারে, আমরা তারপরে মানটি বিদ্যমান কিনা তা পরীক্ষা করতে পারি।
int main()
int d = 2; // 0
maybe(int) p = divide(6, d);
if (p.ok)
printf("%d\n", p.value);
else
printf("division by zero\n");
fflush(stdout);
return 0;
আমরা কি এটি ব্যবহার করতে নিরাপদ করতে পারি? নীতিগতভাবে, আমরা যদি মানটি ব্যবহার করার চেষ্টা করি তবে এটির অস্তিত্ব না থাকলেও আমরা কিছু ত্রুটি পেতে চাই। এর জন্য, আমরা একটি ম্যাক্রো যুক্ত করি maybe_value
যা একটি চেক অন্তর্ভুক্ত।
#define maybe_value(T, x) (*( maybe(T) *_p = &(x); _p->ok ? &_p->value : (void*)0; ))
এখানে, ত্রুটির শর্তটি পরিচালনা করার পরিবর্তে, আমি এমন একটি লভ্যালু তৈরি করি যা কোনও ত্রুটির ক্ষেত্রে কোথাও নির্দেশ করে না কারণ এটি তার সাথে মিলে যায়
(*( (void*)0; ))
এটিকে সুরক্ষার জন্য রান-টাইম ট্র্যাপে রূপান্তর করতে নাল স্যানিটাইজারের উপর নির্ভর করা।
maybe(int) p = divide(6, d);
if (p.ok)
printf("%d\n", maybe_value(p));
আপনি এখানে সম্পূর্ণ উদাহরণ খুঁজে পেতে পারেন: গডবোল্ট
তবে উপরে উল্লিখিত হিসাবে, আরও একটি কেস রয়েছে যেখানে পূর্ণসংখ্যা বিভাগ সি -তে অপরিজ্ঞাত আচরণ করে তবে আমরা যদি বিয়োগের এক দ্বারা ক্ষুদ্রতম প্রতিনিধিত্বযোগ্য পূর্ণসংখ্যাটি ভাগ করি, তবে ফলাফলটি বৃহত্তম প্রতিনিধিত্বযোগ্য পূর্ণসংখ্যার চেয়ে একটি বড়। এর জন্য একটি পরীক্ষাও যুক্ত করা যাক!
maybe(int) unsafe_divide(int a, int b)
if (b == -1 && a == INT_MAX)
return maybe_nothing(int);
return (b != 0) ? maybe_just(int, a / b) : maybe_nothing(int);
সুতরাং আমরা পূর্ণসংখ্যা বিভাগের জন্য একটি নিরাপদ ফাংশন তৈরি করেছি। তবে আমরা কি নিশ্চিত হতে পারি যে এটি নিরাপদ? সম্ভবত আমরা ভুল করেছি। এখন, এখানে সরঞ্জাম এবং একটি সম্পূর্ণ শিল্প রয়েছে যা এটিতে সহায়তা করতে সক্ষম হতে পারে তবে এর পরিবর্তে প্রথমে কেবল এটি দেখুন
সমাবেশ ট্র্যাপিং মোডে স্বাক্ষরিত ওভারফ্লো স্যানিটাইজার ব্যবহার করার সময় জিসিসি দ্বারা উত্পাদিত
-O2 -fsanitize=signed-integer-overflow,integer-divide-by-zero -fsanitize-trap=undefined
।
unsafe_divide:
cmp esi, -1
sete dl
cmp edi, 2147483647
jne .L2
test dl, dl
je .L2
.L4:
xor eax, eax
ret
.L2:
test esi, esi
je .L4
cmp edi, -2147483648
je .L20
mov eax, edi
cdq
idiv esi
sal rax, 32
or rax, 1
ret
.L20:
test dl, dl
jne .L18
mov eax, edi
cdq
idiv esi
sal rax, 32
or rax, 1
ret
safe_divide.cold:
.L18:
ud2
এটি অদ্ভুত, এখনও একটি কোড পাথ রয়েছে যা আকারে একটি ফাঁদে শেষ হয় ud2
নির্দেশ। সুতরাং হয় অপ্টিমাইজারটি দেখতে সক্ষম হয় নি যে এটি সম্ভব নয় বা আমাদের চেকটি ভুল ছিল। আসলে, আমি এটি ভুল পেয়েছি এবং আমাদের বিরুদ্ধে পরীক্ষা করতে হবে INT_MIN
এবং না INT_MAX
। এখানে সংশোধন এবং সুন্দর সংস্করণ।
maybe(int) safe_divide(int a, int b)
if (b == 0
দ্য সমাবেশ এখন অন্যরকম দেখায় এবং একটিতে নেতৃত্বে একটি কোড পাথ থাকে না ud2
আর।
safe_divide:
test esi, esi
je .L2
cmp esi, -1
jne .L3
cmp edi, -2147483648
je .L2
.L3:
mov eax, edi
cdq
idiv esi
sal rax, 32
or rax, 1
ret
.L2:
xor eax, eax
ret
অপ্টিমাইজারটি প্রমাণ করেছে যে আমাদের ফাংশনে শূন্য বামে কোনও ওভারফ্লো বা বিভাগ নেই! এটি কি আমাদের সম্পূর্ণের জন্যও কাজ করে? উদাহরণ
ব্যবহার maybe
? এটা! অপ্টিমাইজারটি স্থিতিশীলভাবে দেখিয়েছে যে কোনও ওভারফ্লো নেই এবং সমস্ত ত্রুটি কেস পরিচালনা করা হয়েছে। এটা কি দুর্দান্ত নয়! কেউ এখন স্যানিটাইজারটি বন্ধ করতে পারে এবং এখনও নিশ্চিত হতে পারে যে কোনও ওভারফ্লো সম্ভব নেই, কারণ এটি স্থিতিশীলভাবে প্রমাণিত হয়েছিল।
অবশ্যই, এটি দেখানোর জন্য ব্যবহার করা যাবে না যে সি প্রোগ্রামগুলি সম্পূর্ণরূপে মেমরি নিরাপদ, কারণ সেখানে এমন অঞ্চলগুলি যা স্যানিটাইজারদের দ্বারা আচ্ছাদিত নয়, এবং এমন স্যানিটাইজারও রয়েছে যা তাদের নিজ নিজ ডোমেনে সমস্ত অপরিবর্তিত আচরণকে ধরে না। বিশেষত, আজীবন সমস্যা এবং পয়েন্টার গাণিতিক আচ্ছাদিত নয়। সুতরাং, একজনকে এইভাবে জোর দিতে হবে যে এই পদ্ধতিতে লিগ্যাসি সি প্রোগ্রামগুলির সুরক্ষা বৈশিষ্ট্যগুলি প্রমাণ করার চেষ্টা করার সময় এই পদ্ধতির খুব সীমাবদ্ধ। তবুও, আপনি যদি পূর্বে আলোচিত হিসাবে পয়েন্টার গাণিতিকগুলির পরিবর্তে ভ্লাস এবং ভেরিয়েলিভাবে পরিবর্তিত প্রকারগুলি ব্যবহার করেন তবে আপনি এমনকি আপনার সীমানা পরীক্ষা করতে পারেন; নিজের জন্য দেখুন উদাহরণআর!
আপনি যদি চান তবে আপনি আমার পরীক্ষামূলক গ্রন্থাগারটি পরীক্ষা করে দেখতে পারেন যেখানে আমি এই ধারণাগুলি নিয়ে পরীক্ষা করছি: লিঙ্ক। কীভাবে এটি আরও ভাল করতে হয় সে সম্পর্কে আপনার যদি ধারণা থাকে তবে আমাকে জানান!