2015-07-10 7 views
9

Vorrei creare un modello di classe che accetta un parametro intero senza segno e ha un membro u_ il cui tipo è il più piccolo tipo senza segno che conserverà il parametro intero.Selezionare un tipo intero in base al parametro intero del modello

Quindi:

template <uint64_t k> 
class A { 
    ??? u_; 
}; 

Per A<0>, u_ dovrebbe essere di tipo uint8_t. Lo stesso per A<255>. Per A<256>, u_ dovrebbe essere di tipo uint16_t, ecc.

Come implementare questo?

+2

Se 'k' è un' uint8_t', allora cosa significa questo ?: '... = A <256, uint16_t>'. 256 è più di 'uint8_t' può contenere, quindi questo non ha senso per me –

+0

Chi ha svalutato questa domanda ?? Non aveva senso finché non dedussi un po 'di tempo a dedurre dalle risposte ciò che la domanda stava effettivamente chiedendo. – Barry

risposta

0

È possibile utilizzare usando le dichiarazioni per i modelli parzialmente rilegati:

template<uint8_t k> 
using A_uint8 = A<k, uint8_t>; 

template<uint8_t k> 
using A_uint16 = A<k, uint16_t>; 

poi:

A_uint8<7> hello; // -> A<7, uint8_t>; 
A_uint16<256> hello; // -> A<256, uint16_t>; 
+1

Come può '256' essere l'argomento, quando il tipo di' k' è 'uint8_t'? Il più grande 'k' consentito è 255 –

+1

Oltre al problema evidenziato da @AaronMcDaid, questo non risolve il problema originale di _automaticamente_ selezionando un tipo, senza che ciò avvenga manualmente dal codice che chiama l'API. Il vantaggio principale che vedo con la selezione automatica è che le macro e le costanti possono essere usate per 'k', e il tipo verrà aggiornato automaticamente con la macro o la costante. –

8

Questo pezzo di metaprogrammazione inganno lo realizza:

template<unsigned int Y> struct typed 
{ 
    typedef typename typed<(Y & (Y - 1)) == 0 ? Y/2 : (Y & (Y - 1))>::type type; 
}; 

template<> struct typed<0> 
{ 
    typedef std::uint8_t type; 
}; 

template<> struct typed<256> 
{ 
    typedef std::uint16_t type; 
}; 

template<> struct typed<65536> 
{ 
    typedef std::uint32_t type; 
}; 

/* ToDo - add more specialisations as necessary*/ 

template<unsigned k> class A 
{ 
public: 
    unsigned static const k_ = k; /*constexpr if your compiler supports it*/ 
    typename typed<k>::type u_; 
}; 

L'utilizzo è esattamente in la domanda.

La versione del modello non specializzata prende il tipo precedente. typed<0> blocca la ricorsione statica. Le altre specializzazioni fungono da punti di ancoraggio per i tipi appropriati.

Il tempo di compilazione valutabile (Y & (Y - 1)) == 0 ? Y/2 : (Y & (Y - 1)) riduce il numero di istanze rimuovendo il bit più a destra di Y finché non viene raggiunta la potenza di 2, quindi divide per 2 successivamente quello. (Riconoscimento @ Jarod42).

6

Con C++ 11 è possibile utilizzare std::conditional:

#include <cassert> 
#include <cstdint> 
#include <limits> 
#include <type_traits> 

template<std::uint32_t K> 
class A 
{ 
public: 
    decltype(K) k_ = K; 

    typename std::conditional<K <= UINT8_MAX, 
          std::uint8_t, 
          typename std::conditional<K <= UINT16_MAX, 
          std::uint16_t, 
          std::uint32_t>::type>::type u_; 
}; 

int main() 
{ 
    A<100> small; 
    A<1000> medium; 
    A<100000> large; 

    assert((std::is_same<std::uint8_t, decltype(small.u_)>::value)); 
    assert((std::is_same<std::uint16_t, decltype(medium.u_)>::value)); 
    assert((std::is_same<std::uint32_t, decltype(large.u_)>::value)); 
} 

