2015-07-01 2 views
1

Следующее репо пытается взять std :: tuple и перебрать его для вывода различных значений, связанных с ним. Std :: tuple - это вершина, и конечным использованием этого будет вызов glEnableVertexArray и glVertexAttribPointer для элементов.Тип выведения основывается на существовании признака

До сих пор я выполнял итерации типов компонентов кортежа, а также находил смещение в каждом кортеже для каждого элемента. Однако я застрять с этой функцией:

template<class T> 
void EmitAttribute(T const & v, int stride, int offset, int i) 
{ 
    std::cout << "Stride is " 
       << stride 
       << " element index " 
       << i 
       << " is at offset " 
       << offset 
       << " has 1 component " 
       << std::endl; 
} 

Для основных типов (не-структуры), я хочу, чтобы излучать «имеет 1 компонент». Для элементов с признаком num_components я хочу испустить количество компонентов. Я пробовал:

template<class T, class S> 
void EmitAttribute(T<S> const & v, int stride, int offset, int i) 
{ 
    ... 
       << " has " << T::num_components << " components " 
    ...  
} 

Но он не компилируется. Как написать шаблон таким образом, что одна функция вызывается, когда T не имеет признака num_components, а другой вызывается, когда это происходит?

Полный репо:

#include <iostream> 
#include <tuple> 

template<class T, int C> 
struct vec 
{ 
    typedef T value_type; 

    enum { num_components = C }; 
}; 

template<class T> 
struct vec2 : vec<T, 2> 
{ 
public: 

    T x, y; 
    vec2(T X, T Y) : x(X), y(Y) {} 
}; 

template<class T> 
struct vec3 : vec<T, 3> 
{ 
public: 

    T x, y, z; 
    vec3(T X, T Y, T Z) : x(X), y(Y), z(Z) {} 
}; 

template<class T> 
struct vec4 : vec<T, 4> 
{ 
public: 

    T x, y, z, w; 
    vec4(T X, T Y, T Z, T W) : x(X), y(Y), z(Z), w(W) {} 
}; 

namespace VertexAttributes 
{ 
    template<class T> 
    void EmitAttribute(T const & v, int stride, int offset, int i) 
    { 
     std::cout << "Stride is " 
        << stride 
        << " element index " 
        << i 
        << " is at offset " 
        << offset 
        << " has 1 component " 
        << std::endl; 
    } 

    template<int index, class T> 
    int ElementOffset(T & t) 
    { 
     return static_cast<int>(reinterpret_cast<char*>(&std::get<index>(t)) - reinterpret_cast<char*>(&t)); 
    } 

    template<int index, typename... Ts> 
    struct Emitter { 
     void EmitAttributes(std::tuple<Ts...>& t, unsigned size) { 
      EmitAttribute(std::get<index>(t), size, ElementOffset<index>(t), index); 
      Emitter <index - 1, Ts...> {}.EmitAttributes(t, size); 
     } 
    }; 

    template<typename... Ts> 
    struct Emitter < 0, Ts... > { 
     void EmitAttributes(std::tuple<Ts...>& t, unsigned size) { 
      EmitAttribute(std::get<0>(t), size, ElementOffset<0>(t), 0); 
     } 
    }; 

    template<typename... Ts> 
    void EmitAttributes(std::tuple<Ts...>& t) { 
     auto const size = std::tuple_size<std::tuple<Ts...>>::value; 
     Emitter < size - 1, Ts... > {}.EmitAttributes(t, sizeof(std::tuple<Ts...>)); 
    } 
} 

int main() 
{ 
    typedef std::tuple<vec2<float>, vec3<double>, vec4<float>> vertexf; 
    typedef std::tuple<vec2<double>, vec3<float>, vec4<double>> vertexd; 
    typedef std::tuple<int, vec3<unsigned>, double> vertexr; 

    vertexf vf = std::make_tuple(vec2<float>(10, 20), vec3<double>(30, 40, 50), vec4<float>(60, 70, 80, 90)); 
    vertexd vd = std::make_tuple(vec2<double>(10, 20), vec3<float>(30, 40, 50), vec4<double>(60, 70, 80, 90)); 
    vertexr vr = std::make_tuple(100, vec3<unsigned>(110, 120, 130), 140.5); 

    VertexAttributes::EmitAttributes(vf); 
    VertexAttributes::EmitAttributes(vd); 
    VertexAttributes::EmitAttributes(vr); 

    return 0; 
} 
+0

Почему бы не использовать только один класс и использовать 'T data [N]' как элемент данных, а не 'T data1, data2, data3, ... dataN'? Также исследуйте вариационный шаблон и 'std :: initializer_list'. – Nawaz

+0

Я думаю, что вы предлагаете создать vec ? Да, да. Что касается того, как данные хранятся для каждого элемента, у меня есть выбор. Я могу использовать std :: array или отдельные компоненты или объединение двух. Приятно иногда писать v [1], а иногда писать v.y. – Robinson

+0

Если вам нужны и хорошие имена, то вы можете иметь 'x()', 'y()', 'z()' как функции-члены, которые 'static_assert' определяют для проверки действительности, а затем используют соответствующий индекс для вернуть запрошенное значение. – Nawaz

ответ

1

Вы можете создать черты

namespace detail 
{ 
    template <typename T> 
    decltype(T::num_components, void(), std::true_type{}) has_num_components_impl(int); 

    template <typename T> 
    std::false_type has_num_components_impl(...); 
} 

template <typename T> 
using has_num_components = decltype(detail::has_num_components_impl<T>(0)); 

, а затем использовать SFINAE или тег диспетчеризацию:

template <typename T> 
std::enable_if_t<!has_num_components<T>::value, std::size_t> 
get_num_components() { return 1; } 

template <typename T> 
std::enable_if_t<has_num_components<T>::value, std::size_t> 
get_num_components() { return T::num_components; } 

И, наконец:

template<class T> 
void EmitAttribute(T const & v, int stride, int offset, int i) 
{ 
    std::cout << "Stride is " 
       << stride 
       << " element index " 
       << i 
       << " is at offset " 
       << offset 
       << " has " 
       << get_num_components<T>() 
       << " component " 
       << std::endl; 
} 

Live Demo

+0

из интереса, почему decltype имеет предложение void() в качестве второго аргумента? –

+0

@RichardHodges: обрабатывать злое дело с возможным перегрузкой оператора coma 'num_components'. – Jarod42

+0

aha! Спасибо. Это отличная вещь в C++, всегда есть что-то новое! –

Смежные вопросы