কনটেক্সপ্র বনাম ম্যাক্রো


92

আমার কোথায় ম্যাক্রোগুলি পছন্দ করা উচিত এবং আমি কোথায় কনটেক্সট্রপ পছন্দ করব ? তারা কি মূলত এক নয়?

#define MAX_HEIGHT 720

বনাম

constexpr unsigned int max_height = 720;

4
AFAIK কনটেক্সারপ আরও ধরণের সুরক্ষা সরবরাহ করে
কোড-শিক্ষানবিশ

13
সহজ: কনটেক্সার, সর্বদা।
এন। 'সর্বনাম' মি।

আপনার কিছু প্রশ্নের উত্তর পারে stackoverflow.com/q/4748083/540286
Ortwin Angermeier

উত্তর:


146

তারা কি মূলত এক নয়?

না। কাছেও নয়।

আপনার ম্যাক্রোটি একটি intএবং আপনার constexpr unsignedএকটি unsignedব্যতীত গুরুত্বপূর্ণ পার্থক্য রয়েছে এবং ম্যাক্রোর কেবল একটি সুবিধা রয়েছে।

ব্যাপ্তি

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

#define MAX_HEIGHT 720
constexpr int max_height = 720;

class Window {
  // ...
  int max_height;
};

সদস্যের পরিবর্তনশীল বলা ভাল, max_heightকারণ এটি একটি শ্রেণীর সদস্য এবং তাই এর আলাদা সুযোগ রয়েছে, এবং নাম স্পেসের স্কোপ থেকে পৃথক। আপনি যদি সদস্যটির জন্য নামটি পুনঃব্যবহার করার চেষ্টা MAX_HEIGHTকরেন তবে প্রিপ্রসেসর এটিকে এই ননসেন্সে পরিবর্তন করে যা সংকলন করে না:

class Window {
  // ...
  int 720;
};

এ কারণেই আপনাকে ম্যাক্রোগুলি দিতে UGLY_SHOUTY_NAMESহবে যাতে তারা দাঁড়ায় এবং আপনি সংঘর্ষ এড়াতে তাদের নামকরণ সম্পর্কে যত্নবান হতে পারেন। যদি আপনি অযথা ম্যাক্রোগুলি ব্যবহার না করেন তবে আপনাকে সে সম্পর্কে চিন্তা করতে হবে না (এবং পড়তে হবে না SHOUTY_NAMES)।

আপনি যদি কোনও ফাংশনের ভিতরে কেবল ধ্রুবক চান তবে আপনি ম্যাক্রোর সাহায্যে এটি করতে পারবেন না, কারণ প্রিপ্রসেসর জানেন না যে কোনও ফাংশন কী বা এর অভ্যন্তরে কী বোঝায়। কোনও ম্যাক্রোকে কেবলমাত্র একটি ফাইলের নির্দিষ্ট অংশে সীমাবদ্ধ করতে আপনার #undefএটির আবার দরকার :

int limit(int height) {
#define MAX_HEIGHT 720
  return std::max(height, MAX_HEIGHT);
#undef MAX_HEIGHT
}

অনেক বেশি বুদ্ধিমানের সাথে তুলনা করুন:

int limit(int height) {
  constexpr int max_height = 720;
  return std::max(height, max_height);
}

আপনি ম্যাক্রোটিকে কেন পছন্দ করবেন?

একটি আসল মেমরি অবস্থান

একটি কনসেক্সট্রপ ভেরিয়েবল একটি পরিবর্তনশীল তাই এটি প্রোগ্রামে আসলে বিদ্যমান থাকে এবং আপনি সাধারণ সি +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 0

এই কোডটির অপরিবর্তিত আচরণ রয়েছে:

#define MAX_HEIGHT 720
int limit(int height) {
  const int& h = std::max(height, MAX_HEIGHT);
  // ...
  return h;
}

সমস্যাটি হ'ল এটি MAX_HEIGHTকোনও পরিবর্তনশীল নয়, সুতরাং std::maxএকটি অস্থায়ী কলটির জন্য intকম্পাইলার তৈরি করতে হবে। std::maxএরপরে ফিরে আসা রেফারেন্সটি সম্ভবত সেই অস্থায়ীটিকে বোঝায়, যা বিবৃতিটি শেষ হওয়ার পরে অস্তিত্বহীন, তাই return hঅবৈধ স্মৃতিতে অ্যাক্সেস করে।

এই সমস্যাটি কেবল সঠিক পরিবর্তনশীল সহ অস্তিত্বহীন, কারণ এটির স্মৃতিতে একটি নির্দিষ্ট অবস্থান রয়েছে যা চলে না:

int limit(int height) {
  constexpr int max_height = 720;
  const int& h = std::max(height, max_height);
  // ...
  return h;
}

