আধুনিক সংকলকটিতে জেনেরিকগুলি কীভাবে প্রয়োগ করা হয়?


15

আমি এখানে যা বোঝাতে চাইছি তা হল আমরা কীভাবে কোনও উত্স থেকে T add(T a, T b) ...উত্পন্ন কোডে যাব ? আমি এটি অর্জনের কয়েকটি উপায় নিয়ে ভেবেছি, আমরা জেনেরিক ফাংশনটি একটি এএসটি হিসাবে সংরক্ষণ করি Function_Nodeএবং তারপরে যতবার আমরা এটি ব্যবহার করি তখন আমরা মূল ফাংশন নোডে সমস্ত প্রকারের সাথে Tপ্রতিস্থাপিত সকল প্রকারের সাথে একটি অনুলিপি সঞ্চয় করি are ব্যবহৃত. উদাহরণস্বরূপ add<int>(5, 6), জেনেরিক ফাংশনের একটি অনুলিপি সংরক্ষণ করবে addএবং এর সাথে T অনুলিপিতে সমস্ত প্রকারকে প্রতিস্থাপন করবেint

সুতরাং এটি দেখতে কিছু হবে:

struct Function_Node {
    std::string name; // etc.
    Type return_type;
    std::vector<std::pair<Type, std::string>> arguments;
    std::vector<Function_Node> copies;
};

তারপরে আপনি এগুলির জন্য কোড তৈরি করতে পারেন এবং যখন আপনি কোনও Function_Nodeঅনুলিপিটির তালিকাতে যান copies.size() > 0, আপনি visitFunctionসমস্ত অনুলিপিটিতে অনুরোধ করেন।

visitFunction(Function_Node& node) {
    if (node.copies.size() > 0) {
        for (auto& node : nodes.copies) {
            visitFunction(node);
        }
        // it's a generic function so we don't want
        // to emit code for this.
        return;
    }
}

এই ভাল কাজ করবে? আধুনিক সংকলকগণ কীভাবে এই সমস্যার কাছে যান? আমি মনে করি এটি করার অন্য একটি উপায় হতে পারে আপনি অনুলিপিগুলিকে এসটি তে ইনজেক্ট করতে পারেন যাতে এটি সমস্ত শব্দার্থক পর্যায়ে চলে। আমি ভেবেছিলাম সম্ভবত আপনি এগুলি জাস্টের এমআইআর বা সুইফট এসআইএল এর মতো তাত্ক্ষণিক আকারে তৈরি করতে পারেন উদাহরণস্বরূপ।

আমার কোডটি জাভাতে লেখা হয়েছে, উদাহরণগুলি এখানে সি ++ কারণ এটি উদাহরণের জন্য কিছুটা কম ভার্বোস - তবে মূলনীতিটি মূলত একই জিনিস। যদিও কয়েকটি ত্রুটি হতে পারে কারণ এটি কেবল প্রশ্ন বাক্সে হাতে লেখা।

মনে রাখবেন যে আমি এই সমস্যাটির কাছে যাওয়ার সর্বোত্তম উপায়টি হিসাবে আধুনিক সংকলক বলতে চাই। এবং আমি যখন জেনেরিক বলি তখন আমি জাভা জেনেরিকের মতো বোঝাতে চাই না যেখানে তারা টাইপ ক্ষয় ব্যবহার করে।


সি ++ (অন্যান্য প্রোগ্রামিং ভাষার জেনেরিকস রয়েছে তবে তারা প্রত্যেকে একে আলাদাভাবে প্রয়োগ করে), এটি মূলত একটি দৈত্যাকার, সংকলন-সময় ম্যাক্রো সিস্টেম। আসল কোডটি বিকল্প ধরনের ব্যবহার করে তৈরি করা হয়।
রবার্ট হার্ভে

মুছে ফেলুন না কেন? মনে রাখবেন এটি কেবল জাভা নয় যা এটি করে এবং এটি কোনও খারাপ কৌশল নয় (আপনার প্রয়োজনীয়তার উপর নির্ভর করে)।
Andres F.

@AndresF। আমি মনে করি যে আমার ভাষাটি যেভাবে কাজ করে তা দেওয়া ভাল, এটি কার্যকর হবে না।
জোন ফ্লো

2
আমি মনে করি আপনি কী ধরণের জেনেরিকের কথা বলছেন তা স্পষ্ট করে দেওয়া উচিত। উদাহরণস্বরূপ, সি ++ টেম্পলেট, সি # জেনেরিকস এবং জাভা জেনেরিকগুলি একে অপরের থেকে খুব আলাদা। আপনি বলেছেন যে আপনি জাভা জেনেরিকগুলি বোঝাতে চাইছেন না, তবে আপনি কী বোঝাতে চেয়েছেন তা বলবেন না।
সুইভ

