2015-05-11 14 views
6

Ispirato da questa domanda: c++ generate (xyz) points in rangededurre intero spazio vettore al momento della compilazione

ho cominciato a chiedermi se c'è una forma di codice di modello che può, da questa affermazione:

using X = axis_limits<-10, +10>; 
using Y = axis_limits<-10, +10>; 
using Z = axis_limits<-10, +10>; 

auto space = std::vector<point>{ generate_point_space<X, Y, Z> }; 

costrutto in fase di compilazione un vettore chiamato spazio che contiene un punto per ogni x, y, z dove inizia (X) < = x < fine (X) ... ecc per y e z.

L'ordine non è importante.

Il tipo di restituzione di generate_point_space<> deve essere std::initializer_list<int> o sequenza simile compilata nel tempo. Non sto cercando di generare una sequenza di chiamate a push_back(). Sarebbe troppo facile :)

struct point avrebbe un costruttore del form:

point::point(int x, int y, int z) 

una sola dimensione di int è semplice (codice qui sotto). L'aspetto multidimensionale del problema è al di là di me oggi

#include <utility> 
#include <iostream> 
#include <vector> 

template<int Begin, int End> 
struct axis_limits 
{ 
    static constexpr int first = Begin; 
    static constexpr int last = End; 
}; 

namespace details 
{ 
    template<typename Int, typename, Int Begin, bool Increasing> 
    struct integer_range_impl; 

    template<typename Int, Int... N, Int Begin> 
    struct integer_range_impl<Int, std::integer_sequence<Int, N...>, Begin, true> { 
     using type = std::integer_sequence<Int, N+Begin...>; 
    }; 

    template<typename Int, Int... N, Int Begin> 
    struct integer_range_impl<Int, std::integer_sequence<Int, N...>, Begin, false> { 
     using type = std::integer_sequence<Int, Begin-N...>; 
    }; 
} 

template<typename Int, Int Begin, Int End> 
using integer_range = typename details::integer_range_impl< 
Int, 
std::make_integer_sequence<Int, (Begin<End) ? End-Begin : Begin-End>, 
Begin, 
(Begin<End) >::type; 

template<int...Is> 
std::vector<int> make_vector(std::integer_sequence<int, Is...>) 
{ 
    return std::vector<int> { Is... }; 
} 

template<int Begin, int End> 
struct axis_range 
{ 
    using sequence_type = integer_range<int, Begin, End>; 
    static constexpr int size = sequence_type::size(); 
    static std::vector<int> as_vector() 
    { 
     return make_vector(sequence_type {}); 
    } 
}; 

template< int Begin, int End > 
std::vector<int> make_axis(const axis_limits<Begin, End> &) 
{ 
    return axis_range<Begin, End>::as_vector(); 
} 

template<class T> 
void dump_vector(std::ostream& os, const std::vector<T>& v) { 
    const char* sep = "{ "; 
    for(const auto& i : v) { 
     os << sep << i; 
     sep = ", "; 
    } 
    os << " }"; 
} 

template<class T> 
std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec) 
{ 
    dump_vector(os, vec); 
    return os; 
} 

using namespace std; 

int main() 
{ 

    using X = axis_limits<-5, +5>; 
    auto space = std::vector<int>(make_axis(X{})); 
    cout << space << endl; 
    return 0; 
} 

uscita in corrente:

{ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4 } 

quello Cerco:

{ { -10, -10, -10 }, { -10, -10, -9 } .... { 9, 9, 8 }, { 9, 9, 9 } } 
+0

Si prega di non aggiungere nuove, diverse domande a una domanda esistente. – Yakk

risposta

2

Alcuni aiutanti metaprogrammazione per lavorare con gli elenchi dei tipi:

template<class T>struct tag{using type=T;}; 
template<class Tag>using type=typename Tag::type; 

template<class...>struct types{using type=types;}; 

template<class...Ts> 
struct cat; 
template<class...Ts> 
using cat_t=type<cat<Ts...>>; 

template<class...As, class...Bs, class...Ts> 
struct cat< types<As...>, types<Bs...>, Ts... >: 
    cat< types<As...,Bs...>, Ts... > 
{}; 
template<class...Ts> 
struct cat< types<Ts...> >: 
    types<Ts...> 
{}; 
template<> 
struct cat<>: 
    types<> 
{}; 

Un modo per mappare tra sequenze di valori e sequenze di tipi. Trovo tipi più facile lavorare con:

template<class Seq> 
struct seq_to_types; 
template<class Seq> 
using seq_to_types_t=type<seq_to_types<Seq>>; 
template<class T, T...ts> 
struct seq_to_types< std::integer_sequence<T,ts...> >: 
    tag< types< std::integral_constant<T,ts>... > > 
{}; 
template<class T, class Rhs> 
struct types_to_seq:tag<Rhs>{}; 
template<class T, class types> 
using types_to_seq_t=type<types_to_seq<T,types>>; 
template<class T, T...ts> 
struct types_to_seq<T, types<std::integral_constant<T, ts>...>>: 
    tag<std::integer_sequence<T, ts...>> 
{}; 
template<class T, class...Ts> 
struct types_to_seq<T, types<Ts...>>: 
    types< types_to_seq_t<T, Ts>... > 
{}; 

ora possiamo fare un std::integer_sequence<int, 1,2,3> e produrre types< std::integral_constant<int,1>, std::integral_constant<int,2>, std::integral_constant<int,3> > che a mio parere è molto più facile da lavorare. Possiamo anche tornare indietro.

Questo richiede un types<Ts...> e una funzione sui tipi, e fa la mappa:

template<template<class...>class M, class Seq> 
struct mapper; 
template<template<class...>class M, class Seq> 
using mapper_t=type<mapper<M,Seq>>; 
template<template<class...>class M, class...Ts> 
struct mapper<M, types<Ts...>>: 
    types<M<Ts>...> 
{}; 

