একটি সি ++ এনাম শ্রেণীর উপাদানগুলির সংখ্যা নির্ধারণ করা সম্ভব?


86

একটি সি ++ এর কার্ডিনালিটি নির্ধারণ করা কি সম্ভব enum class:

enum class Example { A, B, C, D, E };

আমি ব্যবহার করার চেষ্টা করেছি sizeof, তবে এটি একটি এনাম উপাদানটির আকার দেয়।

sizeof(Example); // Returns 4 (on my architecture)

কার্ডিনালিটি পাওয়ার জন্য কি কোনও স্ট্যান্ডার্ড উপায় আছে (আমার উদাহরণে 5)?


আমি ভেবেছিলাম একটি নির্দিষ্ট সি ++ 11 প্রক্রিয়া থাকতে পারে
bquenin

6
এটি কোনও সদৃশ নয়। enumএবং enum classএসগুলি খুব আলাদা ধারণা।
জুতো

@ শো ... তারা আসলেই কি?
কাইল স্ট্র্যান্ড

4
এটি একটি এক্সওয়াই সমস্যার মতো মনে হচ্ছে, আমি জানি এটি অনেক আগে থেকেই ছিল তবে আপনি কি মনে করছেন কেন এটি করার দরকার ছিল? আপনি কোনও enum classমানগুলি নিয়ে পুনরাবৃত্তি করতে পারবেন না , সুতরাং সংখ্যাটি জানার জন্য কী হবে?
কল্পনাপ্রসূত মিস্টার ফক্স

উত্তর:


73

সরাসরি নয়, তবে আপনি নিম্নলিখিত কৌশলটি ব্যবহার করতে পারেন:

enum class Example { A, B, C, D, E, Count };

তারপর কার্ডিনালিটি হিসাবে উপলব্ধ static_cast<int>(Example::Count)

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

enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };

এর একটি অসুবিধা হ'ল Example::Countসংকলকটি আপনাকে এনাম ভ্যালু হিসাবে আর্গুমেন্ট হিসাবে ব্যবহার করতে দেয় - তাই আপনি যদি এটি ব্যবহার করেন তবে সাবধান! (যদিও আমি ব্যক্তিগতভাবে এটি অনুশীলনে কোনও সমস্যা বলে মনে করি না))


4
এনাম মানগুলি এনুম ক্লাসে প্রকারের সুরক্ষিত তাই 'গণনা' টাইপ উদাহরণ হবে না এখানে, না ঠিক আছে? আকারের জন্য এটি ব্যবহার করার জন্য আপনাকে প্রথমে একটি গণনা করতে হবে 'কাউন্ট' cast
ম্যান অফ ওয়ান ওয়ে

@ মন: হ্যাঁ, এই কৌশলটি enum classপ্লেইন এস এর পরিবর্তে কিছুটা মেসেঞ্জার enum। আমি পরিষ্কার হওয়ার জন্য একটি কাস্টে সম্পাদনা করব।
ক্যামেরন

11
আপনি যদি এই এনামের সাথে একটি স্যুইচ স্টেটমেন্ট ব্যবহার করেন তবে কোনও শালীন সংকলক আপনাকে সতর্ক করবে যে আপনি একটি কেস মিস করছেন। এটি যদি খুব বেশি ব্যবহার করা হয় যা খুব বিরক্তিকর হতে পারে .. এই নির্দিষ্ট ক্ষেত্রে কেবল একটি পৃথক পরিবর্তনশীল থাকা ভাল।
কল্পনাপ্রসূত মিঃ ফক্স

@ ফ্যান্টাস্টিক এমআরফক্স আমি অভিজ্ঞতার ভিত্তিতে 100% সম্মত হই। সেই সংকলক সতর্কতাও একটি গুরুত্বপূর্ণ one আমি আপনার প্রস্তাবনার চেতনা অনুসারে একটি বিকল্প পদ্ধতির পোস্ট করেছি।
arr_sea

28

