কেন একটি অস্বচ্ছ "হ্যান্ডেল" ব্যবহার করবেন যার জন্য টাইপসেফ স্ট্রাক পয়েন্টার না করে পাবলিক এপিআইতে কাস্টিং প্রয়োজন?


27

আমি এমন একটি লাইব্রেরির মূল্যায়ন করছি যার পাবলিক এপিআই বর্তমানে এর মতো দেখাচ্ছে:

libengine.h

/* Handle, used for all APIs */
typedef size_t enh;


/* Create new engine instance; result returned in handle */
int en_open(int mode, enh *handle);

/* Start an engine */
int en_start(enh handle);

/* Add a new hook to the engine; hook handle returned in h2 */
int en_add_hook(enh handle, int hooknum, enh *h2);

নোট যে enhএকটি জেনেরিক হ্যান্ডেল, বিভিন্ন ডিটাটাইপগুলি ( ইঞ্জিন এবং হুকস ) এর হ্যান্ডেল হিসাবে ব্যবহৃত ।

অভ্যন্তরীণভাবে, এপিআইয়ের বেশিরভাগ অবশ্যই অভ্যন্তরীণ কাঠামোতে "হ্যান্ডেল" ফেলে দেয় যা তারা করেছে malloc:

engine.c

struct engine
{
    // ... implementation details ...
};

int en_open(int mode, *enh handle)
{
    struct engine *en;

    en = malloc(sizeof(*en));
    if (!en)
        return -1;

    // ...initialization...

    *handle = (enh)en;
    return 0;
}

int en_start(enh handle)
{
    struct engine *en = (struct engine*)handle;

    return en->start(en);
}

ব্যক্তিগতভাবে, আমি জিনিসগুলির পিছনে জিনিসগুলি গোপন করা ঘৃণা করি typedef, বিশেষত যখন এটি ধরণের সুরক্ষার সাথে আপস করে। (একটি দেওয়া হল enh, আমি এটি কীভাবে জানি যা এটি আসলে উল্লেখ করছে?)

সুতরাং আমি নীচের অনুরোধটি জমা দিলাম, নিম্নলিখিত এপিআই পরিবর্তনের পরামর্শ দিয়েছি ( পুরো লাইব্রেরিটি অনুসারে সংশোধন করার পরে ):

libengine.h

struct engine;           /* Forward declaration */
typedef size_t hook_h;    /* Still a handle, for other reasons */


/* Create new engine instance, result returned in en */
int en_open(int mode, struct engine **en);

/* Start an engine */
int en_start(struct engine *en);

/* Add a new hook to the engine; hook handle returned in hh */
int en_add_hook(struct engine *en, int hooknum, hook_h *hh);

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

libengine.c

struct engine
{
    // ... implementation details ...
};

int en_open(int mode, struct engine **en)
{
    struct engine *_e;

    _e = malloc(sizeof(*_e));
    if (!_e)
        return -1;

    // ...initialization...

    *en = _e;
    return 0;
}

int en_start(struct engine *en)
{
    return en->start(en);
}

আমি নিম্নলিখিত কারণে এটি পছন্দ:

যাইহোক, প্রকল্পের মালিকটি টানার অনুরোধটি উপেক্ষা করলেন (প্যারাফ্রেসড):

ব্যক্তিগতভাবে আমি প্রকাশের ধারণা পছন্দ করি না struct engine। আমি এখনও মনে করি বর্তমান উপায়টি পরিষ্কার এবং আরও বন্ধুত্বপূর্ণ।

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

আসুন দেখে নেওয়া যাক এই জনসংযোগ সম্পর্কে অন্যরা কী ভাবছে।

এই গ্রন্থাগারটি বর্তমানে একটি বেসরকারী বিটা পর্যায়ে রয়েছে, তাই চিন্তার মতো ভোক্তার কোড নেই (এখনও) isn't এছাড়াও, আমি নামগুলি কিছুটা অবহেলা করেছি।


নামযুক্ত, অস্বচ্ছ স্ট্রাক্টের চেয়ে কীভাবে অস্বচ্ছ হ্যান্ডেলটি ভাল?