Questo presuppone che:

  • uint8_t in template<uint8_t k, typename > è solo una svista o, come ha sottolineato nel Aaron McDaid 's commento, il l'esempio non funziona. Ho cambiato uint8_t in uint32_t (l'esempio può essere esteso senza problemi a uint64_t);
  • int k_ = k; è un in-class member initialization. Per una definizione costante è possibile utilizzare enum {k_ = K};
  • u_ in typename u_; è un membro di dati. Se si tratta di una definizione di tipo, è possibile modificare facilmente l'esempio utilizzando typedef o type-alias (C++ 11).

Se non è possibile utilizzare C++ 11 c'è boost::conditional oppure si può scrivere la propria versione:

template<bool, class T, class F> 
struct conditional { typedef T type; }; 

template<class T, class F> 
struct conditional<false, T, F> { typedef F type; }; 
-2

Grazie molto. Ragazzi, rock! A proposito, sto usando:

typedef typename std::conditional<k <= UINT8_MAX, 
         std::uint8_t, 
         typename std::conditional<k <= UINT16_MAX, 
         std::uint16_t, 
         typename std::conditional<k <= UINT32_MAX, 
         std::uint32_t, 
         std::uint64_t>::type>::type>::type TypeU; 

std::pair<TypeU, std::shared_ptr<ProtoData>> min_; 
std::pair<TypeU, std::shared_ptr<ProtoData>> max_; 
0
template<uintmax_t Id> 
struct A; 

template<> 
struct A<0>{ 
    enum {id = 0}; 
    using type = uint8_t; 
}; 

template<> 
struct A<255>{ 
    enum {id = 255}; 
    using type = uint8_t; 
}; 

template<> 
struct A<256>{ 
    enum {id = 256}; 
    using type = uint16_t; 
}; 

int main(){ 
    typename A<255>::type a0 = 255; // uint8_t 
    typename A<256>::type a1 = 256; // uint16_t 
} 

Ma penso che si desidera avere una classe in modo che ogni valore al di sotto di 256 definirà uint8_t, ogni valore sotto 65536 definirà uint16_t ecc ecc

Quindi solo in caso, questo è come l'hai fatto:

template<uintmax_t Value> 
struct A{ 
    enum {value = Value}; 
    using type = std::conditional_t< 
     (Value <= std::numeric_limits<uint8_t>::max()), uint8_t, 
      std::conditional_t< 
       (Value <= std::numeric_limits<uint16_t>::max()), uint16_t, 
        std::conditional_t< 
         (Value <= std::numeric_limits<uint32_t>::max()), uint32_t 
          std::conditional_t< 
           (Value <= std::numeric_limits<uint64_t>::max()), uint64_t, uintmax_t 
          > 
        > 
      > 
    >; 
}; 

And here's a live example

4

Se quello che vogliamo è: dato un parametro modello uint64_t, dare il più piccolo tipo senza segno che è in grado di rappresentarlo, quindi quello che vogliamo veramente è solo una semplice iterazione in fase di compilazione.

namespace details { 
    template <typename T> 
    struct tag { 
     using type = T; 
    }; 

    // base case: just fail 
    template <uint64_t V, typename... > 
    struct min_unsigned_type; 

    // recursive case: check using numeric_limits 
    template <uint64_t V, typename T, typename... Ts> 
    struct min_unsigned_type<V, T, Ts...> 
    : std::conditional_t<(V <= std::numeric_limits<T>::max()), 
         tag<T>, 
         min_unsigned_type<V, Ts...>> 
    { }; 
} 

Poi basta un alias per avvolgere le cose insieme:

template <uint64_t V> 
using min_unsigned_type = 
    typename details::min_unsigned_type<V, 
     uint8_t, uint16_t, uint32_t, uint64_t>::type; 

Questo ha il vantaggio di essere in grado di specificare con facilità quanto si vuole andare, o anche essere in grado di aggiungere i tipi senza segno più grandi se è qualcosa che trovi necessario.

E infine la classe:

template <uint64_t V> 
struct A { 
    static constexpr uint64_t k_ = V; 
    min_unsigned_type<V> u_; 
}; 
+0

Funzionerà solo con C++ 11, ma è molto elegante e scalabile. Probabilmente la migliore risposta qui. – manlio

Problemi correlati