সি ++ 17 এর জন্য আপনি magic_enum::enum_countলিব https://github.com/Neargye/magic_enum থেকে ব্যবহার করতে পারেন :

magic_enum::enum_count<Example>() -> 4।

ত্রুটি কোথায়?

এই লাইব্রেরিতে একটি সংকলক-নির্দিষ্ট হ্যাক ব্যবহার করা হয়েছে ( __PRETTY_FUNCTION__/ এর উপর ভিত্তি করে __FUNCSIG__), যা কলঙ্ক> = 5, এমএসভিসি> = 15.3 এবং জিসিসি> = 9 এ কাজ করে।

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

এই পোস্টে এই হ্যাক সম্পর্কে আরও অনেক কিছু https://taylorconor.com/blog/enum-reflection


4
এটা সত্যিই দারুন! এনাম সদস্যদের সংখ্যা গণনা করতে বিদ্যমান কোডটি সংশোধন করার দরকার নেই। এছাড়াও এটি খুব মার্জিতভাবে বাস্তবায়িত হয় (কেবল কোডের মাধ্যমে স্কিমড)!
অ্যান্ড্রি

কেবলমাত্র লিঙ্কযুক্ত উত্তরগুলি নিরুৎসাহিত করা হয়। আপনার লাইব্রেরি যে কৌশলগুলি ব্যবহার করে তার বিবরণ দিয়ে আপনি কি এটি প্রসারিত করতে পারেন?
অ্যাড্রিয়ান ম্যাকার্থি

24
constexpr auto TEST_START_LINE = __LINE__;
enum class TEST { // Subtract extra lines from TEST_SIZE if an entry takes more than one 
    ONE = 7
  , TWO = 6
  , THREE = 9
};
constexpr auto TEST_SIZE = __LINE__ - TEST_START_LINE - 3;

এটি UglyCoder এর উত্তর থেকে উদ্ভূত হয়েছে তবে এটি তিনটি উপায়ে উন্নত করেছে।

  • টাইপ_সেফ এনাম ( BEGINএবং SIZE) তে কোনও অতিরিক্ত উপাদান নেই ( ক্যামেরনের উত্তরেও এই সমস্যা রয়েছে))
    • সংকলকটি তাদের একটি স্যুইচ বিবৃতি (একটি উল্লেখযোগ্য সমস্যা) থেকে নিখোঁজ হওয়ার বিষয়ে অভিযোগ করবে না
    • এগুলি আপনার এনামের প্রত্যাশায় ফাংশনগুলিতে অজান্তেই পাস করা যায় না। (একটি সাধারণ সমস্যা নয়)
  • এটি ব্যবহারের জন্য কাস্টিংয়ের প্রয়োজন হয় না। ( ক্যামেরনের উত্তরেও এই সমস্যা রয়েছে))
  • এনাম শ্রেণীর ধরণের আকারের সাথে বিয়োগটি বিভ্রান্ত হয় না।

এটি ক্যামেরনের উত্তরের উপরে অগলি কোডারের সুবিধা ধরে রেখেছে যে গণনারগণকে নির্বিচার মান দেওয়া যেতে পারে।

একটি সমস্যা ( উগলি কোডারের সাথে ভাগ করা কিন্তু ক্যামেরনের সাথে নয় ) এটি নতুন লাইনের এবং মন্তব্যগুলিকে উল্লেখযোগ্য করে তোলে ... যা অপ্রত্যাশিত। সুতরাং কেউ সাদা TEST_SIZEগণনা সামঞ্জস্য না করে সাদা জায়গা বা একটি মন্তব্য দিয়ে একটি এন্ট্রি যুক্ত করতে পারে ।


7
enum class TEST
{
    BEGIN = __LINE__
    , ONE
    , TWO
    , NUMBER = __LINE__ - BEGIN - 1
};

auto const TEST_SIZE = TEST::NUMBER;

// or this might be better 
constexpr int COUNTER(int val, int )
{
  return val;
}

constexpr int E_START{__COUNTER__};
enum class E
{
    ONE = COUNTER(90, __COUNTER__)  , TWO = COUNTER(1990, __COUNTER__)
};
template<typename T>
constexpr T E_SIZE = __COUNTER__ - E_START - 1;

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