দ্রষ্টব্য: আমি কোড রিভিউতে এই প্রশ্নটি জিজ্ঞাসা করেছি , যেখানে এটি বন্ধ ছিল।


1
আমি এমন শিরোনামটি সম্পাদনা করেছি যা আমি বিশ্বাস করি যে আপনার প্রশ্নের মূলটি আরও স্পষ্টভাবে প্রকাশ করে। আমি যদি এটির ভুল ব্যাখ্যা দিয়ে থাকে তবে নির্দ্বিধায় উদ্বিগ্ন।
Ixrec

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

উত্তর:


33

"সিম্পল ইজ ইজ বেটার" মন্ত্রটি অনেক বেশি কৌতুকপূর্ণ হয়ে উঠেছে। সরল সবসময় আরও ভাল হয় না যদি এটি অন্যান্য জিনিসগুলিকে জটিল করে তোলে। সমাবেশটি সহজ - প্রতিটি কমান্ড উচ্চ-স্তরের ভাষাগুলির কমান্ডের চেয়ে অনেক সহজ - এবং তবুও সমাবেশ সংক্রান্ত প্রোগ্রামগুলি একই কাজ করে এমন উচ্চ-স্তরের ভাষার চেয়ে জটিল Assembly আপনার ক্ষেত্রে, অভিন্ন হ্যান্ডেল enhটাইপগুলি ফাংশনগুলিকে জটিল করার ব্যয়ে প্রকারগুলি আরও সহজ করে তোলে। যেহেতু সাধারণত প্রকল্পের প্রকারগুলি তার কার্যকারণের তুলনায় উপ-লিনিয়ার হারে প্রবৃদ্ধি লাভ করে, প্রকল্পটি বড় হওয়ার সাথে সাথে আপনি সাধারণত আরও জটিল ধরণের পছন্দ করেন যদি এটি ফাংশনগুলি আরও সহজ করে তুলতে পারে - সুতরাং এই ক্ষেত্রে আপনার দৃষ্টিভঙ্গি সঠিক বলে মনে হচ্ছে।

প্রকল্পের লেখক উদ্বিগ্ন যে আপনার দৃষ্টিভঙ্গি " এক্সপোজেশনstruct engine " করছে। আমি তাদের বুঝিয়ে দিতাম যে এটি স্ট্রাক্টটি নিজেই প্রকাশ করছে না - কেবল এই কারণেই একটি কাঠামোর নাম রয়েছে engine। গ্রন্থাগারের ব্যবহারকারীর ইতিমধ্যে সেই ধরণের সম্পর্কে সচেতন হওয়া দরকার - তাদের জানতে হবে, উদাহরণস্বরূপ, প্রথম যুক্তিটি en_add_hookসেই ধরণের এবং প্রথম যুক্তিটি ভিন্ন ধরণের। সুতরাং এটি আসলে এপিআইকে আরও জটিল করে তোলে কারণ ফাংশনের "স্বাক্ষর" নথির পরিবর্তে এই ধরণের ডকুমেন্টগুলি অন্য কোথাও নথিভুক্ত করা দরকার, এবং কারণ সংকলক আর প্রোগ্রামারটির জন্য প্রকারগুলি যাচাই করতে পারে না।

একটি জিনিস যা লক্ষ করা উচিত - আপনার নতুন এপিআই লেখার পরিবর্তে ব্যবহারকারী কোডটিকে কিছুটা জটিল করে তোলে:

enh en;
en_open(ENGINE_MODE_1, &en);

তাদের হ্যান্ডেলটি ঘোষণার জন্য তাদের আরও জটিল বাক্য গঠন প্রয়োজন:

struct engine* en;
en_open(ENGINE_MODE_1, &en);

সমাধানটি তবে বেশ সহজ:

struct _engine;
typedef struct _engine* engine

এবং এখন আপনি সোজাভাবে লিখতে পারেন:

engine en;
en_open(ENGINE_MODE_1, &en);

আমি উল্লেখ করতে ভুলে গেছি যে গ্রন্থাগারটি লিনাক্স কোডিং স্টাইল অনুসরণ করার দাবি করে , যা আমি অনুসরণ করি তা ঘটে। সেখানে, আপনি দেখতে পাবেন যে টাইপডেফিং স্ট্রাকচারগুলি কেবল লেখা এড়ানোর structজন্য স্পষ্টভাবে নিরুৎসাহিত করা হচ্ছে।
জোনাথন রেইনহার্ট