2
মাত্রাতিরিক্ত প্রশস্ত হওয়া এড়াতে এটিকে সত্যই একটি ভাষার সিস্টেমে ফোকাস করা দরকার
ডেনিথ

উত্তর:


14

আধুনিক সংকলকটিতে জেনেরিকগুলি কীভাবে প্রয়োগ করা হয়?

আমি আপনাকে আধুনিক সংকলকটির উত্স কোডটি পড়তে আমন্ত্রণ জানিয়েছি যদি আপনি জানতে চান যে কোনও আধুনিক সংকলক কীভাবে কাজ করে। আমি রোজলিন প্রকল্প দিয়ে শুরু করব, যা সি # এবং ভিজ্যুয়াল বেসিক সংকলকগুলি কার্যকর করে।

বিশেষত আমি সি # সংকলকটিতে কোডটির দিকে আপনার দৃষ্টি আকর্ষণ করছি যা টাইপ প্রতীকগুলি প্রয়োগ করে:

https://github.com/dotnet/roslyn/tree/master/src/Compilers/CSharp/Portable/Symbols

এবং আপনি রূপান্তরকরণের নিয়মের কোডটিও দেখতে চাইতে পারেন। জেনেরিক ধরণের বীজগণিত ম্যানিপুলেশনের সাথে সম্পর্কিত এমন অনেক কিছুই রয়েছে।

https://github.com/dotnet/roslyn/tree/master/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions

দ্বিতীয়টি পড়তে সহজ করার জন্য আমি প্রচুর চেষ্টা করেছি।

আমি এটি অর্জনের কয়েকটি উপায় নিয়ে ভেবেছি, আমরা জেনেরিক ফাংশনটিকে একটি এএসটিতে ফাংশন_নোড হিসাবে সংরক্ষণ করি এবং তারপরে প্রতিটি সময় আমরা মূল ফাংশন নোডে সংরক্ষণ করি এবং সমস্ত ধরণের টি দিয়ে আলাদা করে আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা আলাদা प्रोटीट যে ব্যবহার করা হচ্ছে।

আপনি জেনেরিকগুলি নয়, টেমপ্লেটগুলি বর্ণনা করছেন । সি # এবং ভিজ্যুয়াল বেসিকের টাইপ সিস্টেমে প্রকৃত জেনারিক রয়েছে।

সংক্ষেপে, তারা এই মত কাজ।

  • সংকলনের সময় আনুষ্ঠানিকভাবে কোনও ধরণের গঠন করার জন্য আমরা বিধিগুলি প্রতিষ্ঠা করে শুরু করি। উদাহরণস্বরূপ: intএকটি প্রকার, একটি প্রকারের প্যারামিটার Tএকটি প্রকার, যে কোনও প্রকারের জন্য Xঅ্যারের প্রকারটিও X[]একটি প্রকার, ইত্যাদি।

  • জেনেরিকদের নিয়মগুলি প্রতিস্থাপনের সাথে জড়িত। উদাহরণস্বরূপ, class C with one type parameterকোনও প্রকার নয়। এটি ধরণের তৈরির জন্য একটি নিদর্শন। class C with one type parameter called T, under substitution with int for T হয় একটি টাইপ।

  • প্রকারের মধ্যে সম্পর্কের বর্ণনা দেওয়ার নিয়মগুলি - নিয়োগের উপর সামঞ্জস্যতা, কোনও এক্সপ্রেশনের ধরণ কীভাবে নির্ধারণ করা যায় ইত্যাদি ইত্যাদি - সংকলকটিতে নকশাকৃত এবং প্রয়োগ করা হয়েছে।

  • একটি বাইটকোড ভাষা যা এর মেটাডেটা সিস্টেমে জেনেরিক ধরণের সমর্থন করে তা ডিজাইন এবং প্রয়োগ করা হয়েছে।

  • রানটাইমের সময় জেআইটি সংকলক বাইটোকডটিকে মেশিন কোডে পরিণত করে; এটি একটি জেনেরিক বিশেষায়নের ভিত্তিতে উপযুক্ত মেশিন কোড তৈরির জন্য দায়বদ্ধ।

সুতরাং উদাহরণস্বরূপ, সি # তে যখন আপনি বলবেন

class C<T> { public void X(T t) { Console.WriteLine(t); } }
...
var c = new C<int>(); 
c.X(123);

