সি ++ তে এনামগুলিকে পতাকা হিসাবে কীভাবে ব্যবহার করবেন?


187

ট্রিটিং enumপতাকাগুলি হিসাবে s এর মাধ্যমে C # চমত্কারভাবে কাজ করে [Flags]অ্যাট্রিবিউট, কিন্তু কি সি এই কাজ করতে ++, সবচেয়ে ভালো উপায় কি?

উদাহরণস্বরূপ, আমি লিখতে চাই:

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};

seahawk.flags = CanFly | EatsFish | Endangered;

তবে আমি int/ enumরূপান্তর সংক্রান্ত সংকলক ত্রুটি পেয়েছি get খালি কাস্টিং কাস্টিংয়ের চেয়ে এটিকে প্রকাশ করার কোনও সুন্দর উপায় নেই? অগ্রাধিকার হিসাবে, আমি তৃতীয় পক্ষের লাইব্রেরি যেমন বুস্ট বা কিউটি থেকে নির্মিত উপর নির্ভর করতে চাই না।

সম্পাদনা করুন: যেমন উত্তর নির্দেশিত, আমি ঘোষণা করে কম্পাইলার এরর এড়াতে পারেন seahawk.flagsহিসাবে int। তবে, আমি টাইপ সুরক্ষা কার্যকর করার জন্য কিছু ব্যবস্থা রাখতে চাই, যাতে কেউ লিখতে না পারে seahawk.flags = HasMaximizeButton


যতদূর আমি ভিজ্যুয়াল সি ++ 2013 তে জানি [Flags]এট্রিবিউটটি ঠিক ঠিক কাজ করে যেমন:[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};
রিভানভ

@rivanov, না এটি সি ++ (2015 এছাড়াও) দিয়ে কাজ করে না। আপনি কি সি # বলতে চাইছেন?
অজয়

5
@rivanov, [পতাকাগুলি] বৈশিষ্ট্যটি কেবল সি ++ সিএমএই। নেট ফ্রেমওয়ার্কের সাথে কাজ করে, স্থানীয় সি ++ এই জাতীয় বৈশিষ্ট্যগুলিকে সমর্থন করে না।
জোল্টন তিরিন্দা

উত্তর:


249

"সঠিক" উপায়টি এনামের জন্য বিট অপারেটরগুলি সংজ্ঞায়িত করা যেমন:

enum AnimalFlags
{
    HasClaws   = 1,
    CanFly     = 2,
    EatsFish   = 4,
    Endangered = 8
};

inline AnimalFlags operator|(AnimalFlags a, AnimalFlags b)
{
    return static_cast<AnimalFlags>(static_cast<int>(a) | static_cast<int>(b));
}

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


42
এই। একমাত্র প্রশ্ন হ'ল অপারেটর সংজ্ঞাগুলি কীভাবে স্বয়ংক্রিয় / টেম্প্লেটিজ করা যায় যাতে প্রতিবার নতুন এনাম যুক্ত করার সময় আপনাকে নিয়মিত তাদের সংজ্ঞা দিতে হবে না।
ইওদাবাশ

10
এছাড়াও, কোনও ইচ্ছামত ইনট থেকে এনাম টাইপের কাছে castালাই বৈধ, এমনকি যদি ইনট মানটি এনামের শনাক্তকারীদের কোনওটির সাথে মিলে না?
ইনগো শ্যাচাল-শোপ

8
এটি সম্পূর্ণ আজেবাজে কথা। কোন সদস্যের AnimalFlagsদ্বারা অভিব্যক্তি দ্বারা প্রতিনিধিত্ব করা হয় HasClaws | CanFly? এই না কি enums এর জন্য হয়। পূর্ণসংখ্যা এবং ধ্রুবক ব্যবহার করুন।
অরবিটে মার্চীনতা রেস

25
@ লাইটনেসেসেসিনঅরবিট: এটি সঠিক নয়। এনাম টাইপের ডোমেন হল এর অন্তর্নিহিত ধরণের ডোমেন - এটি কেবলমাত্র নির্দিষ্ট কিছুকে একটি নাম দেওয়া হয়েছে। এবং আপনার প্রশ্নের উত্তর দিতে: সদস্য " (HasClaws | CanFly)"।
Xeo

5
@ মার্কাসজে: আপনার মানগুলিকে 2 টির ক্ষমতায় সীমাবদ্ধ করে আপনাকে আপনার এনামগুলিকে বিট-ফ্ল্যাগ হিসাবে ব্যবহার করার অনুমতি দেয়। সুতরাং আপনি যদি 3 পেয়ে থাকেন তবে আপনি এটি উভয়ই HasClaws(= 1) এবং CanFly(= 2) জানেন। পরিবর্তে যদি আপনি কেবল 1 থেকে 4 পর্যন্ত সরাসরি মান নির্ধারণ করেন এবং আপনি একটি 3 পান তবে এটি একক হতে পারে EatsFish, এবং আবার HasClawsএবং এর সংমিশ্রণ হতে পারে CanFly। যদি আপনার গণনা একচেটিয়া রাজ্যকে বোঝায় তবেই একটানা মানগুলি ভাল হয়, তবে পতাকাগুলির সংমিশ্রণের জন্য মানগুলি বিট-এক্সক্লুসিভ হওয়া দরকার।
খ্রিস্টান সেভেরিন

122

দ্রষ্টব্য (এছাড়াও কিছুটা বন্ধ বিষয়): স্বতন্ত্র পতাকা তৈরির আর একটি উপায় কিছুটা শিফট ব্যবহার করে করা যেতে পারে। আমি নিজেই এটিকে পড়তে সহজ মনে করি।

enum Flags
{
    A = 1 << 0, // binary 0001
    B = 1 << 1, // binary 0010
    C = 1 << 2, // binary 0100
    D = 1 << 3, // binary 1000
};

এটি মান হিসাবে একটি int পর্যন্ত ধরে রাখতে পারে যে বেশিরভাগ সময়, 32 পতাকা যা পরিষ্কারভাবে শিফ্ট পরিমাণে প্রতিফলিত হয়।


2
আপনি কি দয়া করে সর্বশেষ কমা (3,) মুছে ফেলতে এবং কোডটি অনুলিপি এবং আটকানো সহজ করার জন্য after এর পরে একটি কোলন যুক্ত করতে পারেন? ধন্যবাদ
কাতু