(বাস্তবে আপনি সম্ভবত ঘোষণা int hনা করতেন const int& hতবে আরও সূক্ষ্ম প্রসঙ্গে সমস্যাটি দেখা দিতে পারে))

প্রাক প্রসেসর শর্ত

ম্যাক্রোকে পছন্দ করার একমাত্র সময় হ'ল যখন আপনার প্রিপ্রসেসর দ্বারা এটির মান বোঝার প্রয়োজন হয়, #ifশর্তে ব্যবহারের জন্য , যেমন

#define MAX_HEIGHT 720
#if MAX_HEIGHT < 256
using height_type = unsigned char;
#else
using height_type = unsigned int;
#endif

আপনি এখানে কোনও ভেরিয়েবল ব্যবহার করতে পারেননি, কারণ প্রিপ্রোসেসর বুঝতে পারে না কীভাবে নাম দ্বারা ভেরিয়েবলগুলি কীভাবে উল্লেখ করা যায়। এটি কেবল ম্যাক্রো সম্প্রসারণ এবং দিকনির্দেশক #(যেমন #includeএবং #defineএবং #if) এর মাধ্যমে প্রাথমিক খুব বেসিক জিনিসগুলি বোঝে ।

আপনি যদি এমন একটি ধ্রুবক চান যা প্রিপ্রোসেসর দ্বারা বোঝা যায় তবে আপনার এটি সংজ্ঞায়িত করার জন্য প্রিপ্রসেসর ব্যবহার করা উচিত। আপনি যদি সাধারণ সি ++ কোডের জন্য ধ্রুবক চান তবে সাধারণ সি ++ কোড ব্যবহার করুন।

উপরের উদাহরণটি কেবলমাত্র একটি প্রিপ্রোসেসর শর্ত প্রদর্শন করার জন্য, তবে এমনকি কোডটি প্রিপ্রসেসর ব্যবহার করে এড়াতে পারে:

using height_type = std::conditional_t<max_height < 256, unsigned char, unsigned int>;

4
কোনও constexprভেরিয়েবলের ঠিকানা (পয়েন্টার / রেফারেন্স) না নেওয়া পর্যন্ত স্মৃতি দখল করা উচিত নয়; অন্যথায়, এটি সম্পূর্ণরূপে অপ্টিমাইজ করা যেতে পারে (এবং আমি মনে করি যে স্ট্যান্ডার্ডিজ এটির নিশ্চয়তা দেয়)। আমি এটির উপরে জোর দিতে চাই যাতে লোকেরা enumভুল ধারণা থেকে পুরানো, নিকৃষ্ট ' হ্যাক' ব্যবহার অবিরত না করে যে কোনও তুচ্ছ ঘটনাকে constexprস্টোরেজের প্রয়োজন হয় না তবুও তারা কিছু দখল করবে।
আন্ডারস্কোর_

4
আপনার "একটি আসল মেমরি অবস্থান" বিভাগটি ভুল: ১. আপনি মান (ইনট) দ্বারা ফিরে আসছেন, সুতরাং একটি অনুলিপি তৈরি করা হয়েছে, অস্থায়ী কোনও সমস্যা নয়। ২. আপনি যদি রেফারেন্স (ইনট এবং) দিয়ে ফিরে এসেছিলেন int heightতবে ম্যাক্রোর মতোই আপনার সমস্যা হবে, কারণ এর ব্যাপ্তিটি মূলত অস্থায়ীভাবেও ফাংশনটির সাথে আবদ্ধ। ৩. উপরের মন্তব্যটি, "কনস্ট্যান্ট এবং এইচ অস্থায়ীর জীবনকাল বাড়িয়ে দেবে" সঠিক।
পাওয়ারেডবাইরাইস

4
@ আসরস্কোর_ড সত্য, তবে এটি যুক্তি পরিবর্তন করে না। ভেরিয়েবলের অদ্ভুত ব্যবহার না হলে স্টোরেজের প্রয়োজন হবে না। মুল বক্তব্যটি হ'ল যখন স্টোরেজ সহ সত্যিকারের চলক প্রয়োজন হয়, তখন কনটেক্সপ ভেরিয়েবল সঠিক কাজ করে।
জোনাথন ওয়াকলি

4
@ পাওয়ার্ডবাইরাইস ১. সমস্যাটির রিটার্ন মানের সাথে কোনও সম্পর্ক নেই limit, সমস্যাটি হল ফেরতের মান std::max। ২. হ্যাঁ, সে কারণেই এটি কোনও রেফারেন্স দেয় না। ৩. ভুল, উপরে কলিরু লিঙ্কটি দেখুন।
জোনাথন ওয়েকেলি

4
@ পাওয়ার্ড বাইরাইস দীর্ঘশ্বাস ফেললেন, সি ++ কীভাবে আমার সাথে কাজ করে তা আপনাকে সত্যিই বোঝানোর দরকার নেই। যদি আপনার কাছে থাকে const int& h = max(x, y);এবং maxমান অনুসারে তার ফেরতের মানটির আজীবন প্রসারিত হয়। রিটার্ন টাইপের দ্বারা নয়, তবে const int&এটি আবদ্ধ। আমি যা লিখেছি তা সঠিক।
জোনাথন ওয়েকেলি

