সি ++ টাইম কাউন্টারগুলি সংকলন, পুনর্বিবেচিত


28

টি এল; ডিআর

এই পুরো পোস্টটি পড়ার চেষ্টা করার আগে জেনে রাখুন:

  1. উপস্থাপিত ইস্যুটির একটি সমাধান আমি নিজেই খুঁজে পেয়েছি তবে বিশ্লেষণটি সঠিক কিনা তা জানতে আমি এখনও আগ্রহী;
  2. আমি সমাধানটি একটি fameta::counterক্লাসে প্যাকেজ করেছি যা কয়েকটা বাকী বাচ্চা সমাধান করে। আপনি এটি গিথুবে খুঁজে পাবেন ;
  3. আপনি গডবোল্ট নিয়ে কাজ করতে পারেন ।

কিভাবে এটি সব শুরু

ফিলিপ রোজেন আবিষ্কার করেছেন / আবিষ্কার করেছেন, ২০১৫ সালে, টাইম কাউন্টারগুলি সংকলনকারী কালো যাদুটি সি ++ তে রয়েছে , তখন আমি ডিভাইসটি নিয়ে হালকাভাবে আচ্ছন্ন হয়ে পড়েছিলাম, তাই যখন সিডাব্লুজি সিদ্ধান্ত নিয়েছিল যে কার্যকারিতাটিই যেতে হবে তখন আমি হতাশ হয়েছিলাম, তবে এখনও আশাবাদী যে তাদের মন তাদের কয়েকটি বাধ্যতামূলক ব্যবহারের কেস দেখিয়ে পরিবর্তন করা যেতে পারে।

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

সমস্যা রোজেনের ওয়েবসাইটে এবং সম্প্রতি স্ট্যাকওভারফ্লোতেও রিপোর্ট করা হয়েছে : সি ++ কি সংকলন-সময় কাউন্টারগুলিকে সমর্থন করে?

কিছু দিন আগে আমি আবার সমস্যাগুলি সমাধান করার চেষ্টা করব

আমি বুঝতে চেয়েছিলাম যে সংকলকগুলিতে কী পরিবর্তন হয়েছে যা আপাতদৃষ্টিতে এখনও বৈধ সি ++ করেছে, আর কাজ করে না। এ লক্ষ্যে, আমি কারও পক্ষে এটি সম্পর্কে কথা বলার জন্য প্রশস্ত এবং আন্তঃস্বল্প দূরত্বে অনুসন্ধান করেছি, কিন্তু কোন ফলসই হয়নি। তাই আমি পরীক্ষা-নিরীক্ষা শুরু করেছি এবং কিছু সিদ্ধান্তে পৌঁছেছি, আমি এখানে আশেপাশের আরও জ্ঞাত-বুদ্ধিমানের থেকে একটি প্রতিক্রিয়া পাওয়ার আশায় এখানে উপস্থাপন করছি।

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

template<int N>
struct flag {
  friend constexpr int adl_flag (flag<N>);
};

template<int N>
struct writer {
  friend constexpr int adl_flag (flag<N>) {
    return N;
  }

  static constexpr int value = N;
};

template<int N, int = adl_flag (flag<N> {})>
int constexpr reader (int, flag<N>) {
  return N;
}

template<int N>
int constexpr reader (float, flag<N>, int R = reader (0, flag<N-1> {})) {
  return R;
}

int constexpr reader (float, flag<0>) {
  return 0;
}

template<int N = 1>
int constexpr next (int R = writer<reader (0, flag<32> {}) + N>::value) {
  return R;
}

int main () {
  constexpr int a = next ();
  constexpr int b = next ();
  constexpr int c = next ();

  static_assert (a == 1 && b == a+1 && c == b+1, "try again");
}

