编译时间联合数组

Compile Time Union Array

我正在尝试跟踪连续内存块中不同大小的段。段的大小和顺序在编译时是已知的,所以我也想在编译时计算它们在块内的偏移量。我希望能够遍历这些段的大小和偏移量,但我也希望能够在需要时按名称调用特定的段。如果在整个项目建设过程中段的大小、顺序或数量发生变化,我想尽量减少冗余,这样 adjust/reorganize.

的部分就更少了

举个例子,我有一个内存块最多可以包含 12 个对象实例(都是同一类型)。为了这个例子,假设它有 12 个三角形实例,每个三角形实例由 3 个 2D 坐标组成(总共 6 个浮点数)。在这种情况下,每个线段将代表一些几何体,第一线段可以只是一个三角形(实例计数 1),第二线段是一个多面体(保留 10 个三角形实例),第三线段是一个正方形(实例计数 2)。几何将按照它们在内存块中的布局顺序绘制(first/triangle、second/polyhedron 和 third/square 段的顺序)。第一段(三角形)的偏移量将为 0。第二段(多面体)的偏移量将为 1(0 + 三角形的大小)。第三段正方形的偏移量为 11(0 + 三角形的大小 + 多面体的大小)。现在,如果我想绘制所有几何图形,那么能够遍历一个大小数组和一个偏移数组以告诉 gpu 几何图形从何处开始(偏移)以及它持续多长时间(大小)会很好。另一方面,如果我想将多面体几何体向左移动,那么如果我决定更改顺序(例如 make多面体是要绘制的第一个几何体)我可以重新排列阵容但不必记住将对多面体的所有调用更改为新索引的偏移量和大小。

我最初的想法是用一个枚举来表示线段的顺序(三角形,然后是多面体,然后是正方形)。然后我可以在一个结构和一个整数数组之间建立联合,该结构具有表示我可以按名称调用的各个段大小的命名整数变量和一个我可以迭代的整数数组。结果如下:

struct Memory {
    struct Enum {
        enum {triangleSegment, polyhedronSegment, squareSegment, count};
    };
    union SegmentSizes{

        struct Names {
            constexpr Names() = default;
            const uint8_t triangleSegment = 1, polyhedronSegment = 10, squareSegment = 2;
        };

        Names names;
        std::array<uint8_t, Enum::count> arr;
    };

    static constexpr SegmentSizes sizes{ SegmentSizes::Names() };

};

int main() {
    std::cout << std::to_string(Memory::sizes.names.triangleSegment) << std::endl;
    std::cout << std::to_string(Memory::sizes.arr[1]) << std::endl;
}

以上工作...取决于编译器。 MSVC 编译得很好,但 gcc 似乎不喜欢 static constexpr SegmentSizes sizes{ SegmentSizes::Names() }; 行。

我在编译时计算偏移量的想法是使用 constexpr 函数构造一个偏移量数组,然后用结果填充另一个静态 constexpr 联合:

//Declared within Memory, below static constexpr SegmentSizes sizes
struct SegmentOffsets {
    static constexpr std::array<uint16_t, Enum::count> arr() {
        std::array<uint16_t, Enum::count> a{};
        uint16_t currOffset = 0;
        for (uint8_t i = 0; i < Enum::count; ++i) {
            a[i] = currOffset;
            currOffset += sizes.arr[i];
        }
        return a;
    }

    struct Names {
        const uint16_t triangleSegment, polyhedronSegment, squareSegment;
    };

    static constexpr Names names{ *reinterpret_cast<Names*>(arr().data()) };
};



但出于某种原因,我不明白编译器声称 arr() 函数不是编译时常量。

除了尝试进一步微调我对 constexpr 和编译时间常量的理解之外,我还想知道是否有一般更简单的方法来实现相同的目标。我愿意接受所有建议。

为了完整起见,下面是完整的代码片段:

#include <array>
#include <iostream>
#include <string>


struct Memory {
    struct Enum {
        enum {triangleSegment, polyhedronSegment, squareSegment, count};
    };
    union SegmentSizes{

        struct Names {
            constexpr Names() = default;
            const uint8_t triangleSegment = 1, polyhedronSegment = 10, squareSegment = 2;
        };

        Names names;
        std::array<uint8_t, Enum::count> arr;
    };

    static constexpr SegmentSizes sizes{ SegmentSizes::Names() };

    struct SegmentOffsets {
        static constexpr std::array<uint16_t, Enum::count> arr() {
            std::array<uint16_t, Enum::count> a{};
            uint16_t currOffset = 0;
            for (uint8_t i = 0; i < Enum::count; ++i) {
                a[i] = currOffset;
                currOffset += sizes.arr[i];
            }
            return a;
        }

        struct Names {
            const uint16_t triangleSegment, polyhedronSegment, squareSegment;
        };

        static constexpr Names names{ *reinterpret_cast<Names*>(arr().data()) };
    };
};


int main() {
    std::cout << std::to_string(Memory::sizes.names.triangleSegment) << std::endl;
    std::cout << std::to_string(Memory::sizes.arr[1]) << std::endl;
}

arr() function is not a compile time constant.

只能在联合的活动字段上读取联合,否则就是未定义行为。 arr()一直都是坏的,只是在编译的时候被执行了。

否则,通过将命名访问替换为简单的函数调用,可以使整个方法变得更加简单:

#include <array>
#include <iostream>
#include <string>

template<std::size_t N>
constexpr std::array<uint16_t, N> calc_offsets(const std::array<uint8_t, N>& sizes) {
    std::array<uint16_t, N> result;
    uint16_t accum = 0;
    std::size_t i = 0;
    for(const auto & size : sizes) {
      result[i++] = accum;
      accum += size;
    }
    return result;
}

struct Memory {
    enum Segment {firstGroup, secondGroup, thirdGroup, segment_count};

    static constexpr std::array<uint8_t, (std::size_t)segment_count> segment_sizes = {1, 10 , 1};
    static constexpr std::array<uint16_t, (std::size_t)segment_count> segment_offsets = calc_offsets(segment_sizes);

    static constexpr uint8_t size(Segment s) {return segment_sizes[(std::size_t)s];}
    static constexpr uint16_t offset(Segment s) {return segment_offsets[(std::size_t)s];}
};


int main() {
    std::cout << std::to_string(Memory::size(Memory::firstGroup)) << std::endl;
    std::cout << std::to_string(Memory::segment_offsets[1]) << std::endl;
}