Skip to content

Commit

Permalink
AMREX_ENUM: Add more capabilites (#4143)
Browse files Browse the repository at this point in the history
Add support for `=`. For example,

```c++
    AMREX_ENUM(MyColor,
               red,
               chi=red,
               green,
               blue,
               Default = green);
```
The underlying values are then
```c++
    { red = 0,
      chi = 0,
      green = 1,
      blue = 2,
      Default = 1}
```
We can then have
```c++
    assert(amrex::getEnum<MyColor2>("red") == MyColor2::red);
    assert(amrex::getEnum<MyColor2>("chi") == MyColor2::chi);
    assert(amrex::getEnum<MyColor2>("green") == MyColor2::green);
    assert(amrex::getEnum<MyColor2>("blue") == MyColor2::blue);
    assert(amrex::getEnum<MyColor2>("Default") == MyColor2::Default);
```
Because of there are duplicated values, using value to get name string
will return the first matched string. Currently, I don't see a way to
fix it. But, since `MyColor::red == MyColor::chi` is true, it's also not
outrageously wrong.
```c++
    assert(amrex::getEnumNameString(MyColor2::red) == "red");
    assert(amrex::getEnumNameString(MyColor2::chi) == "red");
    assert(amrex::getEnumNameString(MyColor2::green) == "green");
    assert(amrex::getEnumNameString(MyColor2::blue) == "blue");
    assert(amrex::getEnumNameString(MyColor2::Default) == "green");
```
P.S. Chi means red in Chinese. https://en.wikipedia.org/wiki/Radical_155
  • Loading branch information
WeiqunZhang authored Sep 11, 2024
1 parent f547cb5 commit d572928
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 21 deletions.
84 changes: 63 additions & 21 deletions Src/Base/AMReX_Enum.H
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,91 @@
template <typename T>
using amrex_enum_traits = decltype(amrex_get_enum_traits(std::declval<T>()));

namespace amrex {
namespace amrex
{
template <typename T, typename ET = amrex_enum_traits<T>,
std::enable_if_t<ET::value,int> = 0>
std::vector<std::pair<std::string,T>> getEnumNameValuePairs ()
{
auto tmp = amrex::split(std::string(ET::enum_names), ",");
std::vector<std::pair<std::string,T>> r;
r.reserve(tmp.size());
int next_value = 0;
for (auto const& item : tmp) {
auto const& kv = amrex::split(item, "=");
if (kv.size() == 1) {
r.emplace_back(amrex::trim(kv[0]), static_cast<T>(next_value));
++next_value;
} else if (kv.size() == 2) {
auto const& value_string = amrex::trim(kv[1]);
auto it = std::find_if(r.begin(), r.end(),
[&] (auto const& x) -> bool
{ return x.first == value_string; });
auto this_value = it->second;
r.emplace_back(amrex::trim(kv[0]), this_value);
next_value = static_cast<int>(this_value) + 1;
} else {
std::string error_msg("amrex::getEnumNameIntPairs: AMREX_ENUM(");
error_msg.append(ET::class_name).append(", ").append(ET::enum_names)
.append(").");
throw std::runtime_error(error_msg);
}
}
return r;
}

template <typename T, typename ET = amrex_enum_traits<T>,
std::enable_if_t<ET::value,int> = 0>
T getEnum (std::string_view const& s)
{
auto pos = ET::enum_names.find(s);
if (pos == std::string_view::npos) {
auto const& kv = getEnumNameValuePairs<T>();
auto it = std::find_if(kv.begin(), kv.end(),
[&] (auto const& x) -> bool
{ return x.first == s; });
if (it != kv.end()) {
return it->second;
} else {
std::string error_msg("amrex::getEnum: Unknown enum: ");
error_msg.append(s).append(" in AMREX_ENUM(").append(ET::class_name)
.append(", ").append(ET::enum_names).append(").");
throw std::runtime_error(error_msg);
return T();
}
auto count = std::count(ET::enum_names.begin(),
ET::enum_names.begin()+pos, ',');
return static_cast<T>(count);
}

template <typename T, typename ET = amrex_enum_traits<T>,
std::enable_if_t<ET::value,int> = 0>
std::string getEnumNameString (T const& v)
{
auto n = static_cast<int>(v);
std::size_t pos = 0;
for (int i = 0; i < n; ++i) {
pos = ET::enum_names.find(',', pos);
if (pos == std::string::npos) {
std::string error_msg("amrex::getEnum: Unknown enum value: ");
error_msg.append(std::to_string(n)).append(" in AMREX_ENUM(")
.append(ET::class_name).append(", ").append(ET::enum_names)
.append(").");
throw std::runtime_error(error_msg);
}
++pos;
auto const& kv = getEnumNameValuePairs<T>();
auto it = std::find_if(kv.begin(), kv.end(),
[&] (auto const& x) -> bool
{ return x.second == v; });
if (it != kv.end()) {
return it->first;
} else {
std::string error_msg("amrex::getEnum: Unknown enum value: ");
error_msg.append(std::to_string(static_cast<int>(v)))
.append(" in AMREX_ENUM(").append(ET::class_name).append(", ")
.append(ET::enum_names).append(").");
throw std::runtime_error(error_msg);
return std::string();
}
auto pos2 = ET::enum_names.find(',', pos);
return amrex::trim(std::string(ET::enum_names.substr(pos,pos2-pos)));
}

template <typename T, typename ET = amrex_enum_traits<T>,
std::enable_if_t<ET::value,int> = 0>
std::vector<std::string> getEnumNameStrings ()
{
return amrex::split(std::string(ET::enum_names), ", ");
auto r = amrex::split(std::string(ET::enum_names), ",");
for (auto& s : r) {
auto found = s.find('=');
if (found != std::string::npos) {
s.erase(found);
}
s = amrex::trim(s);
}
return r;
}

template <typename T, typename ET = amrex_enum_traits<T>,
Expand Down
36 changes: 36 additions & 0 deletions Tests/Enum/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ namespace my_namespace {
AMREX_ENUM(MyColor, orange, yellow,cyan );
}

AMREX_ENUM(MyColor2,
red, // 0
chi=red, // 0
green, // 1
blue, // 2
Default = green); // 1

int main (int argc, char* argv[])
{
amrex::Initialize(argc, argv);
Expand Down Expand Up @@ -94,6 +101,35 @@ int main (int argc, char* argv[])
amrex::Print() << "\n";
}
}
{
auto names2 = amrex::getEnumNameStrings<MyColor2>();
amrex::Print() << "Names in " << amrex::getEnumClassName<MyColor2>() << "\n";
for (auto const& name : names2) {
amrex::Print() << " " << name << "\n";
}

auto const& kv = amrex::getEnumNameValuePairs<MyColor2>();
amrex::Print() << "Name : Value\n";
for (auto const& item : kv) {
amrex::Print() << " " << item.first << ": "
<< static_cast<int>(item.second) << "\n";
}

AMREX_ALWAYS_ASSERT(amrex::getEnumNameString(MyColor2::red) == "red");
AMREX_ALWAYS_ASSERT(amrex::getEnumNameString(MyColor2::chi) == "red");
AMREX_ALWAYS_ASSERT(amrex::getEnumNameString(MyColor2::green) == "green");
AMREX_ALWAYS_ASSERT(amrex::getEnumNameString(MyColor2::blue) == "blue");
AMREX_ALWAYS_ASSERT(amrex::getEnumNameString(MyColor2::Default) == "green");

static_assert(MyColor2::red == MyColor2::chi);
static_assert(MyColor2::green == MyColor2::Default);

AMREX_ALWAYS_ASSERT(amrex::getEnum<MyColor2>("red") == MyColor2::red);
AMREX_ALWAYS_ASSERT(amrex::getEnum<MyColor2>("chi") == MyColor2::chi);
AMREX_ALWAYS_ASSERT(amrex::getEnum<MyColor2>("green") == MyColor2::green);
AMREX_ALWAYS_ASSERT(amrex::getEnum<MyColor2>("blue") == MyColor2::blue);
AMREX_ALWAYS_ASSERT(amrex::getEnum<MyColor2>("Default") == MyColor2::Default);
}

amrex::Finalize();
}

0 comments on commit d572928

Please sign in to comment.