@ জোনাথনরাইনহার্ট তিনি স্ট্রাক্টটিই নয় বরং স্ট্রাক্টকে স্ট্রাক্ট করার জন্য পয়েন্টারটি টাইপ করছেন ।
ratchet freak

@ জোনাথনরাইনহার্ট এবং প্রকৃতপক্ষে সেই লিঙ্কটি পড়ে আমি দেখতে পাচ্ছি যে "সম্পূর্ণ অস্বচ্ছ বস্তুগুলির" জন্য এটি অনুমোদিত। (অধ্যায় 5 নিয়ম ক)
রাচেট ফ্রিক

হ্যাঁ, তবে কেবলমাত্র ব্যতিক্রমী ক্ষেত্রেই। আমি সত্যই বিশ্বাস করি যে পিটি টাইপিডেফসকে মোকাবেলা করতে সমস্ত মিমি কোড পুনরায় লেখা এড়ানোর জন্য যুক্ত করা হয়েছিল। স্পিন লক কোডটি দেখুন। এটি সম্পূর্ণ আর্চ নির্দিষ্ট (কোনও সাধারণ ডেটা নেই) তবে তারা কখনই টাইপডেফ ব্যবহার করে না।
জোনাথন রাইনহার্ট

8
আমি পছন্দ করতে typedef struct engine engine;এবং ব্যবহার করতে চাই engine*: আরও একটি নাম প্রবর্তিত, এবং এটি স্পষ্ট করে তোলে এটি হ্যান্ডেলের মতো FILE*
উত্সাহক

16

এখানে উভয় পক্ষেই বিভ্রান্তি রয়েছে বলে মনে হচ্ছে:

  • একটি হ্যান্ডেল পদ্ধতির ব্যবহারের জন্য সমস্ত হ্যান্ডেলের জন্য একক হ্যান্ডেল প্রকারের প্রয়োজন হয় না
  • প্রকাশক struct(শুধুমাত্র তার অস্তিত্ব) নাম এটির বিশদ বিবরণ দেখাবে না তা

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

তবে, একর মাধ্যমে সংজ্ঞায়িত এক ধরণের হ্যান্ডেল থাকার পদ্ধতিটি typedefনিরাপদ নয়, এবং প্রচুর শোকের কারণ হতে পারে।

আমার ব্যক্তিগত পরামর্শটি হ'ল টাইপ নিরাপদ হ্যান্ডলগুলির দিকে অগ্রসর হওয়া, যা আমি মনে করি আপনার উভয়কেই সন্তুষ্ট করবে। এটি বরং সহজভাবে সম্পাদিত হয়:

typedef struct {
    size_t id;
} enh;

typedef struct {
    size_t id;
} oth;

এখন, দুর্ঘটনাক্রমে কেউ 2হ্যান্ডেল হিসাবে পাস করতে পারে না বা ঘটনাক্রমে একজন ঝাঁকুনি দিয়ে কোনও ঝাঁকুনি দিতে পারে না যেখানে ইঞ্জিনের একটি হ্যান্ডেল আশা করা যায়।


তাই আমি নীচের অনুরোধটি জমা দিয়ে নীচের এপিআই পরিবর্তনের পরামর্শ দিয়েছি ( পুরো লাইব্রেরিটি অনুসারে সংশোধন করার পরে )

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


1
ধন্যবাদ. আপনি কি ঢোকা করা হয়নি কি হ্যান্ডলগুলি যদিও সঙ্গে। আমি একটি আসল হ্যান্ডেল ভিত্তিক এপিআই প্রয়োগ করেছি , যেখানে পয়েন্টারগুলি কখনই প্রকাশিত হয় না, এমনকি টাইপেফের মাধ্যমেও হয়। এটি প্রতিটি এপিআই কল এন্ট্রি করার সময় the ডেটা ব্যয়বহুল অনুসন্ধানের সাথে জড়িত ছিল - অনেকটা লিনাক্সের struct fileথেকে যেমন দেখায় int fd। এটি অবশ্যই ব্যবহারকারী-মোড লাইব্রেরি আইএমওর জন্য অতিমাত্রার কাজ।
জোনাথন রাইনহার্ট

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

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