4
হেক্সিডিসিমালের কোনও উল্লেখ নেই? ব্লাসফেমি!
ফারাপ

1
@ জেমি, কার্ডিনালগুলি সর্বদা 1 দিয়ে শুরু হয়, আপনি কার সাথে কথা বলছেন তার উপর নির্ভর করে কেবলমাত্র অর্ডিনালগুলি 0 বা 1 দিয়ে শুরু হতে পারে।
মাইকেল

2
@ মিশেল, সত্য! একটি এনামে, আপনি সাধারণত BLAH_NONE এর জন্য 0 সংরক্ষণ করেন। :-) সেই স্মৃতি জারি করার জন্য ধন্যবাদ!
জেমি

1
@ কাতু final চূড়ান্ত গণনার ক্ষেত্রে অতিমাত্রায় কমাটি স্ট্যান্ডার্ড দ্বারা অনুমোদিত। আমি এটি পছন্দ করি না তবে স্ট্রস্ট্রপ আমাকে কী বলবে তা আমি ইতিমধ্যে জানি ... "আপনি এটি পছন্দ করেন না? ভাল নিজের ভাষা তৈরি করতে নির্দ্বিধায় অনুভব করুন। আমি করেছি।"
এলজয়

55

আমার মতো অলস লোকের জন্য, এখানে অনুলিপি করা এবং অনুলিপি করার সমাধান:

template<class T> inline T operator~ (T a) { return (T)~(int)a; }
template<class T> inline T operator| (T a, T b) { return (T)((int)a | (int)b); }
template<class T> inline T operator& (T a, T b) { return (T)((int)a & (int)b); }
template<class T> inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); }
template<class T> inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); }
template<class T> inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); }
template<class T> inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); }

23
+1 অলসতা প্রোগ্রামারটির তিনটি দুর্দান্ত গুণগুলির মধ্যে একটি: থ্রিভিটিউজ ডটকম
ফরাপ

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

@ রাই: আপনি সর্বদা এটি একটি নেমস্পেসে রাখতে পারেন এবং usingএটি যেখানে উপযুক্ত, ঠিক তেমনই রাখতে পারেন rel_ops
ইয়াকভ গালকা

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

19
এই কোড ব্যবহার করবেন না। এটি ভুল করে কোনও ক্লাস পরিচালনা করার জন্য দরজা উন্মুক্ত করে। এছাড়াও কোডটি পুরানো শৈলীর usingালাই ব্যবহার করছে যা জিসিসির কঠোর সংকলন শিতালশাহ . com/p/… দিয়ে যাবে না ।
শীতল শাহ

44

নোট করুন আপনি যদি উইন্ডোজ পরিবেশে DEFINE_ENUM_FLAG_OPERATORSকাজ করছেন তবে উইন্টএনএইচ-তে একটি ম্যাক্রো সংজ্ঞায়িত করা হয়েছে যা আপনার জন্য কাজ করে। সুতরাং এই ক্ষেত্রে, আপনি এটি করতে পারেন:

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};
DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags)

seahawk.flags = CanFly | EatsFish | Endangered;

43

সমুদ্রহক.ফ্লেগগুলি পরিবর্তনশীল কোন প্রকারের?

স্ট্যান্ডার্ড সি ++ এ, গণনাগুলি টাইপ-নিরাপদ নয়। তারা কার্যকরভাবে পূর্ণসংখ্যার হয়।

অ্যানিম্যালফ্লেগগুলি আপনার ভেরিয়েবলের ধরণ নয়। আপনার পরিবর্তনশীলটি int হওয়া উচিত এবং ত্রুটিটি চলে যাবে।

কিছু অন্যান্য ব্যক্তির পরামর্শ মতো হেক্সাডেসিমাল মান রাখার প্রয়োজন নেই। এতে কোন পার্থক্য নেই.

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

এনাম টাইপ হ'ল ইনট্রেটের একটি সীমাবদ্ধ উপসেট যার মান এটির গণ্য করা মানগুলির মধ্যে একটি। অতএব, আপনি যখন এই ব্যাপ্তির বাইরে কিছু নতুন মান তৈরি করেন, আপনি আপনার এনাম টাইপের একটি পরিবর্তনশীলকে ingালাই ছাড়াই এটি নির্ধারণ করতে পারবেন না।

আপনি চাইলে এনাম মান প্রকারগুলিও পরিবর্তন করতে পারেন তবে এই প্রশ্নের কোনও মানে নেই।

সম্পাদনা করুন: পোস্টারটিতে বলা হয়েছে যে তারা সুরক্ষার প্রকারের সাথে উদ্বিগ্ন ছিল এবং তারা এমন কোনও মান চায় না যা ইন্ট টাইপের অভ্যন্তরে না থাকা উচিত।

তবে এ্যানিমালফ্লাগসের প্রকারের ভেরিয়েবলের ভিতরে অ্যানিমালফ্ল্যাগের সীমার বাইরে কোনও মান রাখা টাইপ অনিরাপদ হবে।

প্রকারের ভিতরে থাকা সত্ত্বেও সীমার মানগুলি খুঁজে বের করার একটি নিরাপদ উপায় রয়েছে ...

int iFlags = HasClaws | CanFly;
//InvalidAnimalFlagMaxValue-1 gives you a value of all the bits 
// smaller than itself set to 1
//This check makes sure that no other bits are set.
assert(iFlags & ~(InvalidAnimalFlagMaxValue-1) == 0);

enum AnimalFlags {
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8,

    // put new enum values above here
    InvalidAnimalFlagMaxValue = 16
};

উপরেরটি আপনাকে অন্য এনাম থেকে একটি অবৈধ পতাকা লাগানো থেকে বিরত রাখে না যার মান 1,2,4 বা 8 রয়েছে has

আপনি যদি নিখুঁত প্রকারের সুরক্ষা চান তবে আপনি কেবল একটি স্টাডি :: তৈরি করতে এবং সেখানে প্রতিটি পতাকা সংরক্ষণ করতে পারেন। এটি স্থান দক্ষ নয়, তবে এটি নিরাপদ প্রকারের এবং বিটফ্ল্যাগের মতো দক্ষতা আপনাকে দেয়।