তারপরে সংকলকটি যাচাই করে যে C<int>, আর্গুমেন্টের intজন্য একটি বৈধ প্রতিস্থাপন T, এবং সেই অনুসারে মেটাডেটা এবং বাইটকোড উত্পন্ন করে। রানটাইমের সময়, জিটারটি সনাক্ত করে যে একটি C<int>প্রথমবারের জন্য তৈরি হচ্ছে এবং যথাযথ মেশিন কোডটি গতিশীলভাবে তৈরি করে।


9

জেনেরিকগুলির বেশিরভাগ বাস্তবায়ন (বা বরং: প্যারামেট্রিক পলিমারফিজম) প্রকার ক্ষয়টি ব্যবহার করে। এটি জেনেরিক কোড সংকলনের সমস্যাটি ব্যাপকভাবে সরল করে, তবে কেবল বাক্সযুক্ত প্রকারের জন্যই কাজ করে: যেহেতু প্রতিটি যুক্তি কার্যকরীভাবে একটি অস্বচ্ছ পয়েন্টার, তাই আমাদের আর্গুমেন্টগুলিতে ক্রিয়াকলাপ করতে একটি ভিটিবেল বা অনুরূপ প্রেরণ প্রক্রিয়া দরকার। জাভাতে:

<T extends Addable> T add(T a, T b) { … }

সংকলন করা যায়, টাইপ-চেক করা যায় এবং একইভাবে বলা যেতে পারে

Addable add(Addable a, Addable b) { … }

জেনেরিকগুলি ব্যতীত কল সাইটে আরও তথ্যের সাথে টাইপ চেকার সরবরাহ করে। এই অতিরিক্ত তথ্য টাইপ ভেরিয়েবলগুলি দিয়ে পরিচালনা করা যায় , বিশেষত যখন জেনেরিক প্রকারগুলি অনুমান করা হয়। টাইপ চেকিংয়ের সময়, প্রতিটি জেনেরিক টাইপ একটি ভেরিয়েবলের সাথে প্রতিস্থাপন করা যেতে পারে, আসুন এটি কল করুন $T1:

$T1 add($T1 a, $T1 b)

পরিবর্তনশীল টাইপটি ততক্ষণে পরিচিত হওয়ার সাথে সাথে আরও তথ্যগুলির সাথে আপডেট করা হয় যতক্ষণ না এটি কংক্রিটের ধরণের সাথে প্রতিস্থাপন করা যায়। টাইপ চেকিং অ্যালগরিদম এমনভাবে লিখতে হবে যা এই ধরণের ভেরিয়েবলগুলিকে পুরোপুরি এখনও সমাধান না করে এমনভাবে সামঞ্জস্য করে। জাভা নিজেই এটি সাধারণত সহজেই করা যায় যেহেতু প্রায়শই ফাংশন কলটির প্রকারটি জানা প্রয়োজন আগে যুক্তিগুলির ধরণটি প্রায়শই জানা যায়। একটি উল্লেখযোগ্য ব্যতিক্রম হ'ল ফাংশন আর্গুমেন্ট হিসাবে ল্যাম্বডা এক্সপ্রেশন, যা এই জাতীয় ধরণের ভেরিয়েবলগুলির প্রয়োজন।

অনেক পরে, একটি অপ্টিমাইজার নির্দিষ্ট যুক্তিগুলির জন্য বিশেষায়িত কোড তৈরি করতে পারে, এটি তখন কার্যকরভাবে এক ধরণের ইনলাইনিং হতে পারে।

জেনেরিক-টাইপযুক্ত আর্গুমেন্টগুলির জন্য একটি ভিটিবেল এড়ানো যায় যদি জেনেরিক ফাংশন টাইপটিতে কোনও ক্রিয়াকলাপ না চালায় তবে কেবল সেগুলি অন্য ফাংশনে প্রেরণ করে। যেমন হাস্কেল ফাংশনটিতে আর্গুমেন্টটি call :: (a -> b) -> a -> b; call f x = f xবাক্স করতে হবে না x। যাইহোক, এর জন্য একটি কলিং কনভেনশন দরকার যা তাদের আকারগুলি না জেনে মানগুলির মধ্য দিয়ে যেতে পারে, যা মূলত এটি কোনওভাবে পয়েন্টারে সীমাবদ্ধ করে।