জি ++ এবং ঝনঝন ++ উভয়ই সাম্প্রতিক-ইশ সংকলকগুলির সাথে next()সর্বদা 1 টি ফিরে আসে a এই ফাংশনগুলি ডিফল্ট প্যারামিটারগুলির পুনরায় মূল্যায়নকে ট্রিগার করে না, এভাবে কখনও নতুন ফাংশন ইনস্ট্যান্ট করে না তবে সর্বদা পূর্ববর্তী তাত্ক্ষণিকগুলিকে উল্লেখ করে।


প্রথম প্রশ্ন

  1. আপনি কি আমার এই নির্ণয়ের সাথে সত্যই একমত?
  2. যদি হ্যাঁ, এই নতুন আচরণটি কি মানদণ্ড দ্বারা বাধ্যতামূলক? আগেরটি কি বাগ ছিল?
  3. তা না হলে সমস্যা কী?

উপরের কথাটি মাথায় রেখে আমি চারপাশে একটি কাজ করে হাজির হয়েছি: প্রতিটি next()আহ্বানকে মনোোটোনিকভাবে বাড়ানো অনন্য আইডি দিয়ে চিহ্নিত করুন, কলিগুলিতে পৌঁছানোর জন্য, যাতে কোনও কল একই না হয়, অতএব সংকলককে সমস্ত যুক্তি পুনরায় মূল্যায়ন করতে বাধ্য করা প্রতি বার.

এটি করার জন্য এটি বোঝা বলে মনে হচ্ছে, তবে এটির কথা চিন্তা করে কেউ কেবল কোনও ফাংশনের মতো ম্যাক্রোতে লুকানো স্ট্যান্ডার্ড __LINE__বা __COUNTER__পছন্দসই (যেখানেই উপলভ্য) ম্যাক্রো ব্যবহার করতে পারে counter_next()

সুতরাং আমি নিম্নলিখিতগুলি নিয়ে এসেছি, আমি সর্বাধিক সরলীকৃত ফর্মটিতে উপস্থাপন করি যা আমি পরে যে সমস্যাটি সম্পর্কে কথা বলব তা দেখায়।

template <int N>
struct slot;

template <int N>
struct slot {
    friend constexpr auto counter(slot<N>);
};

template <>
struct slot<0> {
    friend constexpr auto counter(slot<0>) {
        return 0;
    }
};

template <int N, int I>
struct writer {
    friend constexpr auto counter(slot<N>) {
        return I;
    }

    static constexpr int value = I-1;
};

template <int N, typename = decltype(counter(slot<N>()))>
constexpr int reader(int, slot<N>, int R = counter(slot<N>())) {
    return R;
};

template <int N>
constexpr int reader(float, slot<N>, int R = reader(0, slot<N-1>())) {
    return R;
};

template <int N>
constexpr int next(int R = writer<N, reader(0, slot<N>())+1>::value) {
    return R;
}

int a = next<11>();
int b = next<34>();
int c = next<57>();
int d = next<80>();

গডবোল্টের উপরের ফলাফলগুলি আপনি পর্যবেক্ষণ করতে পারেন , যা আমি অলসদের জন্য স্ক্রিনশট করেছি।

এখানে চিত্র বর্ণনা লিখুন

এবং আপনি দেখতে পাচ্ছেন, ট্রাঙ্ক জি ++ এবং ঝনঝন ++ সহ 7.0.0 পর্যন্ত এটি কাজ করে! , প্রত্যাশা হিসাবে কাউন্টারটি 0 থেকে 3 থেকে বৃদ্ধি পায় তবে ঝাঁকুনি ++ সংস্করণ 7.0.0 এর উপরে এটির সাথে হয় না

আঘাতের অপমানের জন্য, আমি আসলে মিশ্রণটিতে একটি "প্রসঙ্গ" পরামিতি যুক্ত করে সংস্করণ 7.0.0 ক্র্যাশ পর্যন্ত ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ কম কম পর্যন্ত অবধি কমিয়ে আনার ব্যবস্থা করেছি such কোনও নতুন প্রসঙ্গ নির্ধারণ করা হলে যে কোনও সময় পুনরায় চালু করা হবে, এটি সম্ভাব্য অসীম পরিমাণ কাউন্টার ব্যবহারের সম্ভাবনার জন্য উন্মুক্ত। এই বৈকল্পিকের সাথে, ঝাঁকুনি ++ উপরের সংস্করণ 7.0.0 ক্র্যাশ হয় না, তবে এখনও প্রত্যাশিত ফলাফল তৈরি করে না। Godbolt এ লাইভ