সি ++ 0 এক্স দ্রষ্টব্য: শক্তভাবে টাইপ করা এনামগুলি

সি ++ 0 এক্সে আপনি শেষ পর্যন্ত নিরাপদ এনাম মানগুলি টাইপ করতে পারেন ....

enum class AnimalFlags {
    CanFly = 2,
    HasClaws = 4
};

if(CanFly == 2) { }//Compiling error

4
এনাম মানগুলি পূর্ণসংখ্যা নয়, তবে তারা খুব সহজেই পূর্ণসংখ্যায় রূপান্তর করে। ধরণ HasClaws | CanFlyকিছু পূর্ণসংখ্যা ধরন, কিন্তু প্রকার HasClawsহল AnimalFlags, না একটি পূর্ণসংখ্যা প্রকার।
করু

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

3
@ স্কট: এটি লক্ষণীয় যে সি ++ স্ট্যান্ডার্ড কোনও এনাম উদাহরণের মানের বৈধ পরিসীমাটিকে সেভাবে সংজ্ঞায়িত করে। "একটি গণনার জন্য যেখানে এমিন হ'ল ক্ষুদ্রতম গণক এবং ইমেক্স বৃহত্তম, গণনার মানগুলি বিমিন থেকে বিম্যাক্সের মধ্যে রয়েছে, নীচে নিম্নরূপ: কে দু'জনের পরিপূরক প্রতিনিধিত্বের জন্য 1 এবং একটির জন্য 0 হতে দিন ' পরিপূর্ণ বা সাইন-মাত্রার প্রতিনিধিত্ব। bmax চেয়ে ক্ষুদ্রতম মান বেশী বা সমান max(|emin| − K, |emax|)এবং সমান (1u<<M) - 1, যেখানে Mএকটি অ-নেতিবাচক পূর্ণসংখ্যা। "
বেন ভয়েগট

যারা (আমার মতো) কেবল ব্যবহারিক কিছু চান যা এনাম মানগুলিকে বিটওয়াসার সাথে চালিত করতে দেয় এবং টেমপ্লেট এবং টাইপ কাস্টিংয়ের সাথে খুব কুৎসিত দেখায় না, এটি একটি ভাল সমাধান; শুধু টাইপ হতে ভেরিয়েবল সংজ্ঞায়িত int
এরিক সোকলোভস্কি 24'19

এছাড়াও নোট করুন যে সি ++ এ নিয়মিত তার অন্তর্নিহিত ধরণের হিসাবে enumপ্রযুক্তিগতভাবে ডিফল্ট হয় না int(হয় প্রাক-সি ++ 11 (আইআইআরসি), অথবা পোস্ট-সি ++ 11 যখন কোনও অন্তর্নিহিত ধরণ নির্দিষ্ট না করা থাকে) যদিও enum class তা করে । পরিবর্তে, অন্তর্নিহিত ধরণটি সমস্ত গণকের প্রতিনিধিত্ব করতে পর্যাপ্ত পরিমাণে ডিফল্ট হয়, একমাত্র সত্যিকারের কঠোর নিয়মের সাথে intএটি স্পষ্টতই হওয়া দরকার হলে তার চেয়ে বড় । মূলত, অন্তর্নিহিত ধরণটি "প্যারাফ্রেসড" হিসাবে নির্দিষ্ট করা হয় "যা কিছু কাজ করে, তবে এটি সম্ভবত int গণনাকারীদের পক্ষে খুব বড় না হলে int"।
জাস্টিন সময় - মনিকাকে

26

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

ব্রায়ান আর বন্ডি নীচে যেমন লিখেছেন, আপনি যদি সি ++ 11 ব্যবহার করেন (যা সবার উচিত, এটি ভাল) তবে আপনি এখন আরও সহজেই এর মাধ্যমে এটি করতে পারেন enum class:

enum class ObjectType : uint32_t
{
    ANIMAL = (1 << 0),
    VEGETABLE = (1 << 1),
    MINERAL = (1 << 2)
};


constexpr enum ObjectType operator |( const enum ObjectType selfValue, const enum ObjectType inValue )
{
    return (enum ObjectType)(uint32_t(selfValue) | uint32_t(inValue));
}

// ... add more operators here. 

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

প্রি -11 সি ++ উপভাষার সাথে আটকে থাকা লোকদের জন্য

যদি আমি এমন একটি সংকলক দিয়ে আটকে থাকি যেটি সি ++ 11 সমর্থন করে না, তবে আমি কোনও শ্রেণিতে একটি প্রকার টাইপ মোড়ানোর সাথে যেতে চাই যা কেবলমাত্র বিটওয়াইড অপারেটরগুলির ব্যবহারের অনুমতি দেয় এবং তার মান নির্ধারণের জন্য এনাম থেকে প্রকারগুলি:

template<class ENUM,class UNDERLYING=typename std::underlying_type<ENUM>::type>
class SafeEnum
{
public:
    SafeEnum() : mFlags(0) {}
    SafeEnum( ENUM singleFlag ) : mFlags(singleFlag) {}
    SafeEnum( const SafeEnum& original ) : mFlags(original.mFlags) {}

    SafeEnum&   operator |=( ENUM addValue )    { mFlags |= addValue; return *this; }
    SafeEnum    operator |( ENUM addValue )     { SafeEnum  result(*this); result |= addValue; return result; }
    SafeEnum&   operator &=( ENUM maskValue )   { mFlags &= maskValue; return *this; }
    SafeEnum    operator &( ENUM maskValue )    { SafeEnum  result(*this); result &= maskValue; return result; }
    SafeEnum    operator ~()    { SafeEnum  result(*this); result.mFlags = ~result.mFlags; return result; }
    explicit operator bool()                    { return mFlags != 0; }

protected:
    UNDERLYING  mFlags;
};

আপনি এটি নিয়মিত এনাম + টাইপএফের মতো অনেকটাই সংজ্ঞায়িত করতে পারেন:

enum TFlags_
{
    EFlagsNone  = 0,
    EFlagOne    = (1 << 0),
    EFlagTwo    = (1 << 1),
    EFlagThree  = (1 << 2),
    EFlagFour   = (1 << 3)
};