@ কাইল স্ট্র্যান্ড: এই সমস্যাটি রয়েছে: চর ব্যবহার করে এবং আপনার কাছে 256 টিরও বেশি গণনা রয়েছে। কিন্তু কম্পাইলার truncations ইত্যাদি আপনাকে জানানোর জন্য ভাল আচরণ আছে লাইন একটি পূর্ণসংখ্যা আক্ষরিক এবং #line ব্যবহার [1, 2147483647] এর একজন সীমা আছে
UglyCoder

আহ ঠিক আছে. তবুও, এমন একটি এনামও যা অন্যথায় হতে shortপারে integ ক্যবদ্ধতা তৈরির সময় উদ্বিগ্ন হতে পারে । (আমি বলতে চাই এটি আপনার প্রস্তাবিত কৌতুকের চেয়ে unityক্য গড়ে ওঠার আরও একটি সমস্যা))
কাইল স্ট্র্যান্ড

কৌতুক? :-) আমি এটি ব্যবহার করি, তবে খুব কমই এবং যথাযথ রায় দিয়ে। কোডিংয়ের প্রতিটি কিছুর মতো আমাদের পক্ষে ভাল এবং কনস এবং বিশেষত দীর্ঘমেয়াদী রক্ষণাবেক্ষণের প্রভাবগুলি সন্ধান করা উচিত। আমি সম্প্রতি এটি # সিডিফাইনস (ওপেনজিএল wglExt.h) এর তালিকা থেকে এনাম ক্লাস তৈরি করতে ব্যবহার করেছি।
UglyCoder

5

এক্স () - ম্যাক্রোগুলির উপর ভিত্তি করে একটি কৌশল আছে: চিত্র, আপনার নিম্নলিখিত এনাম রয়েছে:

enum MyEnum {BOX, RECT};

এটিকে পুনরায় ফর্ম্যাট করুন:

#define MyEnumDef \
    X(BOX), \
    X(RECT)

তারপরে নিম্নলিখিত কোডটি এনামের প্রকারটি সংজ্ঞায়িত করে:

enum MyEnum
{
#define X(val) val
    MyEnumDef
#undef X
};

এবং নিম্নলিখিত কোড এনাম উপাদানগুলির সংখ্যা গণনা করে:

template <typename ... T> void null(T...) {}

template <typename ... T>
constexpr size_t countLength(T ... args)
{
    null(args...); //kill warnings
    return sizeof...(args);
}

constexpr size_t enumLength()
{
#define XValue(val) #val
    return countLength(MyEnumDef);
#undef XValue
}

...
std::array<int, enumLength()> some_arr; //enumLength() is compile-time
std::cout << enumLength() << std::endl; //result is: 2
...

কমাটি মুছে ফেলা #define MyEnumDef(এবং এটিতে রেখে #define X(val) val) দিয়ে আরও সহজ করা যায় , যা আপনাকে ন্যায়বিচারের সাহায্যে উপাদানগুলির সংখ্যা গণনা করতে দেয় #define X(val) +1 constexpr std::size_t len = MyEnumDef;
হলিব্ল্যাকগ্যাট

4

আপনি চেষ্টা করতে পারেন একটি কৌশল আপনার তালিকার শেষে একটি এনাম মান যোগ করুন এবং এটি আকার হিসাবে ব্যবহার করুন। আপনার উদাহরণে

enum class Example { A, B, C, D, E, ExampleCount };

4
প্লেইন enumএর সাথে আচরণের তুলনায়, এটি ExampleCountটাইপ মতো কাজ করবে না Example। উপাদানের সংখ্যা প্রাপ্ত Example, ExampleCountএকটি পূর্ণসংখ্যা টাইপ কাস্ট করা হবে।
আপেলসপ

3

আপনি যদি বুস্টের প্রিপ্রোসেসর ইউটিলিটিগুলি ব্যবহার করেন তবে আপনি ব্যবহার করে গণনাটি পেতে পারেন BOOST_PP_SEQ_SIZE(...)