11

সাধারণভাবে বলতে গেলে, আপনি constexprযখনই পারেন ব্যবহার করা উচিত , এবং অন্য কোনও সমাধান সম্ভব না হলে কেবল ম্যাক্রোগুলি ব্যবহার করা উচিত ।

যুক্তি:

ম্যাক্রোস কোডের একটি সহজ প্রতিস্থাপন এবং এই কারণে তারা প্রায়শই সংঘাত তৈরি করে (যেমন উইন্ডোজ। maxম্যাক্রো বনাম std::max)। অতিরিক্তভাবে, ম্যাক্রো যা কাজ করে তা সহজেই অন্যভাবে ব্যবহার করা যেতে পারে যা বিস্ময়কর সংকলনের ত্রুটিগুলি ট্রিগার করতে পারে। (যেমন Q_PROPERTYকাঠামোর সদস্যদের জন্য ব্যবহৃত)

এই সমস্ত অনিশ্চয়তার কারণে ম্যাক্রোগগুলি এড়ানো ভাল কোড স্টাইল, ঠিক যেমন আপনি সাধারণত গোটোস এড়িয়ে চলেন।

constexpr শব্দার্থগতভাবে সংজ্ঞায়িত করা হয় এবং এটি সাধারণত কম কম সমস্যা উত্পন্ন করে।


4
কোন ক্ষেত্রে একটি অপ্রয়োজনীয় ম্যাক্রো ব্যবহার করা হচ্ছে?
টম ডোরোন

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

বৈকল্পিক ম্যাক্রোগুলি ব্যতীত, প্রায়শই সংকলক সুইচগুলির জন্য ম্যাক্রোর ব্যবহার, তবে বর্তমান ম্যাক্রো স্টেটমেন্টগুলি (যেমন শর্তসাপেক্ষ, স্ট্রিং আক্ষরিক সুইচগুলি) প্রতিস্থাপনের চেষ্টা করে কনটেক্সপ্রের সাথে রিয়েল কোড স্টেটমেন্টগুলি ডিল করা ভাল ধারণা?

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

3

জোনাথন ওয়েকেলি দ্বারা দুর্দান্ত উত্তর । আমি আপনাকে জোগোজাপানের জবাবটি একবার দেখে নিনconst এবং constexprআপনি ম্যাক্রোগুলির ব্যবহার বিবেচনা করার আগে তার মধ্যে পার্থক্য কী তা আগে একবার দেখে নিতে পরামর্শ দিয়েছি ।

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

ম্যাক্রোগুলির বিভিন্ন রকম ব্যবহারের সময়, এই দুটি আমিই প্রায়শই দেখি যা খারাপ / পুরানো তারিখগুলি নয়:

  1. হার্ডওয়্যার এবং প্ল্যাটফর্ম-নির্দিষ্ট কোড বিভাগগুলি
  2. ক্রমবর্ধমান বৃদ্ধি হয়

সুতরাং আপনি যখন ওপি-র ক্ষেত্রে কোনও আন্ত constexprবা সংজ্ঞা নির্ধারণের একই লক্ষ্য অর্জন MACROকরতে পারেন, আধুনিক সম্মেলনগুলি ব্যবহার করার সময় দু'জনেরই ওভারল্যাপ হওয়ার সম্ভাবনা কম। এখানে কিছু সাধারণ ম্যাক্রো-ব্যবহার রয়েছে যা পর্যায়ক্রমে হয়নি, এখনও।

#if defined VERBOSE || defined DEBUG || defined MSG_ALL
    // Verbose message-handling code here
#endif

ম্যাক্রো-ব্যবহারের জন্য অন্য উদাহরণ হিসাবে, আসুন আমরা বলি যে আপনার কাছে মুক্তি পাওয়ার জন্য কিছু আসন্ন হার্ডওয়্যার রয়েছে বা সম্ভবত এটির একটি নির্দিষ্ট প্রজন্মের কিছু জটিল কাজ রয়েছে যা অন্যদের প্রয়োজন হয় না। আমরা এই ম্যাক্রোটিকে হিসাবে সংজ্ঞায়িত করব GEN_3_HW

#if defined GEN_3_HW && defined _WIN64
    // Windows-only special handling for 64-bit upcoming hardware
#elif defined GEN_3_HW && defined __APPLE__
    // Special handling for macs on the new hardware
#elif !defined _WIN32 && !defined __linux__ && !defined __APPLE__ && !defined __ANDROID__ && !defined __unix__ && !defined __arm__
    // Greetings, Outlander! ;)
#else
    // Generic handling
#endif
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.