typedef SafeEnum<enum TFlags_>  TFlags;

এবং ব্যবহারও একইরকম:

TFlags      myFlags;

myFlags |= EFlagTwo;
myFlags |= EFlagThree;

if( myFlags & EFlagTwo )
    std::cout << "flag 2 is set" << std::endl;
if( (myFlags & EFlagFour) == EFlagsNone )
    std::cout << "flag 4 is not set" << std::endl;

এবং আপনি enum foo : typeদ্বিতীয় টেম্পলেট প্যারামিটার ব্যবহার করে বাইনারি-স্থিতিশীল এনামগুলির (যেমন সি ++ 11 এর মতো ) জন্য অন্তর্নিহিত প্রকারটিকে ওভাররাইড করতে পারেন typedef SafeEnum<enum TFlags_,uint8_t> TFlags;

আমি operator boolসি ++ 11 এর explicitকীওয়ার্ড দিয়ে ওভাররাইড চিহ্নিত করেছি যাতে এটি রূপান্তরগুলির ফলে আটকাতে পারে কারণ এগুলি যখন লেখার সময় পতাকাগুলির সেটগুলি 0 বা 1 এ ধসে পড়তে পারে। আপনি সি ++ 11 ব্যবহার করতে পারবেন না তাহলে সেই জমিদার ছেড়ে এবং উদাহরণ ব্যবহার প্রথম শর্তসাপেক্ষ পুনর্লিখন (myFlags & EFlagTwo) == EFlagTwo


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

17

স্ট্যান্ডার্ড লাইব্রেরি ক্লাস বিটসেট ব্যবহার করে এখানে প্রদর্শিত হিসাবে এটি করার সবচেয়ে সহজ উপায় ।

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

    template <class T, int N>
class FlagSet
{

    bitset<N> bits;

    FlagSet(T enumVal)
    {
        bits.set(enumVal);
    }

    // etc.
};

enum MyFlags
{
    FLAG_ONE,
    FLAG_TWO
};

FlagSet<MyFlags, 2> myFlag;

4
আরও সম্পূর্ণ কোডের জন্য এটি দেখুন: কোডরেভিউ.স্ট্যাকেক্সেঞ্জার
শীতল শাহ

11

আমার মতে এখন পর্যন্ত কোনও উত্তরই আদর্শ নয়। আদর্শ হতে আমি সমাধানটি আশা করব:

  1. সমর্থন ==, !=, =, &, &=, |, |=এবং ~প্রচলিত অর্থে অপারেটার (অর্থাত a & b)
  2. প্রকারের সুরক্ষিত থাকুন যেমন নন-গণনা করা মান যেমন আক্ষরিক বা পূর্ণসংখ্যার ধরণের জন্য নির্ধারিত হতে দেওয়া হয় না (গণনা মূল্যের বিটওয়াইজ সংযোজন ব্যতীত) বা একটি এনুম ভেরিয়েবলকে একটি পূর্ণসংখ্যার ধরণের জন্য নির্ধারিত করার অনুমতি দিন
  3. অনুমতি হিসাবে এক্সপ্রেশন if (a & b)...
  4. অশুভ ম্যাক্রো, বাস্তবায়ন নির্দিষ্ট বৈশিষ্ট্য বা অন্যান্য হ্যাকের প্রয়োজন হয় না

সমাধানগুলি এখন পর্যন্ত বেশিরভাগ 2 বা 3 পয়েন্টে পড়ে যায় ওয়েবড্যান্সার হ'ল আমার মতে বন্ধ হওয়া কিন্তু পয়েন্ট 3 এ ব্যর্থ হয় এবং প্রতিটি এনামের জন্য পুনরাবৃত্তি করা প্রয়োজন।

আমার প্রস্তাবিত সমাধানটি ওয়েবড্যান্সারের একটি সাধারণ সংস্করণ যা এটি পয়েন্ট 3 কেও সম্বোধন করে:

#include <cstdint>
#include <type_traits>

template<typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
class auto_bool
{
    T val_;
public:
    constexpr auto_bool(T val) : val_(val) {}
    constexpr operator T() const { return val_; }
    constexpr explicit operator bool() const
    {
        return static_cast<std::underlying_type_t<T>>(val_) != 0;
    }
};

template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
constexpr auto_bool<T> operator&(T lhs, T rhs)
{
    return static_cast<T>(
        static_cast<typename std::underlying_type<T>::type>(lhs) &
        static_cast<typename std::underlying_type<T>::type>(rhs));
}

template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
constexpr T operator|(T lhs, T rhs)
{
    return static_cast<T>(
        static_cast<typename std::underlying_type<T>::type>(lhs) |
        static_cast<typename std::underlying_type<T>::type>(rhs));
}

enum class AnimalFlags : uint8_t 
{
    HasClaws = 1,
    CanFly = 2,
    EatsFish = 4,
    Endangered = 8
};

enum class PlantFlags : uint8_t
{
    HasLeaves = 1,
    HasFlowers = 2,
    HasFruit = 4,
    HasThorns = 8
};

int main()
{
    AnimalFlags seahawk = AnimalFlags::CanFly;        // Compiles, as expected
    AnimalFlags lion = AnimalFlags::HasClaws;         // Compiles, as expected
    PlantFlags rose = PlantFlags::HasFlowers;         // Compiles, as expected
//  rose = 1;                                         // Won't compile, as expected
    if (seahawk != lion) {}                           // Compiles, as expected
//  if (seahawk == rose) {}                           // Won't compile, as expected
//  seahawk = PlantFlags::HasThorns;                  // Won't compile, as expected
    seahawk = seahawk | AnimalFlags::EatsFish;        // Compiles, as expected
    lion = AnimalFlags::HasClaws |                    // Compiles, as expected
           AnimalFlags::Endangered;
//  int eagle = AnimalFlags::CanFly |                 // Won't compile, as expected
//              AnimalFlags::HasClaws;
//  int has_claws = seahawk & AnimalFlags::CanFly;    // Won't compile, as expected
    if (seahawk & AnimalFlags::CanFly) {}             // Compiles, as expected
    seahawk = seahawk & AnimalFlags::CanFly;          // Compiles, as expected

    return 0;
}