কী চলছে তা সম্পর্কে কোনও ধারণা না পেয়ে, আমি সিপ্পিনসাইটস.আইও ওয়েবসাইটটি আবিষ্কার করেছি , এটি কীভাবে এবং কখন টেমপ্লেটগুলি ইনস্ট্যান্ট হয় তা দেখতে দেয়। আমি যা মনে করি সে পরিষেবাটি ব্যবহার করে হ'ল ঝনক ++ প্রকৃতপক্ষে যখনই তাত্ক্ষণিক হয় তখন কোনও কার্যকারিতা সংজ্ঞায়িত করে নাfriend constexpr auto counter(slot<N>)writer<N, I>

counter(slot<N>)ইতিমধ্যে তাত্ক্ষণিকভাবে হওয়া উচিত যে কোনও প্রদত্ত এনকে স্পষ্টভাবে কল করার চেষ্টা করা এই অনুমানকে ভিত্তি বলে মনে হচ্ছে।

তবে, যদি আমি writer<N, I>প্রদত্ত যে কোনওটির জন্য স্পষ্টভাবে তাত্ক্ষণিকভাবে চেষ্টা করার চেষ্টা করি Nএবং Iএটি ইতিমধ্যে তাত্ক্ষণিকভাবে হওয়া উচিত ছিল, তবে ঝনক ++ একটি নতুন সংজ্ঞায়িত সম্পর্কে অভিযোগ করে friend constexpr auto counter(slot<N>)

উপরেরটি পরীক্ষা করতে, আমি পূর্ববর্তী উত্স কোডে আরও দুটি লাইন যুক্ত করেছি।

int test1 = counter(slot<11>());
int test2 = writer<11,0>::value;

গডবোল্টে আপনি নিজের জন্য এটি সব দেখতে পারেন । স্ক্রিনশট নীচে।

ঝাঁকুনি ++ বিশ্বাস করে যে এটি এমন কোনও সংজ্ঞা দিয়েছে যা এটি বিশ্বাস করে যে এটি সংজ্ঞায়িত হয়নি

সুতরাং, দেখা যায় যে ঝনঝন ++ বিশ্বাস করে যে এটি এমন কোনও সংজ্ঞা দিয়েছে যা এটি বিশ্বাস করে যে এটি সংজ্ঞায়িত হয়নি , কোন ধরণের আপনার মাথাকে স্পিন করে তোলে, তাই না?


প্রশ্ন দ্বিতীয় ব্যাচ

  1. আমার কার্যত আইনী সি ++ আদৌ কি, বা আমি অন্য একটি জি ++ বাগ আবিষ্কার করতে পারি?
  2. যদি এটি আইনী হয় তবে আমি কি কিছু বাজে ঝাঁকুনি ++ বাগ আবিষ্কার করেছি?
  3. বা আমি কি কেবল অনির্ধারিত আচরণের অন্ধকার পাতালখানায় ডুবেছি, তাই আমি নিজেই কেবল দোষী?

যে কোনও ইভেন্টে, আমি যদি এই খরগোশের গর্ত থেকে আমাকে বেরিয়ে আসতে সহায়তা করতে চাই, এমন কাউকে আমি আন্তরিকভাবে স্বাগত জানাব, যদি প্রয়োজন হয় তবে মাথা ব্যথার ব্যাখ্যা সরবরাহ করে। : ডি


2