2
@ রওং: নিরপেক্ষ প্রকল্পে এটি কেবল একটি সমস্যা; আপনি সহজেই একটি যুগের কাউন্টারকে সংহত করতে পারেন, উদাহরণস্বরূপ, যাতে কোনও পুরানো হ্যান্ডেল নির্দিষ্ট করা হয়, আপনি একটি যুগের অমিল পাবেন।
ম্যাথিউ এম।

1
@ জোনাথনরেইনহার্টের পরামর্শ: আরও গুরুত্বপূর্ণ বিষয়গুলির দিকে আলোচনার দিকে পরিচালিত করতে আপনি আপনার প্রশ্নে "কঠোর আলিয়াজিং বিধি" উল্লেখ করতে পারেন।
রওয়ং

3

এখানে একটি পরিস্থিতি যেখানে অস্বচ্ছ হ্যান্ডেল প্রয়োজন;

struct SimpleEngine {
    int type;  // always SimpleEngine.type = 1
    int a;
};

struct ComplexEngine {
    int type;  // always ComplexEngine.type = 2
    int a, b, c;
};

int en_start(enh handle) {
    switch(*(int*)handle) {
    case 1:
        // treat handle as SimpleEngine
        return start_simple_engine(handle);
    case 2:
        // treat handle as ComplexEngine
        return start_complex_engine(handle);
    }
}

লাইব্রেরিতে যখন দুটি বা ততোধিক স্ট্রাক্ট ধরণের ক্ষেত্রগুলির একই শিরোনামের অংশ থাকে, যেমন উপরের "টাইপ" এর মতো, এই স্ট্রাক্টগুলি সাধারণ প্যারেন্ট স্ট্রাক্ট (সি ++ তে একটি বেস শ্রেণীর মতো) হিসাবে বিবেচনা করা যেতে পারে।

আপনি শিরোনাম অংশটিকে "স্ট্রাক্ট ইঞ্জিন" হিসাবে সংজ্ঞায়িত করতে পারেন;

struct engine {
    int type;
};

struct SimpleEngine {
    struct engine base;
    int a;
};

struct ComplexEngine {
    struct engine base;
    int a, b, c;
};

int en_start(struct engine *en) { ... }

তবে এটি একটি alচ্ছিক সিদ্ধান্ত কারণ স্ট্রাক্ট ইঞ্জিনটি নির্বিশেষে টাইপ-কাস্টগুলির প্রয়োজন।

উপসংহার

কিছু ক্ষেত্রে, অস্বচ্ছ হ্যান্ডলগুলি অস্বচ্ছ নামযুক্ত স্ট্রাক্টগুলির পরিবর্তে ব্যবহৃত হওয়ার কারণ রয়েছে।


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

তবে আসলে switch"ভার্চুয়াল ফাংশন" ব্যবহার করে প্রথমে এড়ানো সম্ভবত আদর্শ, এবং পুরো বিষয়টি সমাধান করে issue
জোনাথন রাইনহার্ট

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

আচ্ছা আপনি যদি এটি সত্যিই সহজ রাখতে চান তবে আপনি ত্রুটি-চেকও পুরোপুরি বাদ দিতে পারেন!
জোনাথন রাইনহার্ট

আমার মতে, কিছু পরিমাণ ত্রুটি-পরীক্ষার চেয়ে ডিজাইনের সরলতা পছন্দ করা হয়। এই ক্ষেত্রে, এই জাতীয় ত্রুটি-পরীক্ষা কেবলমাত্র API ফাংশনে বিদ্যমান in তদ্ব্যতীত, আপনি ইউনিয়ন ব্যবহার করে টাইপ-কাস্টগুলি অপসারণ করতে পারেন, তবে মনে রাখবেন যে ইউনিয়নটি প্রাকৃতিকভাবে টাইপ-অনিরাপদ।
আকিও টাকাহাশি

2

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

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