এটি প্রয়োজনীয় অপারেটরগুলির ওভারলোডগুলি তৈরি করে তবে সেগুলি গণনার ক্ষেত্রে সীমাবদ্ধ করতে SFINAE ব্যবহার করে। লক্ষ্য করুন সংক্ষিপ্ততা স্বার্থে আমি অপারেটার কিন্তু শুধুমাত্র এক যে কোন ভিন্ন সব সংজ্ঞায়িত করেন নি &। অপারেটরগুলি বর্তমানে বিশ্বব্যাপী (অর্থাত্ সমস্ত গণিত প্রকারের ক্ষেত্রে প্রযোজ্য) তবে নামমাত্রে (আমি কী করি) ওভারলোডগুলি রেখে বা অতিরিক্ত এসএফআইএনই শর্ত যুক্ত করে (সম্ভবত নির্দিষ্ট অন্তর্নিহিত ধরণের ব্যবহার করে বা বিশেষত তৈরি প্রকারের এলিয়াসগুলি ব্যবহার করে) এটি হ্রাস করা যেতে পারে )। underlying_type_tএকটি সি ++ 14 বৈশিষ্ট্য কিন্তু এটা ভাল সমর্থিত হবে বলে মনে হয় এবং একটি সহজ সঙ্গে সি ++ 11 এর জন্য অনুকরণ করা সহজtemplate<typename T> using underlying_type_t = underlying_type<T>::type;


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

@ ওয়েবেড্যান্সার, আপনি অবশ্যই সঠিক, তবে তারপরে আমি ইতিমধ্যে আমার উত্তরে বলেছি। আমি সমস্যাটি সমাধানের দুটি উপায়ও পরামর্শ দিয়েছি - এটি একটি নেমস্পেসে রেখে দেওয়া বা আরও নিয়ন্ত্রিত এসএফআইএনএই শর্ত ব্যবহার করা।
ট্রেভর

আমার বক্তব্যটি যদি আপনি সত্যিই সংকীর্ণ নেমস্পেস না করেন (উদাহরণস্বরূপ নেমস্পেস AllMyFlagEnums) বা একটি SFINAE শর্ত না থাকে যে কোনও উপায়ে কেবলমাত্র কিছু সঠিক এনাম নির্বাচন করে যা মনে মনে ভেঙে যায়। এটি ঝুঁকিপূর্ণ করার পরিবর্তে, আমি একটি "পাঠ্য টেম্পলেট" অনুলিপি করে আটকান যেখানে আমি কেবল তখন এনাম নাম এবং কখনও কখনও "দুষ্ট" ম্যাক্রোগুলি প্রতিস্থাপন করি। আমি আরও ভাল উপায় আছে ইচ্ছুক।
ওয়েবড্যান্সার

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

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

8

সি ++ স্ট্যান্ডার্ড স্পষ্টভাবে এটি সম্পর্কে কথা বলে, বিভাগ "17.5.2.1.3 বিটমাস্কের ধরণগুলি" দেখুন:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf

এই "টেমপ্লেট" আপনি পেয়েছেন:

enum AnimalFlags : unsigned int
{
    HasClaws = 1,
    CanFly = 2,
    EatsFish = 4,
    Endangered = 8
};

constexpr AnimalFlags operator|(AnimalFlags X, AnimalFlags Y) {
    return static_cast<AnimalFlags>(
        static_cast<unsigned int>(X) | static_cast<unsigned int>(Y));
}

AnimalFlags& operator|=(AnimalFlags& X, AnimalFlags Y) {
    X = X | Y; return X;
}

এবং অন্যান্য অপারেটরদের জন্য একই। এছাড়াও "কনটেক্সটপ্রিপ" নোট করুন, আপনি যদি অপারেটরগুলি সংকলনের সময়টি সংযোজন করতে সক্ষম হতে চান তবে এটি প্রয়োজন।

আপনি যদি সি ++ / সিএলআই ব্যবহার করে থাকেন এবং রেফ শ্রেণির সদস্যদের এনাম করার জন্য আপনাকে নিয়োগ করতে সক্ষম করতে চান তবে তার পরিবর্তে ট্র্যাকিং রেফারেন্স ব্যবহার করতে হবে:

AnimalFlags% operator|=(AnimalFlags% X, AnimalFlags Y) {
    X = X | Y; return X;
}

দ্রষ্টব্য: এই নমুনাটি সম্পূর্ণ নয়, অপারেটরগুলির সম্পূর্ণ সেটের জন্য বিভাগ "17.5.2.1.3 বিটমাস্ক প্রকার" দেখুন।


6

আমি নিজেকে একই প্রশ্ন জিজ্ঞাসা করে দেখতে পেয়েছি এবং সোরুর মতো জেনেরিক সি ++ 11 ভিত্তিক সমাধান নিয়ে এসেছি:

template <typename TENUM>
class FlagSet {

private:
    using TUNDER = typename std::underlying_type<TENUM>::type;
    std::bitset<std::numeric_limits<TUNDER>::max()> m_flags;

public:
    FlagSet() = default;

    template <typename... ARGS>
    FlagSet(TENUM f, ARGS... args) : FlagSet(args...)
    {   
        set(f);
    }   
    FlagSet& set(TENUM f)
    {   
        m_flags.set(static_cast<TUNDER>(f));
        return *this;
    }   
    bool test(TENUM f)
    {   
        return m_flags.test(static_cast<TUNDER>(f));
    }   
    FlagSet& operator|=(TENUM f)
    {   
        return set(f);
    }   
};

ইন্টারফেস স্বাদ উন্নত করা যেতে পারে। তাহলে এটি এর মতো ব্যবহার করা যেতে পারে:

FlagSet<Flags> flags{Flags::FLAG_A, Flags::FLAG_C};
flags |= Flags::FLAG_D;

2
আরও ভাল এবং আরও সম্পূর্ণ কোডের জন্য এটি দেখুন: কোডেরভিউ.স্ট্যাকেক্সেঞ্জার.কমেসেশনস
শীতল শাহ

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

1
এটি একটি বিশাল বিটসেট!
হালকা ঘোড়দৌড়

(সম্ভাব্য ...)
অরবিট

5