2
আমি যেমন মনে করি যে স্ট্যান্ডার্ড কমিটির লোকেরা এমন কোনও ধরণের, আকৃতি বা ফর্মের সংকলন-কালেকট্রাক্টগুলি নিষ্ক্রিয় করার স্পষ্ট অভিপ্রায় রাখে যা তারা প্রতিবার (অনুমানিকভাবে) মূল্যায়ন করার সময় সঠিক ফলাফল দেয় না produce সুতরাং এটি একটি সংকলক বাগ হতে পারে, এটি একটি "দুর্বৃত্ত, কোনও রোগনির্ণয়ের প্রয়োজন নেই" ক্ষেত্রে হতে পারে বা এটি স্ট্যান্ডার্ড মিস করা কিছু হতে পারে। তবুও এটি "স্ট্যান্ডার্ডের স্পিরিট" এর বিরুদ্ধে যায়। আমি দুঃখিত. আমি টাইম কাউন্টারগুলিও সংকলন পছন্দ করতাম।
বলভ

@ হোলিব্ল্যাকগেট আমাকে অবশ্যই স্বীকার করতে হবে যে এই কোডটির চারপাশে আমার মাথা পেতে খুব কষ্ট হচ্ছে। দেখে মনে হচ্ছে এটি next()ফাংশনটির প্যারামিটার হিসাবে স্পষ্টভাবে একটি মনোোটোনিক ক্রমবর্ধমান সংখ্যা পাস করার প্রয়োজনীয়তা এড়াতে পারে , তবে আমি কীভাবে এটি কাজ করে তা সত্যই বুঝতে পারি না। যে কোনও ইভেন্টে, আমি আমার নিজের সমস্যার প্রতিক্রিয়া নিয়ে এখানে এসেছি: stackoverflow.com/a/60096865/566849
ফ্যাবিও এ

@FabioA। আমিও পুরোপুরি উত্তরটি বুঝতে পারি না। যে প্রশ্নটি জিজ্ঞাসা করার পরে, আমি বুঝতে পেরেছি যে আমি আর কখনও কনস্টেক্সপ্র কাউন্টারগুলিকে স্পর্শ করতে চাই না।
হলিব্ল্যাকগেট

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

উত্তর:


5

আরও তদন্তের পরে, দেখা যাচ্ছে যে একটি ছোটখাট পরিবর্তন রয়েছে next()যা ফাংশনে সম্পাদন করা যেতে পারে , যা কোডটি কলঙ্ক ++ সংস্করণে .0.০.০ এর উপরে সঠিকভাবে কাজ করে, তবে এটি অন্য সমস্ত ঝোলা ++ সংস্করণগুলির জন্য কাজ করা বন্ধ করে দেয়।

আমার আগের সমাধান থেকে নেওয়া নিম্নলিখিত কোডটি একবার দেখুন।

template <int N>
constexpr int next(int R = writer<N, reader(0, slot<N>())+1>::value) {
    return R;
}

আপনি যদি এটির দিকে মনোযোগ দিন, এটি আক্ষরিকভাবে যা যা তা হ'ল এটির সাথে যুক্ত মানটি পড়ার চেষ্টা করা slot<N>, এটিতে 1 যুক্ত করুন এবং তারপরে এই নতুন মানটিকে একেবারে সংযুক্ত করুন slot<N>

যখন slot<N>কোন সংযুক্ত করেছে মান, মান সঙ্গে যুক্ত slot<Y>পরিবর্তে প্রাপ্ত করা হয়, Yকম সর্বোচ্চ সূচক হচ্ছে Nযেমন যে slot<Y>ক্ষেত্রে সংশ্লিষ্ট মান আছে।

উপরের কোডটির সমস্যাটি হ'ল, যদিও এটি g ++, ঝাঁকুনি +++ এ কাজ করে (যথাযথভাবে, আমি বলব?) এর সাথে সম্পর্কিত মান না থাকাকালীন যা ফিরে আসে তা reader(0, slot<N>()) স্থায়ীভাবে ফিরিয়ে দেয় slot<N>। ঘুরেফিরে, এর অর্থ এই যে সমস্ত স্লট কার্যকরভাবে বেস মানের সাথে যুক্ত হয় 0