যেহেতু আপনি আপনার ক্লায়েন্টগুলিকে যে কোনও উপায়ে কাঠামোগত জোগান দিচ্ছেন, তাই আপনি কাস্টিংয়ের প্রয়োজন এমন একটি সহজ সরল API এর জন্য কিছুটা সুরক্ষার (যা এখনও রানটাইমের সময় চেক করা যায়) ত্যাগ করেন।


5
"আপনি অভ্যন্তরীণ কাঠামোকে .. ছাড়াই সংশোধন করতে পারেন .." - আপনি সামনের ঘোষণার পদ্ধতির মাধ্যমেও পারেন।
ব্যবহারকারী 253751

"ফরওয়ার্ড ডিক্লেয়ারেশন" পদ্ধতির জন্য কি এখনও আপনাকে স্বাক্ষর প্রকারের ঘোষণার প্রয়োজন হয় না? এবং যদি আপনি স্ট্রাইকগুলি পরিবর্তন করেন তবে এই ধরণের স্বাক্ষরগুলি কি এখনও পরিবর্তন হয় না?
রবার্ট হার্ভে

ফরোয়ার্ড ঘোষণার জন্য কেবল আপনার প্রকারের নাম ঘোষণা করা দরকার - এটির কাঠামোটি লুকানো থাকে remains
ইদান আরে

তারপরে ফরওয়ার্ড ডিক্লেয়ারেশনের সুবিধা কী হবে যদি এটি এমনকি টাইপ স্ট্রাকচারটি প্রয়োগ করে না?
রবার্ট হার্ভে

6
@ রবার্টহার্ভে মনে রাখবেন - এটি সি আমরাই কথা বলছি। কোনও পদ্ধতি নেই, সুতরাং নাম এবং কাঠামো ব্যতীত অন্য ধরণের কিছু নেই। যদি করেনি গঠন জোরদার এটা নিয়মিত ঘোষণা অভিন্ন হতে হত। কাঠামোটি প্রয়োগ না করে নামটি প্রকাশ করার বিষয়টি হ'ল আপনি সেই ধরণের ফাংশন স্বাক্ষরে ব্যবহার করতে পারেন। অবশ্যই, কাঠামো ব্যতীত আপনি কেবল প্রকারের জন্য পয়েন্টার ব্যবহার করতে পারেন, যেহেতু সংকলকটি এটির আকারটি জানতে পারে না, তবে যেহেতু সিটিতে পয়েন্টারগুলি ব্যবহার করে কোনও সুরক্ষিত পয়েন্টার ingালাই আপনার রক্ষার জন্য স্ট্যাটিক-টাইপিংয়ের পক্ষে যথেষ্ট ভাল।
ইদান আরে

2

Déjà vu

নামযুক্ত, অস্বচ্ছ স্ট্রাক্টের চেয়ে কীভাবে অস্বচ্ছ হ্যান্ডেলটি ভাল?

আমি ঠিক একই দৃশ্যের মুখোমুখি হয়েছি , কেবল কিছু সূক্ষ্ম পার্থক্য নিয়ে। আমাদের এসডিকে আমরা এরকম অনেক কিছুই পেয়েছিলাম:

typedef void* SomeHandle;

আমার একমাত্র প্রস্তাবটি ছিল এটি আমাদের অভ্যন্তরীণ ধরণের সাথে মেলে:

typedef struct SomeVertex* SomeHandle;

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

* মনে রাখবেন, জেনেশার যেমন উল্লেখ করেছেন, সেখানে প্রকৃতপক্ষে এমন কিছু ঘটনা ঘটতে পারে যেখানে পয়েন্টার থেকে কাঠামোর মতো কিছু অকার্যকর হতে পারে * আসলে এটি ভিন্ন আকারের হতে পারে যেখানে এটি এবিআইকে প্রভাবিত করবে। তাঁর মতো, আমি বাস্তবে কখনও এর মুখোমুখি হইনি। তবে সেই দৃষ্টিকোণ থেকে, দ্বিতীয়টি কিছুটা অস্পষ্ট প্রেক্ষাপটে বহনযোগ্যতার উন্নতি করতে পারে, তাই এটি বেশিরভাগ লোকের পক্ষে সম্ভবত দ্বিতীয়টির পক্ষে থাকার পক্ষে দ্বিতীয় কারণ।