mapper_t< some_metafunction, types<blah...>> mapperà ogni blah attraverso il some_metafunction per la produzione di un nuovo elenco di tipi.

successivo, un modo per prendere una funzione tipo, e fermare il primo argomento di X:

template<template<class...>class F, class X> 
struct bind_1st { 
    template<class...Ts> 
    using apply=F<X,Ts...>; 
}; 

che può utilizzare fare un prodotto trasversale facilmente (insieme con cat_t e mapper_t):

template<class...Ts> 
struct cross_product:types<types<>>{}; 
template<class...Ts> 
using cross_product_t=type<cross_product<Ts...>>; 

template<class...T0s, class...Ts> 
struct cross_product<types<T0s...>, Ts...>:cat< 
    mapper_t< 
    bind_1st<cat_t, types<T0s>>::template apply, 
    cross_product_t<Ts...> 
    >... 
>{}; 

Ora lavoriamo al prossimo problema. Abbiamo una serie di punti e vogliamo generare il loro prodotto incrociato.

template<class...Seq> 
struct coords; 
template<class...Seq> 
using coords_t=type<coords<Seq...>>; 
template<class T, T...ts, class...Ts> 
struct coords< std::integer_sequence<T,ts...>, Ts... >: 
    types_to_seq< 
    T, 
    cross_product_t< 
     seq_to_types_t<std::integer_sequence<T,ts...>>, 
     seq_to_types_t<Ts>... 
    > 
    > 
{}; 

dovrebbe esplodere bene.

live example.

Il passo successivo è costruire la sintassi.

template<class T, T t0, class Seq> 
struct offset_sequence; 
template<class T, T t0, class Seq> 
using offset_sequence_t=type<offset_sequence<T, t0, Seq>>; 
template<class T, T t0, T...ts> 
struct offset_sequence<T, t0, std::integer_sequence<T, ts...>>: 
    tag<std::integer_sequence<T, (t0+ts)...>> 
{}; 
template<int start, int finish> 
using axis_limits = offset_sequence_t<int, start, 
    std::make_integer_sequence<finish-start> 
>; 

template<class T> 
using point = std::vector<T>; 

template<class T, T...Is> 
point<T> make_point(std::integer_sequence<T, Is...>) { 
    return {Is...}; 
} 
template<class...Pts> 
std::vector<point<int>> make_space(types<Pts...>) { 
    return { make_point(Pts{})... }; 
} 
template<class...Ts> 
std::vector<point<int>> generate_point_space() { 
    return make_space(coords_t<Ts...>{}); 
} 

e abbiamo la sintassi che si desidera.

Possiamo fare cose in matrici e tutto constexpr se vogliamo. Basta cambiare make_point per restituire un array di sizeof...(Is) e simili.

+0

Bello. Grazie. –

+1

come mi piacerebbe leggere un libro su quest'arte. Mi manca questo nel bookshelf C++, moderno modello metaprogramming (con approfondimenti funzionali). –

2

std::vector constructors non sono constexpr, in modo da' sfortunato qui. Non è possibile restituirli come constexpr da una funzione di fabbrica, il vettore è sempre costruito in fase di esecuzione. Forse dovresti provare con lo std::array? Qualcosa di simile

#include <iostream> 
#include <utility> 
#include <array> 

template<int...Is> 
constexpr auto make_array(const std::integer_sequence<int, Is...>& param) 
{ 
    return std::array<int, sizeof...(Is)> {Is...}; 
} 

int main() 
{ 
    constexpr std::integer_sequence<int, 1,2,3,4> iseq{}; 
    constexpr auto arr = make_array(iseq); 

    for(auto elem: arr) 
     std::cout << elem << " "; 
} 
+0

std :: array sarebbe accettabile penso –

+0

mi stavo chiedendo se c'era qualcosa che poteva essere fatto con tuple_cat e successivamente trasformando la tupla in un tipo variadico? –

+0

Quindi è possibile creare una funzione factory che restituisce un 'constexpr std :: array'. Francamente, non ho una soluzione immediata per quest'ultimo, ma sono abbastanza sicuro che possa essere fatto. So solo per certo che 'std :: vector' non è la strada da percorrere. – vsoftco

6

Si può fare qualcosa di simile al seguente:

template<int Begin, int End> 
struct axis_limits 
{ 
    static constexpr int first = Begin; 
    static constexpr int last = End; 
    static constexpr int range = End - Begin + 1; 
}; 

struct point 
{ 
    explicit point(int x, int y, int z) : x(x), y(y), z(z) {} 
    int x; int y; int z; 
}; 

namespace detail 
{ 

    template <typename X, typename Y, typename Z, std::size_t... Is> 
    std::vector<point> generate_point_space_impl(std::index_sequence<Is...>) 
    { 
     return {point(
      static_cast<int>(Is/(Z::range * Y::range)) % X::range + X::first, 
      static_cast<int>(Is/Z::range) % Y::range + Y::first, 
      static_cast<int>(Is) % Z::range + Z::first)... 
      }; 
    } 

} 


template <typename X, typename Y, typename Z> 
std::vector<point> generate_point_space() 
{ 
    return detail::generate_point_space_impl<X, Y, Z>(std::make_index_sequence<X::range * Y::range * Z::range>()); 
} 

Live demo

+0

bello che ha funzionato bene (con un paio di modifiche alla sintassi) –

+0

@RichardHodges: sintassi typo risolto. – Jarod42

+0

Solo una parte, non usare C-cast come hai fatto qui ('int (...)'), è pericoloso. Usa 'static_cast (...)' come ricambio. http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used – dasdave

Problemi correlati