এই ক্ষেত্রে বেশিরভাগ ভাষার থেকে সি ++ খুব আলাদা। একটি টেম্প্লেটেড ক্লাস বা ফাংশন (আমি কেবল এখানে টেম্পলেটযুক্ত ফাংশনগুলি নিয়ে আলোচনা করব) নিজে কল হয় না। পরিবর্তে, টেমপ্লেটগুলি একটি সংকলন-সময় মেটা-ফাংশন হিসাবে বোঝা উচিত যা কোনও আসল ফাংশন দেয়। এক মুহুর্তের জন্য টেমপ্লেট আর্গুমেন্ট অনুমান উপেক্ষা করে, সাধারণ পদ্ধতির পরে এই পদক্ষেপগুলিতে সিদ্ধ হয়:

  1. প্রদত্ত টেমপ্লেট আর্গুমেন্টগুলিতে টেম্পলেটটি প্রয়োগ করুন। উদাহরণস্বরূপ কল template<class T> T add(T a, T b) { … }করা add<int>(1, 2)আমাদের আসল ফাংশন দেবে int __add__T_int(int a, int b)(বা নাম-ম্যাঙ্গলিংয়ের যে কোনও পদ্ধতিই ব্যবহৃত হয়)।

  2. যদি এই ক্রিয়াকলাপের কোডটি ইতিমধ্যে বর্তমান সংকলন ইউনিটে উত্পন্ন হয়েছে, চালিয়ে যান। অন্যথায়, কোডটি তৈরি করুন যেন কোনও int __add__T_int(int a, int b) { … }উত্স কোডটিতে কোনও ফাংশন লেখা হয়েছিল। এটিতে টেমপ্লেট যুক্তির সমস্ত উপস্থিতি এর মানগুলির সাথে প্রতিস্থাপন জড়িত। এটি সম্ভবত একটি এএসটি-এএসটি রূপান্তর। তারপরে, উত্পন্ন এএসটি-তে টাইপ চেকিং সম্পাদন করুন।

  3. কলটি কম্পাইল করুন যেন উত্স কোডটি ছিল __add__T_int(1, 2)

নোট করুন যে সি ++ টেম্পলেটগুলির ওভারলোড রেজোলিউশন ব্যবস্থার সাথে একটি জটিল ইন্টারঅ্যাকশন রয়েছে, যা আমি এখানে বর্ণনা করতে চাই না। এছাড়াও মনে রাখবেন যে এই কোড-জেনারেশনটি একটি ভার্চুয়াল এমন একটি টেম্প্লেটেড পদ্ধতি থাকা অসম্ভব করে তোলে - একটি টাইপ-ইরেজর ভিত্তিক পদ্ধতির এই যথেষ্ট বাধা থেকে ভোগ হয় না।


আপনার সংকলক এবং / অথবা ভাষার জন্য এর অর্থ কী? আপনি যে ধরণের জেনেরিক অফার করতে চান সে সম্পর্কে আপনাকে সাবধানে চিন্তা করতে হবে। টাইপ অনুমানের অনুপস্থিতিতে প্রকার মুছে ফেলা হ'ল যদি আপনি বাক্সযুক্ত প্রকারকে সমর্থন করেন তবে সবচেয়ে সহজ সম্ভাবনা approach টেমপ্লেট বিশেষায়িতকরণটি মোটামুটি সহজ বলে মনে হয় তবে নাম ম্যাগলিং এবং (একাধিক সংকলন ইউনিটের জন্য) আউটপুটে যথেষ্ট নকল জড়িত, যেহেতু টেমপ্লেটগুলি কল সাইটে ইনস্ট্যান্ট হয়, সংজ্ঞা সাইট নয়।

আপনি যে অ্যাপ্রোচটি দেখিয়েছেন তা হ'ল মূলত একটি সি ++ - টেম্পলেট পদ্ধতির মতো। তবে, আপনি বিশেষায়িত / ইনস্ট্যান্টিয়েটেড টেম্পলেটগুলি মূল টেমপ্লেটের "সংস্করণ" হিসাবে সঞ্চয় করেন। এটি বিভ্রান্তিকর: এগুলি ধারণাগতভাবে একই নয় এবং কোনও ফাংশনের বিভিন্ন তাত্ক্ষণিকভাবে বন্যভাবে বিভিন্ন ধরণের থাকতে পারে। আপনি যদি ফাংশন ওভারলোডিংকেও অনুমতি দেন তবে এটি দীর্ঘ সময়ের বিষয়গুলিকে জটিল করবে। পরিবর্তে, আপনার একটি ওভারলোড সেটের একটি ধারণা দরকার যাতে একটি নাম ভাগ করে নেওয়ার সমস্ত সম্ভাব্য ফাংশন এবং টেম্পলেট রয়েছে। অতিরিক্ত লোডিং সমাধানের জন্য ব্যতীত, আপনি বিভিন্ন তাত্ক্ষণিক টেম্পলেট একে অপরের থেকে সম্পূর্ণ আলাদা বিবেচনা করতে পারেন।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.