যদি আপনার সংকলকটি এখনও দৃ strongly়ভাবে টাইপ করা এনামগুলিকে সমর্থন না করে তবে আপনি সি ++ উত্স থেকে নিম্নলিখিত নিবন্ধটি দেখে নিতে পারেন :

বিমূর্ত থেকে:

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


5

আমি নিম্নলিখিত ম্যাক্রো ব্যবহার:

#define ENUM_FLAG_OPERATORS(T)                                                                                                                                            \
    inline T operator~ (T a) { return static_cast<T>( ~static_cast<std::underlying_type<T>::type>(a) ); }                                                                       \
    inline T operator| (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) | static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T operator& (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) & static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T operator^ (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) ^ static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T& operator|= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) |= static_cast<std::underlying_type<T>::type>(b) ); }   \
    inline T& operator&= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) &= static_cast<std::underlying_type<T>::type>(b) ); }   \
    inline T& operator^= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) ^= static_cast<std::underlying_type<T>::type>(b) ); }

এটি উপরে বর্ণিতগুলির সাথে সমান তবে বেশ কয়েকটি উন্নতি রয়েছে:

  • এটি টাইপ নিরাপদ (এটি ধরে নেই যে অন্তর্নিহিত টাইপটি একটি an int )
  • এর জন্য ম্যানুয়ালি অন্তর্নিহিত ধরণের নির্দিষ্টকরণের প্রয়োজন নেই (@ লুনারএক্লিপসের উত্তরের বিপরীতে)

এটিতে টাইপ_ট্রেটস অন্তর্ভুক্ত করা দরকার:

#include <type_traits>

4

টেমপ্লেট এবং অভাবের কারণে আমি সিউইটি +99 এর জন্য তার কোডটি স্থির করে এবং সেফ বুল আইডিয়মটি ব্যবহার করে আমি ইউলুইনটিস উত্তরটি বিশদভাবে বলতে চাইstd::underlying_type<>explicitসি ++ ১১ এর নীচে সি ++ সংস্করণগুলির কীওয়ার্ডের ।

আমি এটিকেও সংশোধন করেছি যাতে এনাম মানগুলি কোনও স্পষ্ট নির্ধারিত কাজ ছাড়াই অনুক্রমিক হতে পারে, যাতে আপনি থাকতে পারেন

enum AnimalFlags_
{
    HasClaws,
    CanFly,
    EatsFish,
    Endangered
};
typedef FlagsEnum<AnimalFlags_> AnimalFlags;

seahawk.flags = AnimalFlags() | CanFly | EatsFish | Endangered;

এরপরে আপনি কাঁচা পতাকাগুলির সাথে মূল্য পেতে পারেন

seahawk.flags.value();

এখানে কোড।

template <typename EnumType, typename Underlying = int>
class FlagsEnum
{
    typedef Underlying FlagsEnum::* RestrictedBool;

public:
    FlagsEnum() : m_flags(Underlying()) {}

    FlagsEnum(EnumType singleFlag):
        m_flags(1 << singleFlag)
    {}

    FlagsEnum(const FlagsEnum& original):
        m_flags(original.m_flags)
    {}

    FlagsEnum& operator |=(const FlagsEnum& f) {
        m_flags |= f.m_flags;
        return *this;
    }

    FlagsEnum& operator &=(const FlagsEnum& f) {
        m_flags &= f.m_flags;
        return *this;
    }

    friend FlagsEnum operator |(const FlagsEnum& f1, const FlagsEnum& f2) {
        return FlagsEnum(f1) |= f2;
    }

    friend FlagsEnum operator &(const FlagsEnum& f1, const FlagsEnum& f2) {
        return FlagsEnum(f1) &= f2;
    }

    FlagsEnum operator ~() const {
        FlagsEnum result(*this);
        result.m_flags = ~result.m_flags;
        return result;
    }

    operator RestrictedBool() const {
        return m_flags ? &FlagsEnum::m_flags : 0;
    }

    Underlying value() const {
        return m_flags;
    }

protected:
    Underlying  m_flags;
};

3

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

struct AnimalProperties
{
    bool HasClaws : 1;
    bool CanFly : 1;
    bool EatsFish : 1;
    bool Endangered : 1;
};

union AnimalDescription
{
    AnimalProperties Properties;
    int Flags;
};

void TestUnionFlags()
{
    AnimalDescription propertiesA;
    propertiesA.Properties.CanFly = true;

    AnimalDescription propertiesB = propertiesA;
    propertiesB.Properties.EatsFish = true;

    if( propertiesA.Flags == propertiesB.Flags )
    {
        cout << "Life is terrible :(";
    }
    else
    {
        cout << "Life is great!";
    }

    AnimalDescription propertiesC = propertiesA;
    if( propertiesA.Flags == propertiesC.Flags )
    {
        cout << "Life is great!";
    }
    else
    {
        cout << "Life is terrible :(";
    }
}

আমরা দেখতে পাচ্ছি যে জীবন দুর্দান্ত, আমাদের আলাদা মূল্যবোধ রয়েছে এবং আমাদের কাছে & এবং | আমাদের হৃদয়ের বিষয়বস্তুতে, এর বিটগুলি কী বোঝায় তার প্রসঙ্গ এখনও রয়েছে। আমার কাছে সবকিছু সুসংগত এবং অনুমানযোগ্য ... যতক্ষণ না আমি উইন 10 এক্স 64 তে মাইক্রোসফ্টের ভিসি ++ সংকলক ডাব্লু / আপডেট 3 ব্যবহার করি এবং আমার সংকলক পতাকাগুলি স্পর্শ না করি :)

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

class AnimalDefinition {
public:
    static AnimalDefinition *GetAnimalDefinition( AnimalFlags flags );   //A little too obvious for my taste... NEXT!
    static AnimalDefinition *GetAnimalDefinition( AnimalProperties properties );   //Oh I see how to use this! BORING, NEXT!
    static AnimalDefinition *GetAnimalDefinition( int flags ); //hmm, wish I could see how to construct a valid "flags" int without CrossFingers+Ctrl+Shift+F("Animal*"). Maybe just hard-code 16 or something?