সমাধানটি হ'ল উপরের কোডটিকে এটিকে রূপান্তর করা:

template <int N>
constexpr int next(int R = writer<N, reader(0, slot<N-1>())+1>::value) {
    return R;
}

নোটিশ যে slot<N>()পরিবর্তন করা হয়েছে slot<N-1>()। এটি উপলব্ধি করে: আমি যদি কোনও মানকে সংযুক্ত করতে চাই slot<N>তবে এর অর্থ কোনও মান এখনও জড়িত নয়, সুতরাং এটি পুনরুদ্ধারের প্রচেষ্টা করার কোনও অর্থ হয় না। এছাড়াও, আমরা একটি কাউন্টার বাড়াতে চাই এবং এর সাথে সম্পর্কিত কাউন্টারটির slot<N>মান একের সাথে যুক্ত মান হতে হবে slot<N-1>

ইউরেকা!

যদিও ঝাঁকুনি ++ সংস্করণগুলি <= 7.0.0 ভাঙছে।

উপসংহার

আমার কাছে মনে হয় যে আমি পোস্ট করা আসল সমাধানটিতে একটি ধারণামূলক বাগ রয়েছে, যেমন:

  • জি ++ এর মধ্যে কীর্তি / বাগ / শিথিলতা রয়েছে যা আমার সমাধানের বাগটি বাতিল করে দেয় এবং শেষ পর্যন্ত কোডটি কাজ সত্ত্বেও করে তোলে।
  • ঝাঁকুনি ++ সংস্করণ> 7.0.0 কঠোর এবং মূল কোডটিতে বাগটি পছন্দ করে না।
  • ঝনঝন ++ সংস্করণ <= 7.0.0 এ একটি বাগ রয়েছে যা সঠিক সমাধানটি কাজ করে না।

এই সমস্ত সংক্ষেপে, নীচের কোডটি g ++ এবং ঝাঁকুনি ++ এর সমস্ত সংস্করণে কাজ করে।

#if !defined(__clang_major__) || __clang_major__ > 7
template <int N>
constexpr int next(int R = writer<N, reader(0, slot<N-1>())+1>::value) {
    return R;
}
#else
template <int N>
constexpr int next(int R = writer<N, reader(0, slot<N>())+1>::value) {
    return R;
}
#endif

কোডটি এমএসভিসি-তেও কাজ করে। আইসিসি সংকলকটি decltype(counter(slot<N>()))সক্ষম না deduce the return type of function "counter(slot<N>)"হওয়ায় অভিযোগ করতে পছন্দ করে ব্যবহার করার সময় SFINAE ট্রিগার করে না it has not been defined। আমি বিশ্বাস করি এটি একটি ত্রুটি , এর প্রত্যক্ষ ফলাফলের জন্য SFINAE করে কাজ করা যায় counter(slot<N>)। এটি অন্যান্য সমস্ত সংকলকগুলিতেও কাজ করে, তবে জি ++ খুব বিরক্তিকর সতর্কতাগুলি বন্ধ করতে পারে না বলে প্রচুর পরিমাণে থুতু ফেলার সিদ্ধান্ত নেয়। সুতরাং, এছাড়াও এই ক্ষেত্রে, #ifdefউদ্ধার আসতে পারে।

প্রমাণ godbolt হয় নিচের screnshotted।

এখানে চিত্র বর্ণনা লিখুন


2
আমি মনে করি এই প্রতিক্রিয়াটি বিষয়টি বন্ধ করে দেয় তবে আমি এখনও আমার বিশ্লেষণে সঠিক কিনা তা জানতে চাই, অতএব আমি আমার নিজের উত্তরটিকে সঠিক হিসাবে গ্রহণ করার আগে অপেক্ষা করব, আশা করি অন্য কেউ পাশ কাটিয়ে আমাকে আরও ভাল ক্লু দেবে hop বা একটি নিশ্চিতকরণ। :)
ফ্যাবিও এ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.