枚举类计算常量
Enum like calculated constants
其实这个"problem"感觉非常简单。在计算图标偏移量时,我想到了以下方法:
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());
}
例如,现在可以写 Icons::tree.iconBig
来获取该图标的计算偏移量。基本上,设计师可以更改图标 - 有时 adding/removing 以及 - 但总是必须按照惯例提供整套图标(正常、小和大)。
如您所见,这种方法的问题是我必须执行那个 next()
函数并重复使用它——普通的枚举不会有这个缺点。
我知道 BOOST_PP 和其他宏技巧,但我希望没有宏的东西 - 因为我觉得不需要它,然后我更喜欢我已经拥有的普通东西next()
函数。
另一种解决方案当然只是一个普通的枚举和一个计算函数,但这违背了预先计算布局的目的。
因此,我正在寻找一种简单且可移植的解决方案,以提供类似枚举的功能。它不一定是编译时或 constexpr
如果例如只是内联会使它更容易。
一个简单的 non-constexpr 解决方案,使用静态计数器并依赖于在单个 TU 中执行静态初始化 top-to-bottom 的事实:
namespace Icons {
namespace detail_iconSet {
static std::size_t current_base_offset = 0;
}
struct IconSet {
IconSet() noexcept
: base_offset_(detail_iconSet::current_base_offset++)
, icon(base_offset_ * 3)
, iconSmall(icon + 1)
, iconBig(icon + 2) { }
std::size_t base_offset_;
std::size_t icon;
std::size_t iconSmall;
std::size_t iconBig;
};
static IconSet flower;
static IconSet tree;
static IconSet forest;
static IconSet mountain;
}
要注意的是,如果您有多个 headers 包含 IconSet
定义(即它们的编号会根据包含的顺序而改变),这将表现得很奇怪,但我不认为有一种方法可以避免这种情况。
您可以使用以下方法,它基于编译时整数序列上的折叠表达式来按索引实例化图标。结构化绑定让您单独命名为非静态、非 constexpr 变量。
Icons
中的匿名命名空间使这些定义仅在此翻译单元中可见,您可能想要也可能不想要。
Compiler explorer link 这样您就可以自己探索代码选项了。
#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;
}
其实这个"problem"感觉非常简单。在计算图标偏移量时,我想到了以下方法:
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());
}
例如,现在可以写 Icons::tree.iconBig
来获取该图标的计算偏移量。基本上,设计师可以更改图标 - 有时 adding/removing 以及 - 但总是必须按照惯例提供整套图标(正常、小和大)。
如您所见,这种方法的问题是我必须执行那个 next()
函数并重复使用它——普通的枚举不会有这个缺点。
我知道 BOOST_PP 和其他宏技巧,但我希望没有宏的东西 - 因为我觉得不需要它,然后我更喜欢我已经拥有的普通东西next()
函数。
另一种解决方案当然只是一个普通的枚举和一个计算函数,但这违背了预先计算布局的目的。
因此,我正在寻找一种简单且可移植的解决方案,以提供类似枚举的功能。它不一定是编译时或 constexpr
如果例如只是内联会使它更容易。
一个简单的 non-constexpr 解决方案,使用静态计数器并依赖于在单个 TU 中执行静态初始化 top-to-bottom 的事实:
namespace Icons {
namespace detail_iconSet {
static std::size_t current_base_offset = 0;
}
struct IconSet {
IconSet() noexcept
: base_offset_(detail_iconSet::current_base_offset++)
, icon(base_offset_ * 3)
, iconSmall(icon + 1)
, iconBig(icon + 2) { }
std::size_t base_offset_;
std::size_t icon;
std::size_t iconSmall;
std::size_t iconBig;
};
static IconSet flower;
static IconSet tree;
static IconSet forest;
static IconSet mountain;
}
要注意的是,如果您有多个 headers 包含 IconSet
定义(即它们的编号会根据包含的顺序而改变),这将表现得很奇怪,但我不认为有一种方法可以避免这种情况。
您可以使用以下方法,它基于编译时整数序列上的折叠表达式来按索引实例化图标。结构化绑定让您单独命名为非静态、非 constexpr 变量。
Icons
中的匿名命名空间使这些定义仅在此翻译单元中可见,您可能想要也可能不想要。
Compiler explorer link 这样您就可以自己探索代码选项了。
#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;
}