使用可变参数模板(或其他解决方案)有效地对数据施加结构

时间:2014-03-23 21:58:32

标签: c++ c++11

在我的项目中,我使用浮点数来处理大量的三维数学(因此方形矩阵和长度为4的向量)。当然我已经创建了类来帮助我,因为我已经使用数组作为成员的数据类型,我知道它们将被连续分配。没有虚拟,所以没有v-table指针,所以没有RTTI,我确实有一个断言,但是我会警告我。

现在我有类似的东西:(这是伪代码,显示哪些有效,但没有具体细节,为了简洁起见)

class program {
    /*...*/
protected:
    Vec4 point_pos;
protected:
    program() {
        register_var("pos",point_pos);
    }
    void prepare() {}
    void run() {
        point_pos.fill(0);
    }
};
class specific_program: public program {
private:
    Mat4x4 mvp;
    Vec3 amb_dir;
    Vec4 vertex;
    Vec4as3<1> surface_norm;
    /*
    Vec4as3 may be assigned to by vec3s, takes the space of vec3;
    but the 4th value is the template param
    */
    Mat4x4 norm_trans;
public:
    specific_program() {
        register_var("a",mvp);
        register_var("b",amb_dir);
        register_var("c",surface_norm);
        register_Var("d",vertex);
    }   

    void prepare() {
        norm_trans = Mat4::inverse(mvp).transpose();
    }

    void run() {
        point_pos = mvp * vertex;
    }
};

register_var接受引用,并根据它找到地址,然后告诉系统&#34; heres你把这些数据放在哪里&#34;它会复制所需数量的花车。运行它然后研究结果。我想要做的是使用程序的结果(例如point_pos和任何其他声明为输出的结果)但不是作为单独的块,我想要有一个浮点数组和将结构投影到它们上,例如:(假设我们有输出point_pos和point_colour)

float 0: point_pos.x
float 1: point_pos.y
float 2: point_pos.z
float 3: point_pos.w
float 4: point_colour.x
float 5: point_colour.y
float 6: point_colour.z
/*any floats left (if fixed size) unused*/

如果我可以处理浮动缓冲区,它会快得多。 (在上面,7个浮点数,而不是可能在内存中不连续的2个变量)。

现在在C ++ 11中你有比以前更强大的元编程,使用与元组相同的逻辑,我想要一个我可以这样使用的类:

data_buffer<Mat4x4,Vec4,Vec3> variables;

说。然后(最坏情况)访问:variables.get<0>()*variables.get<1>()

虽然这不是一个元组!编译器不能填充这些,它必须在浮点数组上强加结构。例如,start_of_floats+4是上例中颜色向量的开头。它可以将此演员表返回Vec3&

&#34;更低&#34; (进一步远离代码结构)优化将确保这样的解决方案是有效的(至少对于GCC而言),因此在性能方面这很好。

我不确定如何使用模板执行此操作,该缓冲区指针的偏移量将来自模板参数,但我不确定如何获得Ts的sizeofs的总和进入模板的一个点。

奖励积分

您可以添加,更改或做的任何事情,以使这更好。我想使用对象,这是一个练习/实验,过去我使用了大量的宏而没有对象(在C中)来做这个。我希望看起来更漂亮,没有性能损失。

1 个答案:

答案 0 :(得分:0)

如果我理解你的话,我会从这样的设计开始:

template<typename ElType, std::size_t N>
class SubArray
{
public:
    typedef ElType value_type;
    typedef ElType* iterator;
    typedef const ElType* const_iterator;
    typedef ElType& reference;
    typedef const ElType& const_reference;
    typedef typename std::iterator_traits<ElType*>::size_type size_type;
    typedef typename std::iteartor_traits<ElType*>::difference_type difference_type;

    explicit SubArray(ElType* ptr);

    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;
    static constexpr size_type size() const;

    reference operator[](size_type i);
    const_reference operator[](size_type i) const;

private:
    ElType* m_start;
};

template<typename T> class ArrayUserWithStorage;

template<typename ElType, std::size_t N>
class ArrayUser
{
protected:
    typedef ElType element_type;
    static constexpr std::size_t elements();

    explicit ArrayUser(SubArray<ElType, N>);

    SubArray<ElType, N>& storage();
    const SubArray<ElType, N>& storage() const;

private:
    SubArray<ElType, N> m_storage;

    template <typename T>
    friend class ArrayUserWithStorage<T>;
};

template <typename T>
struct ArrayUserWithStorage :
    private std::array<typename T::element_type, T::elements()>,
    public ArrayUser<typename T::element_type, T::elements()>
{
public:
    template <typename... Args>
    ArrayUserWithStorage(Args&& ... args) :
        std::array(),
        T(ArrayUser<typename T::element_type, T::elements()>(data()),
          std::forward<Args>(args)...)
    {}
};

class Vec3 : public ArrayUser<float, 3>
{
public:
    explicit Vec3(SubArray<float, 3> storg);
    Vec3(SubArray<float, 3> storg, float x, float y, float z) :
        ArrayUser(storg)
    {
        storage()[0] = x;
        storage()[1] = y;
        storage()[2] = z;
    }

    // ...
};

typedef ArrayUserWithStorage<Vec3> LoneVec3;

template <typename ElType, typename... Ts>
class ArrayAsTuple
{
    template <std::size_t NToAdd, typename... Us>
    struct offset_helper

    template <std::size_t Sum, typename... Us>
    struct offset_helper<0, Us...>
    {
        static constexpr std::size_t offset = 0;
    };

    template <std::size_t NToAdd, typename U1, typename... Us>
    struct offset_helper<NToAdd, U1, Us...>
    {
        static_assert(std::is_same<typename U1::element_type, ElType>::value,
                      "Mismatch in ArrayUser element types");
        static constexpr std::size_t offset =
            U1::elements() + offset_helper<NToAdd-1, Us...>::offset;
    };

    static constexpr total_elements = offset_helper<sizeof...(Ts), Ts...>::offset;

    template <std::size_t I, typename... Us>
    struct nth_type;

    template <typename U1, typename... Us>
    struct nth_type<0, U1, Us...>
    { typedef U1 type; };

    template <std::size_t I, typename U1, typename... Us>
    struct nth_type<I, U1, Us...>
    { typedef typename nth_type<I-1, Us...>::type type; };

    std::array<ElType, total_elements> m_storage;

public:
    using array_type = std::array<ElType, total_elements>;

    ArrayAsTuple();

    array_type& arr() &;
    const array_type& arr() const &;

    template<std::size_t N>
    nth_type<N, Ts...> get() &;
    { return nth_type<N, Ts...>( m_storage.data() + offset_helper<N, Ts...>::offset ); }
};