2016-05-17 29 views
12

Desidero scrivere una libreria di serializzazione generica che fornisce ad es. una funzione generica save. La libreria contiene tratti di tipo personalizzato, ad es. some_condition:Caratteri estensibili di tipo in C++

comportamento
template <typename T> 
struct some_condition 
{ 
    constexpr static bool value = std::is_same<std::string, T>::value ||std::is_arithmetic<T>::value ; 
}; 

s' save viene selezionato in base some_condition:

template <typename T> 
std::enable_if_t<some_condition<T>::value> save(const T& value) 
{ 
    std::cout << "these types will be handled in a specific way: " << value << std::endl; 
} 

template <typename T> 
std::enable_if_t<!some_condition<T>::value> save(const T& value) 
{ 
    std::cout << "these types will be handled in another way: " << value << std::endl; 
} 

save sarà personalizzabile per i tipi di dati degli utenti, non solo attraverso il sovraccarico, ma anche genericamente tramite tratti. Quindi ho creato trait_extension che può essere specializzata per i tratti modelli:

template <template<typename> class Trait, typename T> 
struct trait_extension : Trait<T> 
{ 
} 

save deve essere modificato di conseguenza:

template <typename T> 
std::enable_if_t<trait_extension<some_condition,T>::value> save(const T& value) { ... } 


template <typename T> 
std::enable_if_t<!trait_extension<some_condition,T>::value> save(const T& value) { ... } 

Un utente potrebbe ora fornire la propria specializzazione delle trait_extension:

template <typename T> 
struct trait_extension<some_condition, T> 
{ 
    // user specific extension: exclude floats from condition 
    constexpr static bool value = !std::is_floating_point<T>::value && some_condition<T>::value; 
}; 

La mia domanda ::

Esiste un modo "migliore"/più elegante per realizzare i tratti estensibili?

live example

+0

Non sarebbe più semplice avere un "caso generale" in cui is_string è definito come falso e le specializzazioni definiscono qualsiasi cosa è considerata una stringa? – OMGtechy

+0

Hai consultato [C++ Container Pretty-Printer] (https://github.com/louisdx/cxx-prettyprint) come riferimento? – ildjarn

+0

"Tuttavia, ciò funzionerà solo se la specializzazione' trait_extension' esiste prima di 'print'." Eh? –

risposta

1

Non credo che il tuo approccio è elegante a tutti. Potrebbe facilmente trasformarsi in codice spaghetti e renderlo difficile da mantenere o utilizzare. Adotterei invece un approccio basato su "policy", simile alla classe std::allocator nella libreria standard. Cambiare il comportamento è una semplice questione di implementare l'interfaccia di allocatore e fornirla come parametro di modello. Quindi tutto funziona autonomamente.

Per prima cosa, in una libreria di "serializzazione generica", è necessario preoccuparsi non solo dei tipi, ma anche della localizzazione. Potrebbe essere qualcosa di semplice come usare , invece di . o complesso come capitalizzazione uniforme. Con il vostro approccio, non è molto facile modificare il flusso o locale (ad esempio, std vs boost) di back-end, ma con un approccio basato su policy è una questione di ricerca e sostituzione:

serialize<int, std_locale<int>>(32.000) 
serialize<int, boost_locale<int>>(32.000) 

Questo permette di fornire un insieme di "valori predefiniti", ad esempio una classe di locale principale, ala std::allocator, e quindi l'utente può ereditarlo e modificare il comportamento per uno o due tipi, anziché fornire folli sovraccarichi di SFINAE.