উদাহরণস্বরূপ, কেউ CREATE_ENUMনিম্নলিখিত হিসাবে ম্যাক্রো সংজ্ঞায়িত করতে পারে :

#include <boost/preprocessor.hpp>

#define ENUM_PRIMITIVE_TYPE std::int32_t

#define CREATE_ENUM(EnumType, enumValSeq)                                  \
enum class EnumType : ENUM_PRIMITIVE_TYPE                                  \
{                                                                          \
   BOOST_PP_SEQ_ENUM(enumValSeq)                                           \
};                                                                         \
static constexpr ENUM_PRIMITIVE_TYPE EnumType##Count =                     \
                 BOOST_PP_SEQ_SIZE(enumValSeq);                            \
// END MACRO   

তারপরে, ম্যাক্রোকে কল করা:

CREATE_ENUM(Example, (A)(B)(C)(D)(E));

নিম্নলিখিত কোড উত্পন্ন করবে:

enum class Example : std::int32_t 
{
   A, B, C, D, E 
};
static constexpr std::int32_t ExampleCount = 5;

এটি কেবলমাত্র বুস্ট প্রিপ্রোসেসর সরঞ্জামগুলির সাথে পৃষ্ঠের উপর স্ক্র্যাচ করছে। উদাহরণস্বরূপ, আপনার ম্যাক্রো আপনার স্ট্রিং রূপান্তর ইউটিলিটি এবং অস্ট্রিম অপারেটরগুলিকে আপনার দৃ strongly়ভাবে টাইপ করা এনামের জন্য / থেকে সংজ্ঞা দিতে পারে।

এখানে প্রিপ্রসেসর সরঞ্জামগুলিতে বুস্ট করার জন্য আরও: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntr स्ताtoPreprocessorMetaprogramming.html


অন্যদিকে, আমি @ ফ্যান্টাস্টিক এমআরএফক্সের সাথে দৃ strongly়ভাবে একমত হয়েছি যে Countগ্রহণযোগ্য উত্তরে নিযুক্ত অতিরিক্ত গণ্য মান কোনও switchবিবৃতি ব্যবহার করা হলে সংকলক সতর্কতা মাথা ব্যথার সৃষ্টি করবে । unhandled caseনিরাপদ কোড রক্ষণাবেক্ষণের জন্য আমি সংকলক সতর্কতাটি বেশ কার্যকর বলে মনে করি, তাই আমি এটিকে হ্রাস করতে চাই না।


@ ফ্যান্টাস্টিক এমআরএফক্স গৃহীত উত্তর সম্পর্কে কোনও সমস্যা সম্পর্কিত নির্দেশ করার জন্য ধন্যবাদ। আমি এখানে একটি বিকল্প পদ্ধতির সরবরাহ করেছি যা আপনার সুপারিশের মনোভাবের সাথে সামঞ্জস্যপূর্ণ।
arr_sea

2

এটি স্টাডি :: ইনিশিয়ালাইজার_লিস্ট দিয়ে একটি কৌশল দ্বারা সমাধান করা যেতে পারে:

#define TypedEnum(Name, Type, ...)                                \
struct Name {                                                     \
    enum : Type{                                                  \
        __VA_ARGS__                                               \
    };                                                            \
    static inline const size_t count = []{                        \
        static Type __VA_ARGS__; return std::size({__VA_ARGS__}); \
    }();                                                          \
};

ব্যবহার:

#define Enum(Name, ...) TypedEnum(Name, int, _VA_ARGS_)
Enum(FakeEnum, A = 1, B = 0, C)

int main()
{
    std::cout << FakeEnum::A     << std::endl
              << FakeEnun::count << std::endl;
}

2

আরও একটি উপায় আছে যা লাইন গণনা বা টেম্পলেটগুলির উপর নির্ভর করে না। একমাত্র প্রয়োজনীয়তা হ'ল তাদের নিজস্ব ফাইলে এনাম মানগুলিকে স্টিক করা এবং প্রিপ্রোসেসর / সংকলককে এইভাবে গণনা করা:

my_enum_inc.h

ENUMVAL(BANANA)
ENUMVAL(ORANGE=10)
ENUMVAL(KIWI)
...
#undef ENUMVAL

my_enum.h

typedef enum {
  #define ENUMVAL(TYPE) TYPE,
  #include "my_enum_inc.h"
} Fruits;

#define ENUMVAL(TYPE) +1
const size_t num_fruits =
  #include "my_enum_inc.h"
  ;

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

আপনি যদি মন্তব্যগুলির বিষয়ে চিন্তা না করেন তবে আপনার অতিরিক্ত ফাইলের দরকার নেই এবং উপরে বর্ণিত কেউ হিসাবে এটি করতে পারেন, যেমন:

#define MY_ENUM_LIST \
    ENUMVAL(BANANA) \
    ENUMVAL(ORANGE = 7) \
    ENUMVAL(KIWI)

এবং #include "my_enum_inc.h"MY_ENUM_LIST এর সাথে নির্দেশাবলী প্রতিস্থাপন করুন তবে আপনাকে #undef ENUMVALপ্রতিটি ব্যবহারের পরে প্রয়োজন ।


1

এর অন্য ধরণের "বোকা" সমাধানটি হ'ল:

enum class Example { A, B, C, D, E };

constexpr int ExampleCount = [] {
  Example e{};
  int count = 0;
  switch (e) {
    case Example::A:
      count++;
    case Example::B:
      count++;
    case Example::C:
      count++;
    case Example::D:
      count++;
    case Example::E:
      count++;
  }

  return count;
}();

এটির সাথে সংকলন করে -Werror=switchআপনি কোনও স্যুইচ কেস বাদ দিলে বা নকল করে দিলে একটি সংকলক সতর্কতা পেতে নিশ্চিত করুন make এটি কনসেক্সট্রপও তাই সংকলনের সময়টিতে এটি গণনা করা হয়।

তবে মনে রাখবেন যে enum classএনামের জন্য এমনকি এনফলের প্রথম মান 0 হয় এমনকি এনামের প্রথম মান 0 না হলেও আপনাকে 0 থেকে শুরু করতে হবে বা স্পষ্টভাবে প্রথম মানটি ব্যবহার করতে হবে।


0

না, আপনাকে কোডটিতে লিখতে হবে।


0

static_cast<int>(Example::E) + 1অতিরিক্ত উপাদানগুলি কে সরিয়ে দেয় তাও আপনি বিবেচনা করতে পারেন ।


8
এই নির্দিষ্ট প্রোগ্রামিং সমস্যার জন্য এই উত্তরটি সঠিক, তবে সাধারণভাবে মার্জিত এবং ত্রুটির প্রবণতা থেকে দূরে। এনাম ভবিষ্যতে নতুন মানগুলির সাথে বাড়ানো যেতে পারে Example::Eযা এনামের সর্বশেষ মান হিসাবে প্রতিস্থাপন করতে পারে । এমনকি যদি এটি না হয় তবে Example::Eএর আক্ষরিক মান পরিবর্তন হতে পারে।
ম্যাথিয়া

0

প্রতিচ্ছবি টিএস: এনামগুলির স্থির প্রতিবিম্ব (এবং অন্যান্য ধরণের)

প্রতিফলন হিজড়া , বিশেষ করে [reflect.ops.enum] / 2 প্রতিফলন টিএস-এর সর্বশেষ সংস্করণ খসড়া প্রস্তাব get_enumerators TransformationTraitঅপারেশন:

[রিফ্লেক্ট.পস.এনম] / ২

template <Enum T> struct get_enumerators

সমস্ত বিশেষায়নের প্রয়োজনীয়তা get_enumerators<T>পূরণ করতে TransformationTraitহবে (20.10.1)। নেস্টেড টাইপ নামের typeএকটি মেটা-অবজেক্ট টাইপ সন্তোষজনক হিসাবে মনোনীত করে ObjectSequence, এমন উপাদান রয়েছে যা Enumeratorদ্বারা পরিগণিত ধরণের গণকেরকে সন্তুষ্ট করে এবং প্রতিফলিত করে T