    AnimalFlags animalFlags;  //Well this is *way* too hard to break unintentionally, screw this!
    int flags; //PERFECT! Nothing will ever go wrong here... 
    //wait, what values are used for this particular flags field? Is this AnimalFlags or ObjectFlags? Or is it RuntimePlatformFlags? Does it matter? Where's the documentation? 
    //Well luckily anyone in the code base and get confused and destroy the whole program! At least I don't need to static_cast anymore, phew!

    private:
    AnimalDescription m_description; //Oh I know what this is. All of the mystery and excitement of life has been stolen away :(
}

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

দুর্ভাগ্যক্রমে যদি আপনি চান যে আপনার কোডটি পোর্টেবল হতে পারে তবে আমি মনে করি না এ এর ​​কোনও উপায় আছে) বিট লেআউটের গ্যারান্টি বা বি) সংকলনের সময় বিট লেআউট নির্ধারণ করুন (যাতে আপনি এটি ট্র্যাক করতে পারেন এবং কমপক্ষে পরিবর্তনের জন্য কমপক্ষে সঠিক করতে পারেন) সংস্করণ / প্ল্যাটফর্ম ইত্যাদি) বিট ক্ষেত্র সহ কোনও স্ট্রাক্টে অফসেট

রানটাইমের সময় আপনি কৌতুক খেলতে পারেন / ক্ষেত্রগুলি নির্ধারণ করতে এবং পতাকাগুলিতে কী কী বিটগুলি পরিবর্তন হয়েছে তা Xoring দেখতে আমার কাছে অত্যন্ত কৃপণ মনে হয় যদিও শ্লোকে 100% ধারাবাহিক, প্ল্যাটফর্মটি স্বাধীন এবং সম্পূর্ণরূপে নির্বিচার সমাধান রয়েছে যেমন: একটি ENUM।

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

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


3

কেবল সিনট্যাকটিক চিনি। কোনও অতিরিক্ত মেটাডেটা নেই।

namespace UserRole // grupy
{ 
    constexpr uint8_t dea = 1;
    constexpr uint8_t red = 2;
    constexpr uint8_t stu = 4;
    constexpr uint8_t kie = 8;
    constexpr uint8_t adm = 16;
    constexpr uint8_t mas = 32;
}

ইন্টিগ্রাল টাইপের ফ্ল্যাগ অপারেটর কেবল কাজ করে।


আইএমএইচও এটি সর্বোত্তম উত্তর। পরিষ্কার এবং সহজ, সহজ ক্লায়েন্ট সিনট্যাক্স। আমি কেবল "কনস্টেক্সপ্রিন্ট uint8_t" এর পরিবর্তে "কনস্টেক্স" ব্যবহার করব, তবে ধারণাটি একই।
yoyo

(দুঃখিত, "constexpr int- এ")
Yoyo

3

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

আমার সমাধানটি হ'ল এনাম-ক্লাসের অন্তর্নিহিত প্রকারটি ব্যবহার করে টাইপ-সেফ বিটওয়াইজ অপারেশনগুলির জন্য সমর্থন যুক্ত কেবলমাত্র এনাম-ইনস্ট্যান্টিয়েটেড টেম্পলেট ফাংশন তৈরি করা হবে:

ফাইল: EnumClassBitwise.h

#pragma once
#ifndef _ENUM_CLASS_BITWISE_H_
#define _ENUM_CLASS_BITWISE_H_

#include <type_traits>

//unary ~operator    
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum& operator~ (Enum& val)
{
    val = static_cast<Enum>(~static_cast<std::underlying_type_t<Enum>>(val));
    return val;
}

// & operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator& (Enum lhs, Enum rhs)
{
    return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));
}

// &= operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator&= (Enum& lhs, Enum rhs)
{
    lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));
    return lhs;
}

//| operator

template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator| (Enum lhs, Enum rhs)
{
    return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));
}
//|= operator

template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum& operator|= (Enum& lhs, Enum rhs)
{
    lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));
    return lhs;
}

#endif // _ENUM_CLASS_BITWISE_H_

সুবিধার্থে এবং ভুলগুলি হ্রাস করার জন্য, আপনি এনামগুলি এবং পূর্ণসংখ্যার জন্য আপনার বিট ফ্ল্যাগ ক্রিয়াকলাপগুলি মোড়াতে চাইতে পারেন:

ফাইল: BitFlags.h

#pragma once
#ifndef _BIT_FLAGS_H_
#define _BIT_FLAGS_H_

#include "EnumClassBitwise.h"

 template<typename T>
 class BitFlags
 {
 public:

     constexpr inline BitFlags() = default;
     constexpr inline BitFlags(T value) { mValue = value; }
     constexpr inline BitFlags operator| (T rhs) const { return mValue | rhs; }
     constexpr inline BitFlags operator& (T rhs) const { return mValue & rhs; }
     constexpr inline BitFlags operator~ () const { return ~mValue; }
     constexpr inline operator T() const { return mValue; }
     constexpr inline BitFlags& operator|=(T rhs) { mValue |= rhs; return *this; }
     constexpr inline BitFlags& operator&=(T rhs) { mValue &= rhs; return *this; }
     constexpr inline bool test(T rhs) const { return (mValue & rhs) == rhs; }
     constexpr inline void set(T rhs) { mValue |= rhs; }
     constexpr inline void clear(T rhs) { mValue &= ~rhs; }

 private:
     T mValue;
 };
#endif //#define _BIT_FLAGS_H_

সম্ভাব্য ব্যবহার:

#include <cstdint>
#include <BitFlags.h>
void main()
{
    enum class Options : uint32_t
    { 
          NoOption = 0 << 0
        , Option1  = 1 << 0
        , Option2  = 1 << 1
        , Option3  = 1 << 2
        , Option4  = 1 << 3
    };

    const uint32_t Option1 = 1 << 0;
    const uint32_t Option2 = 1 << 1;
    const uint32_t Option3 = 1 << 2;
    const uint32_t Option4 = 1 << 3;

   //Enum BitFlags
    BitFlags<Options> optionsEnum(Options::NoOption);
    optionsEnum.set(Options::Option1 | Options::Option3);

   //Standard integer BitFlags
    BitFlags<uint32_t> optionsUint32(0);
    optionsUint32.set(Option1 | Option3); 

    return 0;
}

3

@Xaqq ব্যবহার enum পতাকা করার জন্য একটি সত্যিই চমৎকার টাইপ-নিরাপদ উপায় প্রদান করেনি এখানে একটি দ্বারাflag_set বর্গ।

