如何遍历枚举以求和具有相同名称的类的大小

问题描述 投票:0回答:4

我有一个枚举,变量名称为 v1、v2...、vn,每个名称都有一个对应的同名类。我想在编译时(尽管我也不知道在运行时是否可行)找到这些类的 sizeof 的总和,如下例所示。你可以使用模板或预处理器来做到这一点吗?

enum class Enum
{
    First,
    A = First,
    B,
    C,
    Last
};
class A
{
    // ...
};
class B
{
    // ...
};
class C
{
    // ...
};
constexpr size_t GetTotSumSizeOf()
{
    // iterate from Enum::First to Enum::Last, summing sizeof the corresponding class
}
static constexpr size_t TotSum = GetTotSumSizeOf();
c++ templates preprocessor
4个回答
3
投票

c++ 不允许您直接迭代枚举类。因此,您可以通过创建一个辅助结构来实现所需的结果,该结构将每个枚举值映射到其对应的类,然后使用模板递归计算总和。 为每个枚举值创建 EnumToClass 结构的模板特化。并使用 GetSizeOfHelper 模板函数递归计算相应类的大小之和。最后使用 GetToSumSizeof 函数调用递归函数并将其包装。

#include <type_traits>
#include <iostream>

enum class Enum
{
    First,
    A = First,
    B,
    C,
    Last
};

class A
{
    // ...
};

class B
{
    // ...
};

class C
{
    // ...
};

template<Enum E>
struct EnumToClass;

template<>
struct EnumToClass<Enum::A>
{
    using Type = A;
};

template<>
struct EnumToClass<Enum::B>
{
    using Type = B;
};

template<>
struct EnumToClass<Enum::C>
{
    using Type = C;
};

template<Enum E>
constexpr size_t GetSizeOfHelper()
{
    if constexpr (E == Enum::Last)
    {
        return 0;
    }
    else
    {
        using ClassType = typename EnumToClass<E>::Type;
        return sizeof(ClassType) + GetSizeOfHelper<static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(E) + 1)>();
    }
}

constexpr size_t GetTotSumSizeOf()
{
    return GetSizeOfHelper<Enum::First>();
}

static constexpr size_t TotSum = GetTotSumSizeOf();

int main()
{
    // Use TotalSum
    char buffer[TotSum];

   // Print the value of TotSum
   std::cout << "Total size of the classes: " << TotSum << std::endl;

   // Example usage of buffer
   A* a_ptr = reinterpret_cast<A*>(&buffer[0]);
   B* b_ptr = reinterpret_cast<B*>(&buffer[sizeof(A)]);
   C* c_ptr = reinterpret_cast<C*>(&buffer[sizeof(A) + sizeof(B)]);
    
}

2
投票

您可以编写一个 constexpr 例程,通过它的 Enum 标识符返回类的信息,然后在您的 constexpr 中汇总该信息

GetTotSumSizeOf
.

Enum 标识符和类名的关联是手动的。语言中没有任何东西将它们联系在一起或强制它们是正确的。

#include <cstddef>
#include <iostream>
#include <type_traits>

using std::cout;
using std::size_t;
using std::underlying_type_t;

enum class Enum
{
    First,
    A = First,
    B,  
    C,  
    Last
};
constexpr
auto operator++(Enum& e) -> Enum& {
    using UEnum = underlying_type_t<Enum>;
    auto ue = static_cast<UEnum>(e);
    e = static_cast<Enum>(ue + 1); 
    return e;
}

class A
{
    // ...
};
class B
{
    // ...
};
class C
{
    // ...
};

constexpr auto SizeBy(Enum e) -> size_t {
    if (e == Enum::A) return sizeof(A);
    if (e == Enum::B) return sizeof(B);
    if (e == Enum::C) return sizeof(C);
    return 0;
}

constexpr size_t GetTotSumSizeOf() {
    size_t result{};

    for (Enum i = Enum::First; i != Enum::Last; ++i) {
        result += SizeBy(i);
    }   

    return result;
}
static constexpr size_t TotSum = GetTotSumSizeOf();

int main() {
    cout << TotSum << "\n";
}

1
投票

根据您的评论假设不需要实际的

enum
,您可以通过类型系统列出您的类并在其之上构建标识符生成:

#include <iostream>
#include <type_traits>
#include <cstddef>

template <class...> struct typelist {};

template <class... Classes>
constexpr auto total_sizeof(typelist<Classes...>) {
    return (sizeof(Classes) + ...);
}

template <class Class, class... Classes>
constexpr auto class_id(typelist<Classes...>) {
    return []<std::size_t... Idx>(std::index_sequence<Idx...>) {
        return ((std::is_same_v<Class, Classes> * Idx) + ...);
    }(std::index_sequence_for<Classes...>{});
}

////////////////////////////////////////////////////////////////

constexpr typelist<struct A, struct B, struct C> all_classes;

struct A { char a[1]; };
struct B { char b[2]; };
struct C { char c[3]; };

int main() {
    static_assert(class_id<A>(all_classes) == 0);
    static_assert(class_id<B>(all_classes) == 1);
    static_assert(class_id<C>(all_classes) == 2);
    static_assert(total_sizeof(all_classes) == sizeof(A) + sizeof(B) + sizeof(C));
}

在 godbolt.org 上观看直播


0
投票

如评论中所述,一种选择是使用外部脚本生成枚举类和 total_size 变量,但这不是很容易维护。即使您自动将脚本配置为在每次编译之前运行,它也会增加编译时间,而且 IDE 在开发过程中会因未定义的符号而变得疯狂。所以我觉得最好手动指定。

我想出的一个更简单的构造使用单个 static_assert 来确保程序员记得指定与枚举值相同数量的类。

constexpr size_t GetTotSumSizeOf()
{
    constexpr size_t LENS[]
    {
        sizeof(A),
        sizeof(B),
        sizeof(C),
    };
    static_assert(sizeof(LENS) / sizeof(*LENS) == (size_t)Enum::Last);
    return std::accumulate(LENS, LENS + (size_t)Enum::Last, size_t(0));
}
"

但是,这种方法对于枚举变量名称的意外更改和忘记更新相应的 sizeof() 是不安全的,这就是为什么我接受了 곽지훈 的答案,因为我认为它是最可靠的。

\n "
© www.soinside.com 2019 - 2024. All rights reserved.