খসড়াটির [রিফ্লেক.ওপস.ওবজেসেক] ObjectSequenceক্রিয়াকলাপকে কভার করে, যেখানে বিশেষত [রিফ্লেক্ট.ওপস.ওবজেসেক] / ১ get_sizeমেটা-অবজেক্ট সন্তুষ্ট করার জন্য উপাদানগুলির সংখ্যা বের করার বৈশিষ্ট্যকে অন্তর্ভুক্ত করে ObjectSequence:

[রিফ্লেক্ট.ওপস.ওবজেসেক] / ১

template <ObjectSequence T> struct get_size;

সমস্ত বিশেষায়নের প্রয়োজনীয়তাগুলি get_size<T>পূরণ করতে UnaryTypeTraitহবে (20.10.1) এর বেস বৈশিষ্ট্য সহ integral_constant<size_t, N>, যেখানে Nবস্তুর ক্রমের উপাদানগুলির সংখ্যা।

সুতরাং, প্রতিবিম্বে টিএসকে তার বর্তমান আকারে গ্রহণ এবং প্রয়োগ করতে হবে, একটি এনামের উপাদানগুলির সংখ্যার সংকলন সময়ে নিম্নরূপে গণনা করা যেতে পারে:

enum class Example { A, B, C, D, E };

using ExampleEnumerators = get_enumerators<Example>::type;

static_assert(get_size<ExampleEnumerators>::value == 5U, "");

যেখানে আমরা সম্ভবত ওরফে টেম্পলেটগুলি দেখতে পাচ্ছি get_enumerators_vএবং get_type_vপ্রতিবিম্বটি আরও সরল করতে:

enum class Example { A, B, C, D, E };

using ExampleEnumerators = get_enumerators_t<Example>;

static_assert(get_size_v<ExampleEnumerators> == 5U, "");

প্রতিবিম্ব টিএস-এর স্থিতি

হার্ব সাটারের ট্রিপ প্রতিবেদনে বলা হয়েছে: গ্রীষ্ম আইএসও সি ++ স্ট্যান্ডার্ড মিটিং (র‌্যাপারসিল) জুন 9, 2018 থেকে আইএসও সি ++ কমিটির গ্রীষ্ম সভা, রিফ্লেকশন টিএসকে বৈশিষ্ট্য-সম্পূর্ণ হিসাবে ঘোষণা করা হয়েছে

প্রতিচ্ছবি টিএস বৈশিষ্ট্য-সম্পূর্ণ : প্রতিবিম্ব টিএসটি বৈশিষ্ট্য-সম্পূর্ণ হিসাবে ঘোষিত হয়েছিল এবং গ্রীষ্মে এটির মূল মন্তব্য ব্যালটের জন্য পাঠানো হচ্ছে। আবার লক্ষ করুন যে টিএসের বর্তমান টেম্পলেট মেটাপোগ্র্যামিং-ভিত্তিক বাক্য গঠন কেবল একটি স্থানধারক; অনুরোধ করা হচ্ছে প্রতিক্রিয়াটি হ'ল ডিজাইনের মূল "সাহস", এবং কমিটি ইতিমধ্যে জানে যে এটি একটি সরল প্রোগ্রামিং মডেলের সাথে পৃষ্ঠতল বাক্য গঠনটি প্রতিস্থাপন করতে চায় যা সাধারণ সংকলন-সময় কোড ব্যবহার করে এবং না<> স্টাইল রূপক ব্যবহার করে ।

এবং প্রাথমিকভাবে সি ++ ২০ এর জন্য প্ল্যানড করা হয়েছিল , তবে এটি প্রতিফলিত টিএস-তে এখনও এটি C ++ 20 রিলিজ করার সুযোগ পাবে কিনা তা কিছুটা অস্পষ্ট।

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