আমি কোডটি গিটহাবে প্রকাশিত করেছি , ব্যবহারটি নিম্নরূপ:

#include "flag_set.hpp"

enum class AnimalFlags : uint8_t {
    HAS_CLAWS,
    CAN_FLY,
    EATS_FISH,
    ENDANGERED,
    _
};

int main()
{
    flag_set<AnimalFlags> seahawkFlags(AnimalFlags::HAS_CLAWS
                                       | AnimalFlags::EATS_FISH
                                       | AnimalFlags::ENDANGERED);

    if (seahawkFlags & AnimalFlags::ENDANGERED)
        cout << "Seahawk is endangered";
}

2

আপনি বিভ্রান্তিকর জিনিস এবং অবজেক্টগুলির সংগ্রহ করছেন। বিশেষত, আপনি বাইনারি পতাকাগুলির সেট সহ বাইনারি পতাকাগুলি বিভ্রান্ত করছেন। একটি সঠিক সমাধান দেখতে হবে:

// These are individual flags
enum AnimalFlag // Flag, not Flags
{
    HasClaws = 0,
    CanFly,
    EatsFish,
    Endangered
};

class AnimalFlagSet
{
    int m_Flags;

  public:

    AnimalFlagSet() : m_Flags(0) { }

    void Set( AnimalFlag flag ) { m_Flags |= (1 << flag); }

    void Clear( AnimalFlag flag ) { m_Flags &= ~ (1 << flag); }

    bool Get( AnimalFlag flag ) const { return (m_Flags >> flag) & 1; }

};

2

এখানে কোনও গুচ্ছ ওভারলোডিং বা ingালাই ছাড়াই আমার সমাধানটি দেওয়া হয়েছে:

namespace EFoobar
{
    enum
    {
        FB_A    = 0x1,
        FB_B    = 0x2,
        FB_C    = 0x4,
    };
    typedef long Flags;
}

void Foobar(EFoobar::Flags flags)
{
    if (flags & EFoobar::FB_A)
        // do sth
        ;
    if (flags & EFoobar::FB_B)
        // do sth
        ;
}

void ExampleUsage()
{
    Foobar(EFoobar::FB_A | EFoobar::FB_B);
    EFoobar::Flags otherflags = 0;
    otherflags|= EFoobar::FB_B;
    otherflags&= ~EFoobar::FB_B;
    Foobar(otherflags);
}

আমি মনে করি এটি ঠিক আছে, কারণ আমরা যাইহোক এনামগুলি এবং ইনটগুলি সনাক্ত করি।

ঠিক তেমন (দীর্ঘতর) সাইড নোট, যদি আপনি থাকেন

  • দৃ strongly়ভাবে টাইপ করা এনাম এবং ব্যবহার করতে চান
  • আপনার পতাকাগুলির সাথে ভারী বিট ফিডিংয়ের দরকার নেই
  • পারফরম্যান্স কোন সমস্যা নয়

আমি এটি সঙ্গে আসতে হবে:

#include <set>

enum class EFoobarFlags
{
    FB_A = 1,
    FB_B,
    FB_C,
};

void Foobar(const std::set<EFoobarFlags>& flags)
{
    if (flags.find(EFoobarFlags::FB_A) != flags.end())
        // do sth
        ;
    if (flags.find(EFoobarFlags::FB_B) != flags.end())
        // do sth
        ;
}

void ExampleUsage()
{
    Foobar({EFoobarFlags::FB_A, EFoobarFlags::FB_B});
    std::set<EFoobarFlags> otherflags{};
    otherflags.insert(EFoobarFlags::FB_B);
    otherflags.erase(EFoobarFlags::FB_B);
    Foobar(otherflags);
}

সি ++ 11 আরম্ভকারী তালিকা এবং ব্যবহার করে enum class


যাইহোক, আমি বরং পতাকাগুলির জন্য এনামগুলিকে মোটেও সুপারিশ করব না। সরল কারণ: পতাকাগুলির সংমিশ্রণগুলি আবার এনামের উপাদান নয়। সুতরাং এটি বেশ অনুপযুক্ত মনে হয়। বিকল্পভাবে আমি using Flags = unsigned longকোনও নেমস্পেস বা স্ট্রাক্টের অভ্যন্তরে একটি পতাকা ব্যবহার করব যা পতাকাটির মান হিসাবে থাকে /*static*/ const Flags XY = 0x01so
Yau

1

উপরে হিসাবে (কাই) বা নিম্নলিখিত করুন। প্রকৃতপক্ষে এনামগুলি হ'ল "গণনা", আপনি যা করতে চান তা একটি সেট রয়েছে, সুতরাং আপনার সত্যিকারের stl :: set ব্যবহার করা উচিত

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};

int main(void)
{
    AnimalFlags seahawk;
    //seahawk= CanFly | EatsFish | Endangered;
    seahawk= static_cast<AnimalFlags>(CanFly | EatsFish | Endangered);
}

1

অবজেক্টিভ-সি এর NS_OPTIONS এর মতো হতে পারে।

#define ENUM(T1, T2) \
enum class T1 : T2; \
inline T1 operator~ (T1 a) { return (T1)~(int)a; } \
inline T1 operator| (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) | static_cast<T2>(b))); } \
inline T1 operator& (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) & static_cast<T2>(b))); } \
inline T1 operator^ (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) ^ static_cast<T2>(b))); } \
inline T1& operator|= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) |= static_cast<T2>(b))); } \
inline T1& operator&= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) &= static_cast<T2>(b))); } \
inline T1& operator^= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) ^= static_cast<T2>(b))); } \
enum class T1 : T2

ENUM(Options, short) {
    FIRST  = 1 << 0,
    SECOND = 1 << 1,
    THIRD  = 1 << 2,
    FOURTH = 1 << 3
};

auto options = Options::FIRST | Options::SECOND;
options |= Options::THIRD;
if ((options & Options::SECOND) == Options::SECOND)
    cout << "Contains second option." << endl;
if ((options & Options::THIRD) == Options::THIRD)
    cout << "Contains third option." << endl;
return 0;

// Output:
// Contains second option. 
// Contains third option.

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