c++ 如何循环访问枚举,对具有相同名称的类的sizeof求和

voase2hg  于 2023-03-20  发布在  其他
关注(0)|答案(4)|浏览(148)

我有一个枚举变量名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();
k5ifujac

k5ifujac1#

c++不允许你直接迭代枚举类。所以你可以通过创建一个helper结构来获得想要的结果,helper结构将每个枚举值Map到它相应的类,然后使用模板递归地计算总和。为每个枚举值创建一个EnumToClass结构的模板特化。然后使用GetSizeOfHelper模板函数递归地计算相应类的大小总和。最后使用GetToSumSizeof函数调用递归函数并将其 Package 。

#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)]);
    
}
sd2nnvve

sd2nnvve2#

您可以编写一个constexpr例程,通过类的Enum标识符返回类的信息,然后在constexpr GetTotSumSizeOf中汇总这些信息。
枚举标识符和类名的关联是手动的。语言中没有任何东西将它们联系在一起或强制它们是正确的。

#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";
}
66bbxpm5

66bbxpm53#

从注解中假设不需要实际的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));
}

See it live on godbolt.org

yiytaume

yiytaume4#

正如在注解中提到的,一种选择是使用外部脚本来生成枚举类和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()并不安全,这就是为什么我接受的答案,因为我认为它是最可靠的。

相关问题