তৃতীয় পক্ষের বাগগুলি

তদুপরি, অভ্যন্তরীণ বিকাশ / ডিবাগিংয়ের জন্য টাইপ সুরক্ষার চেয়েও আমার আরও কারণ ছিল। আমাদের কাছে ইতিমধ্যে বেশ কয়েকটি প্লাগইন বিকাশকারী রয়েছে যার কোডগুলিতে বাগ রয়েছে কারণ দুটি অনুরূপ হ্যান্ডল ( Panelএবং PanelNew, উভয়ই void*হ্যান্ডলগুলির জন্য টাইপডেফ ব্যবহার করেছিল, এবং কেবলমাত্র ব্যবহারের ফলে তারা ভুলভাবে হ্যান্ডেলগুলিতে ভুল জায়গায় চলে যাচ্ছিল just void*সব কিছুর জন্য. সুতরাং এটি প্রকৃতপক্ষে যারা ব্যবহার করছেন তাদের পক্ষে বাগগুলি সৃষ্টি করছিলএসডিকে। তাদের বাগগুলির জন্য অভ্যন্তরীণ উন্নয়ন দলকেও প্রচুর সময় ব্যয় করতে হয়, যেহেতু তারা আমাদের এসডিকে বাগ সম্পর্কে অভিযোগকারী বাগ রিপোর্ট পাঠাত এবং আমাদের প্লাগইনটি ডিবাগ করতে হবে এবং এটি আবিষ্কার করতে হবে যে এটি প্লাগইনটিতে ভুল হ্যান্ডলগুলি পাস করার সময় একটি বাগের ফলে হয়েছিল actually ভুল জায়গাগুলিতে (যা প্রতিটি হ্যান্ডেল void*বা এর জন্য একটি উপনাম হয় যখন কোনও সতর্কতা ছাড়াই সহজেই অনুমোদিত হয় size_t)। আমরা অযথা কারণ দূরে সমস্ত অভ্যন্তরীণ তথ্য, এমনকি নিছক আত্মগোপন ধারণাগত বিশুদ্ধতা জন্য আমাদের ইচ্ছা দ্বারা তাদের পক্ষ থেকে সৃষ্ট ভুলের তৃতীয় পক্ষের জন্য একটি ডিবাগিং সেবা প্রদানের আমাদের সময় নষ্ট হয় তাই নাম আমাদের অভ্যন্তরীণ এর structs

টাইপেডেফ রাখছেন

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

টাইপ তথ্য

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

ধারণামূলক আদর্শ

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

ব্যবহারকারী-শেষ পছন্দ

এমনকি যারা এপিআই ব্যবহার করছেন তাদের কঠোর ব্যবহারকারীর পরিণতি থেকেও তারা কি কোনও বগি এপিআই বা এমন একটি এপিআই পছন্দ করবে যা ভালভাবে কাজ করে তবে এমন কোনও নাম প্রকাশ করে যা তারা বিনিময়ে খুব যত্ন সহকারে দেখাতে পারে? কারণ এটাই ব্যবহারিক বাণিজ্য। জেনেরিক প্রসঙ্গে অজান্তে প্রকারের তথ্য হারিয়ে ফেলা ত্রুটির ঝুঁকি বাড়িয়ে তুলছে, এবং বেশ কয়েকটি বছর ধরে একটি টিম-ওয়াইড সেটিংয়ে বড় আকারের কোডবেস থেকে, মারফি আইনটি যথেষ্ট প্রযোজ্য বলে প্রবণতা রয়েছে। আপনি যদি অতিমাত্রায় বাগের ঝুঁকি বাড়ান, তবে সম্ভাবনা হ'ল আপনি কমপক্ষে আরও কিছু বাগ পাবেন। একটি বিশাল দল গঠনে এটি খুব বেশি সময় নেয় না যে কল্পনাযোগ্য প্রতিটি ধরণের মানুষের ভুল শেষ পর্যন্ত সম্ভাবনা থেকে বাস্তবে রূপান্তরিত হয়।

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

প্রস্তাব

সুতরাং আমি এখানে একটি সমঝোতার পরামর্শ দেব যা এখনও আপনাকে সমস্ত ডিবাগিং সুবিধা বজায় রাখার ক্ষমতা দেয়:

typedef struct engine* enh;

... এমনকি টাইপফাইংয়ের ব্যয় করেও structকি তা সত্যিই আমাদের মেরে ফেলবে? সম্ভবত না, তাই আমি আপনার পক্ষ থেকে কিছুটা বাস্তববাদিতারও প্রস্তাব দিই, তবে আরও বিকাশকারীদের জন্য যারা size_tএখানে ইতিমধ্যে 99 টি তথ্য গোপন করা বাদ দিয়ে কোনও সঠিক কারণে এখানে ব্যবহার করে এবং পূর্ণসংখ্যায় / থেকে কাস্টিংয়ের মাধ্যমে তাত্পর্যপূর্ণভাবে ডিবাগিং করতে পছন্দ করবেন ব্যবহারকারীর কাছে% লুকিয়ে রয়েছে এবং এর চেয়ে সম্ভবত আর কোনও ক্ষতি করতে পারে না size_t


1
এটা একটা ব্যাপার অতি ক্ষুদ্র , সি স্ট্যান্ডার্ড, সমস্ত অভিন্ন উপস্থাপনা, তাই সমস্ত "ইউনিয়ন পয়েন্টার" do "struct যাও পয়েন্টার" অনুযায়ী "বর্তমান অকার্যকর *" এবং "গৃহস্থালির কাজ *", কিন্তু একটি অকার্যকর * এবং একটি "পয়েন্টার করুন: পার্থক্য কাঠামোতে "বিভিন্ন আকারের () এবং / অথবা বিভিন্ন উপস্থাপনা থাকতে পারে। বাস্তবে আমি এর আগে কখনও দেখিনি।
gnasher729

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


1

আমি সন্দেহ করি যে আসল কারণটি জড়তা, এটাই তারা সবসময়ই করেছে এবং এটি কাজ করে তাই কেন এটি পরিবর্তন করবেন?

আমি দেখতে পাবার প্রধান কারণ হ'ল অস্বচ্ছ হ্যান্ডেলটি ডিজাইনারকে কেবল কোনও কাঠামো নয়, এর পিছনে কিছু দিতে দেয়। যদি API এ ফিরে আসে এবং একাধিক অস্বচ্ছ ধরণের ধরণ গ্রহণ করে তবে তারা সকলেই কলারের কাছে একই রকম দেখাচ্ছে এবং সূক্ষ্ম মুদ্রণ পরিবর্তন হলে কোনও সংকলন সমস্যা বা পুনরায় সংকলনের প্রয়োজন নেই। যদি en_NewFlidgetTwiddler (হ্যান্ডেল ** newTwiddler) কোনও হ্যান্ডেলের পরিবর্তে টুইটারে কোনও পয়েন্টারটি ফেরত পাঠায়, এপিআই পরিবর্তন হয় না এবং কোনও নতুন কোড নীরবে একটি পয়েন্টার ব্যবহার করবে যেখানে এটি হ্যান্ডেলটি ব্যবহার করার আগে ছিল। পাশাপাশি, ওএস বা অন্য কোনও কিছুর কোনও বিপদ নেই যদি পয়েন্টারটি সীমানা পেরিয়ে যায় তবে নিঃশব্দে "ফিক্সিং" করছে।

অবশ্যই এর অসুবিধে হ'ল কলার যে কোনও কিছুতে এটি খাওয়াতে পারে। আপনার একটি 64 বিট জিনিস আছে? এপিআই কলটিতে এটি 64 বিট স্লটে দেখান এবং কী হয় তা দেখুন।

en_TwiddleFlidget(engine, twiddler, flidget)
en_TwiddleFlidget(engine, flidget, twiddler)

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


1

আমি বিশ্বাস করি যে একটি নতুন গ্রন্থাগার এপিআইকে নতুনদের দ্বারা আপত্তি থেকে রক্ষা করার জন্য মনোভাব দীর্ঘকালীন দর্শনের মধ্য দিয়ে যায়।

নির্দিষ্টভাবে,

  • লাইব্রেরি লেখকরা জানেন যে এটি স্ট্রাক্টের একটি পয়েন্টার, এবং কাঠামোর বিবরণগুলি লাইব্রেরি কোডে দৃশ্যমান।
  • সমস্ত অভিজ্ঞ প্রোগ্রামার যারা গ্রন্থাগারটি ব্যবহার করেন তারাও জানেন এটি কিছু অস্বচ্ছ স্ট্রাক্টের পয়েন্টার;
    • এই স্ট্রাক্টগুলিতে সংরক্ষিত বাইটগুলি নিয়ে গণ্ডগোল না করার জন্য তাদের যথেষ্ট কষ্টদায়ক অভিজ্ঞতা ছিল ।
  • অনভিজ্ঞ প্রোগ্রামাররা না জানে।
    • তারা memcpyঅস্বচ্ছ ডেটা চেষ্টা করবে বা স্ট্রকের ভিতরে থাকা বাইট বা শব্দের বৃদ্ধি করবে। হ্যাকিং যান

দীর্ঘকালীন traditionalতিহ্যবাহী প্রতিবিম্বটি হ'ল:

  • একটি অস্বচ্ছ হ্যান্ডেলটি একই প্রক্রিয়া-মেমরি-স্পেসে বিদ্যমান একটি অস্বচ্ছ স্ট্রাক্টের পয়েন্টার হ'ল এটি মাস্করেড করুন।
    • এটি দাবি করে এটি করার জন্য এটির মতো একটি সংখ্যা হিসাবে বিট সংখ্যা থাকা একটি পূর্ণসংখ্যা মান void*
    • অতিরিক্ত পরিস্থিতি হিসাবে চিহ্নিত করতে পয়েন্টারের বিটগুলিও মাস্ক্রেড করুন, যেমন
      struct engine* peng = (struct engine*)((size_t)enh ^ enh_magic_number);

এটি কেবল দীর্ঘ দীর্ঘ ;তিহ্য রয়েছে তা বলার জন্য; এটি সঠিক না ভুল সম্পর্কে আমার কোনও মতামত ছিল না।


3
হাস্যকর জোর বাদে, আমার সমাধানটি সেই সুরক্ষাও সরবরাহ করে। প্রকার সুরক্ষার অতিরিক্ত সুবিধা সহ ক্লায়েন্ট কাঠামোর আকার বা বিষয়বস্তু সম্পর্কে অসচেতন রয়েছেন। কোনও পয়েন্টার ধরে রাখতে সাইজ_টাকে কীভাবে অপব্যবহার করা হয় তা আমি আরও ভাল দেখি না।
জোনাথন রেইনার্ট

@ জোনাথনরাইনহার্টের পক্ষে ক্লায়েন্টটি কাঠামোগুলি সম্পর্কে বাস্তবে অজানা থাকার পক্ষে অত্যন্ত সম্ভাবনা নেই। প্রশ্নটি আরও: তারা কি কাঠামোটি পেতে পারে এবং তারা কীভাবে আপনার লাইব্রেরিতে পরিবর্তিত সংস্করণটি ফিরিয়ে দিতে পারে? কেবল ওপেন সোর্স দিয়ে নয়, আরও সাধারণভাবে। সমাধানটি হ'ল আধুনিক মেমরি বিভাজন, মূর্খ XOR নয়।
Moz

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

6
এই যুক্তিটি অনেকটা ম্যাকিয়াভেলির বিরুদ্ধে রক্ষা করার মতো মনে হচ্ছে । যদি ব্যবহারকারী আমার এপিআইতে আবর্জনা দিতে চায় তবে আমি তাদের থামানোর উপায় নেই। এই জাতীয় টাইপ-অনিরাপদ ইন্টারফেসের সাথে পরিচিত করা খুব কমই এর সাথে সাহায্য করে, কারণ এটি আসলে API এর দুর্ঘটনাজনিত অপব্যবহারকে সহজ করে তোলে।
কমিকসান্স এমএমএস

@ComicSansMS "দুর্ঘটনাজনক" উল্লেখ করার জন্য আপনাকে ধন্যবাদ জানায়, কারণ এখানেই আমি প্রকৃতপক্ষে রোধ করার চেষ্টা করছি ।
জোনাথন রাইনহার্ট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.