Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enum like calculated constants

Actually this "problem" feels extremely simple. While doing some calculated icon offsets, I came up with the following approach:

namespace Icons {

  struct IconSet {
    constexpr IconSet(size_t base_offset) noexcept
      : base_offset_(base_offset), icon(base_offset * 3), iconSmall(icon + 1), iconBig(icon + 2) {
    }
    size_t icon;
    size_t iconSmall;
    size_t iconBig;

    size_t base_offset_;

    constexpr size_t next() const {
      return base_offset_ + 1;
    }
  };


  static constexpr IconSet flower = IconSet(0);
  static constexpr IconSet tree = IconSet(flower.next());
  static constexpr IconSet forest = IconSet(tree.next());
  static constexpr IconSet mountain = IconSet(forest.next());

 }

Now one may write Icons::tree.iconBig for example to get that icon's calculated offset. Basically the designer can change the icons - sometimes adding/removing as well - but always has to provide the entire set (normal, small and big) by convention.

As you see, the issue with this approach is that I had to do that next() function and use it repeatedly - a normal enum wouldn't have this downside.

I know of BOOST_PP and other macro tricks, but I was hoping for something without macro's - since I have a feeling it is not needed and I then sort of would prefer what I already have with the plain next() function. Another solution would of course just be a normal enum and a calculation function, but that is defeating the purpose of laying it out precalculated.

Hence, I am looking for a simple and portable solution that will give that enum-like functionality. It doesn't have to be compile time or constexpr if for example just inline will make it easier.

like image 441
darune Avatar asked Oct 31 '18 09:10

darune


People also ask

Are constants allowed in enum?

An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it.

Is it better to use enum or constant?

Enums are lists of constants. When you need a predefined list of values which do represent some kind of numeric or textual data, you should use an enum. You should always use enums when a variable (especially a method parameter) can only take one out of a small set of possible values.

What is the difference between enum and string?

If your set of parameters is limited and known at compile time, use enum . If your set of parameters is open and unkown at compile time, use strings.


1 Answers

Here is an approach you can use, based on a fold expression over a compile time integer sequence to instantiate the icons by index. The structured binding gets you individually named non-static, non-constexpr variables.

The anonymous namespace inside Icons makes those definitions visible in this translation unit only, which you may or may not want.

Compiler explorer link so you can explore the code options yourself.

#include <cstddef>
#include <array>
#include <utility>

namespace Icons {

struct IconSet {
    constexpr IconSet(size_t base_offset) noexcept 
      : base_offset_(base_offset), icon(base_offset * 3), iconSmall(icon + 1), iconBig(icon + 2) {
    }
    size_t icon;
    size_t iconSmall;
    size_t iconBig;

    size_t base_offset_;
};

template <std::size_t... Ints>
constexpr auto make_icons_helper(std::index_sequence<Ints...>) -> std::array<IconSet, sizeof...(Ints)> 
{
    return {IconSet(Ints)...};
}

template <size_t N>
constexpr auto make_icons()
{
    return make_icons_helper(std::make_index_sequence<N>{});
}

namespace {
    auto [flower, tree, forest, mountain] = make_icons<4>();
}

}

int main()
{
    return Icons::forest.iconSmall;
}
like image 98
Bruce Collie Avatar answered Oct 17 '22 02:10

Bruce Collie