Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
247 views
in Technique[技术] by (71.8m points)

enum class constructor c++ , how to pass specific value?

I came from Java and here we have such option as set value to constuctor.

Example

enum TYPE
{
    AUTO("BMW"),
    MOTOCYCLE("Kawasaki");

    private String mBrandName = "";

    TYPE(final String iBrandName)
    {
        mBrandName = iBrandName;
    }

    public String getBrandName()
    {
        return mBrandName;
    }

    static TYPE getMotocycle()
    {
        return MOTOCYCLE;
    }

    static TYPE getAuto()
    {
        return AUTO;
    }
}

Usage

String motoBrand = TYPE.getMotocycle().getBrandName(); // == BMW
String autoBrand = TYPE.getAuto().getBrandName(); // == Kawasaki

So, idea is that you can give to constructor specific value (int, String whatever) and then get it. So, you have order number as well as specific value that you set to...

Question is, from documentation https://docs.microsoft.com/en-us/cpp/cpp/enumerations-cpp?view=vs-2019 I understood that there is no such option in cpp, is it right?

P.S. Reason why I need enum, because you save all enum functionality(like count of elements or get element by number) and additionally you get a little bit more with constructor.

In Java I can get count of elements this way TYPE.values().length https://stackoverflow.com/a/17492102/5709159

In Java I can get element by number this way TYPE.values()[ordinal] https://stackoverflow.com/a/609866/5709159

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

C++ ain't Java! Every language has its own techniques which are a good fit with the language. Don't try to mimic a perfectly fine construct of one language in the same (but broken) construct in a different language.

Here is how I would solve your issue in C++:

 // Define the actual enumeration
 enum class [[nodiscard]] Vehicle : unsigned char
 {
     CAR,
     MOTORCYCLE,
     SIZE [[maybe_unused]]
 };

// Convert your enumeration to a string (view)
#include <cassert>
#include <string_view>
[[nodiscard]] constexpr auto to_string(Vehicle v) noexcept -> std::string_view {
  assert(v != Vehicle::SIZE);
  switch (v) {
    case Vehicle::CAR:
      return "Car";
    case Vehicle::MOTORCYCLE:
      return "Motorcycle";
  }
}

To use it, you can do something like:

 for (unsigned char c = 0; c < static_cast<unsigned char>(Vehicle::SIZE); ++c)
        std::cout << to_string(static_cast<Vehicle>(c)) << std::endl;

Writing this every time is a bit cumbersome, however, you could write your own template class that helps with iterating over it. For example:

#include <type_traits>
// The generic stuff you only write once
// Assumes you don't assign any values to your enumeration by hand + it ends on
// 'SIZE' (unless a second argument was passed if a different name was used)
template <typename TEnumeration, TEnumeration TSize = TEnumeration::SIZE>
class [[nodiscard]] EnumRange final {
  using type = std::underlying_type_t<TEnumeration>;

 public:
  // The iterator that can be used to loop through all values
  //
  class [[nodiscard]] Iterator final {
    TEnumeration value{static_cast<TEnumeration>(0)};

   public:
    constexpr Iterator() noexcept = default;
    constexpr Iterator(TEnumeration e) noexcept : value{e} {}

    constexpr auto operator*() const noexcept -> TEnumeration { return value; }
    constexpr auto operator-> () const & noexcept -> const TEnumeration* {
      return &value;
    }
    constexpr auto operator++() & noexcept -> Iterator {
      value = static_cast<TEnumeration>(1 + static_cast<type>(value));
      return *this;
    }

    [[nodiscard]] constexpr auto operator==(Iterator i) -> bool { return i.value == value; }
    [[nodiscard]] constexpr auto operator!=(Iterator i) -> bool { return i.value != value; }
  };

  constexpr auto begin() const noexcept -> Iterator { return Iterator{}; }
  constexpr auto cbegin() const noexcept -> Iterator { return Iterator{}; }

  constexpr auto end() const noexcept -> Iterator { return Iterator{TSize}; }
  constexpr auto cend() const noexcept -> Iterator { return Iterator{TSize}; }

  [[nodiscard]] constexpr auto size() const noexcept -> type {
    return static_cast<type>(TSize);
  }
};

The usage:

#include <iostream>
int main(int, char**) {
  auto range = EnumRange<Vehicle>{};
  std::cout << static_cast<int>(range.size()) << std::endl;
  for (auto v : range) std::cout << to_string(v) << std::endl;
}

As you saw in the first test code, you can go from a numeric value to an enumeration by using static_cast. However, it assumes that you have some value that is valid for the enumeration. With the same assumptions of the range, we can write our own checked variant:

#include <stdexcept>
#include <type_traits>

template <typename TEnumeration, TEnumeration TSize = TEnumeration::SIZE>
[[nodiscard]] constexpr auto checked_enum_cast(
    std::underlying_type_t<TEnumeration> numeric_value) noexcept(false)
    -> TEnumeration {
        using type = std::underlying_type_t<TEnumeration>;
  if constexpr (std::is_signed_v<type>)
    if (numeric_value < 0) throw std::out_of_range{"Negative value"};

  if (numeric_value >= static_cast<type>(TSize)) throw std::out_of_range{"Value too large"};

  return static_cast<TEnumeration>(numeric_value);
}

To use this, you can write:

  try {
    std::cout << to_string(checked_enum_cast<Vehicle>(1)) << std::endl;
    std::cout << to_string(checked_enum_cast<Vehicle>(2)) << std::endl;
  } catch (const std::out_of_range& e) {
    std::cout << e.what() << std::endl;
  }

Note: If one would live in an exception-free world, one could return std::nullopt and change the return type to std::optional<TEnumeration> instead.

All code combined + execution at Compiler Explorer

Please note that the iterator can be refined, however, I ain't an expert in the details. (and for looping, it doesn't matter, if you ever want to use it for an algorithm it could)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...