I modelli C++ sono generalmente assimilati ai creatori di bloat e l'idea di Shim si occupa esattamente di questo: rendere il modello solo un involucro sottile su una funzione regolare. È davvero un ottimo modo per ridurre il peso.Un modo più succinto di utilizzare gli shim nei modelli variadici?
Per esempio, usiamo un semplice spessore:
//
// Shim interface
//
struct Interface {
virtual void print(std::ostream& out) const = 0;
}; // struct Interface
std::ostream& operator<<(std::ostream& out, Interface const& i) {
i.print(out);
return out;
}
template <typename T>
struct IT: public Interface {
IT(T const& t): _t(t) {}
virtual void print(std::ostream& out) const { out << _t; }
T const& _t;
};
template <typename T>
IT<T> shim(T const& t) { return IT<T>(t); }
Ora, posso usarlo in questo modo:
void print_impl(Interface const& t);
template <typename T>
void print(T const& t) { print_impl(shim(t)); }
E non importa quanto print_impl
è implementata, print
rimane molto leggero e dovrebbe essere in linea. Vai tranquillo.
C++ 11 introduce tuttavia variadic modelli. L'impulso tipico è quindi quello di reimplementare tutti i C-variadics non sicuri con i modelli variadici di C++ 11, anche Wikipedia lo suggerisce con lo a printf
implementation.
Sfortunatamente, l'implementazione di Wikipedia non tratta argomenti posizionali: il tipo che consente di specificare stampare il terzo parametro di là, ecc ... Sarebbe facile, se solo avessimo una funzione con questo prototipo:
void printf_impl(char const* format, Interface const* array, size_t size);
o simile.
Ora, come possiamo ponte dalla interfaccia originale:
template <typename... T>
void printf(char const* format, T const&... t);
alla firma di cui sopra?
Una difficoltà con gli spessori è che si basano sul legame al comportamento const-ref per estendere la durata di vita del involucro temporaneo creato quel tanto che basta, senza dover allocare dinamicamente la memoria (che avrebbero non essere a buon mercato, se hanno fatto).
Sembra difficile però ottenere quel binding + la trasformazione dell'array in un solo passaggio. Soprattutto perché non sono consentiti matrici di riferimenti (e puntatori ai riferimenti) nella lingua.
Ho un inizio di una soluzione, per chi fosse interessato:
//
// printf (or it could be!)
//
void printf_impl(char const*, Interface const** array, size_t size) {
for (size_t i = 0; i != size; ++i) { std::cout << *(array[i]); }
std::cout << "\n";
}
template <typename... T>
void printf_bridge(char const* format, T const&... t) {
Interface const* array[sizeof...(t)] = { (&t)... };
printf_impl(format, array, sizeof...(t));
}
template <typename... T>
void printf(char const* format, T const&... t) {
printf_bridge(format, ((Interface const&)shim(t))...);
}
tuttavia si nota l'introduzione di una fase complementare, che è un po 'fastidioso. Ancora, it appears to work.
Sarei molto grato se qualcuno avesse una migliore implementazione da proporre.
@Potatoswatter suggerito usando liste di inizializzazione, che helps a bit (nessuna fascia-per lì).
void printf_impl(char const*, std::initializer_list<Interface const*> array) {
for (Interface const* e: list) { std::cout << *e; }
std::cout << "\n";
}
template <typename... T>
void printf_bridge(char const* format, T const&... t) {
printf_impl(format, {(&t)...});
}
Tuttavia, non risolve il problema della funzione intermedia.
Oh no, sbuffo! Alcuni KB in più nel mio eseguibile sono la fine del mio mondo. – Puppy
@DeadMG questo tipo di cose può davvero risparmiare megabyte. – Potatoswatter
Non penso che la variabile membro 'T const & _t;' in 'IT' estende la durata del temporaneo restituito dalla funzione' shim() '. Quando il costruttore ritorna, '_t' si riferisce all'oggetto destructed. – Nawaz