Va bene, questa domanda si è evoluto un po ', e voglio provare ad avviare (oltre), con gli obiettivi di base sto girando per:c Namespace ++ mal di testa
- Creare codice della libreria che wrappers eredità linguaggio C le entità nell'acquisizione di risorse C++ sono inizializzate e forniscono anche una garanzia di eccezione di base o migliore.
- Abilita i client di questo codice a utilizzarlo in modo C++ molto naturale senza creare un sovraccarico di codice esistente per convertirlo a utilizzare gli oggetti wrapper C++ (ovvero conversione automatica a tipi legacy appropriati, costruttori che ereditano legacy tipi, ecc.)
- Limitare l'impatto sullo spazio dei nomi del codice della libreria. Idealmente, la libreria avrebbe diversi sottolivelli che forniscono funzionalità correlate che limitano il volume e l'impatto dell'uso delle dichiarazioni del tipo X dello spazio dei nomi - proprio come fanno le librerie di boost (cioè l'uso di spazi dei nomi dei dettagli per iniettare solo quei simboli che l'utente vorrebbe ragionevolmente per utilizzare e nascondere quelli che sono dettagli di implementazione, limitare anche la portata di possibili nuovi significati ai simboli esistenti, per evitare conversioni implicite sorprendenti all'interno del codice utente)
- Richiedere che i client richiedano esplicitamente quelle parti della libreria che in realtà voglio iniettare nella loro base di codice. Questo va di pari passo con la limitazione dell'impatto dell'inclusione delle intestazioni della biblioteca. Il codice client dovrebbe avere un ragionevole livello di controllo su quali parti della libreria verranno automaticamente utilizzate per la risoluzione dei nomi quando compilano il loro codice.
- Il mio codice libreria non dovrebbe essere pieno di costrutti di codice refactoring. Sarebbe ideale se le intestazioni della libreria non dovessero dichiarare costantemente typedef privati per poter accedere al resto di quella sezione della libreria. O in altre parole: voglio che la mia libreria sia in grado di essere scritta in modo intuitivo come i miei clienti quando fanno uso di detta libreria. La risoluzione dei nomi dovrebbe includere lo spazio dei nomi in cui è definita la libreria, oltre a tutti gli altri che sono stati esplicitamente "using'd".
Ho incontrato questo scenario spesso, e sono alla ricerca di un modo migliore ...
Ho una classe, C nel namespace N. C ha un membro, libero. È gratis qualcosa che C gestisce e consente a C di gestire una cosa nuova.
Esistono diverse funzioni globali gratuite. Ci sono anche alcune funzioni di supporto nello stesso namespace N di C, una delle quali è un helper che libera la cosa gestita da C, chiamata free.
Così abbiamo qualcosa di simile:
namespace N {
void free(THING * thing);
class C
{
public:
... details omitted...
free()
{
free(m_thing); // <- how best to refer to N::free(THING&)
}
}
} // namespace N
ho potuto utilizzare N :: gratuito (m_thing). Ma mi sembra sfortunato. Non c'è modo di riferirsi a ciò che è al di fuori dell'ambito della classe, ma senza risolvere lo spazio dei nomi assoluto (un relativo passo fuori dal campo di applicazione)?
Mi sembra che dover nominare N :: free sia odioso, poiché non sarebbe necessario se si trattasse di una funzione indipendente. Né sarebbe necessario se il nome del metodo della classe fosse diverso (ad es. Dispose). Ma poiché ho usato lo stesso nome, non posso accedervi senza dover specificare cosa rappresenta un percorso assoluto - piuttosto che un percorso relativo - se mi concederai l'analogia.
Odio i percorsi assoluti. Trasformano le cose in giro in spazi dei nomi molto fragili, quindi il refactoring del codice diventa molto più brutto. Inoltre, le regole su come denominare le cose nei corpi delle funzioni diventano più complesse con l'attuale serie di regole (come le comprendo) - meno regolari - inducendo uno scisma tra ciò che ci si aspetta e ciò che si ottiene come programmatore.
Esiste un modo migliore per accedere a funzioni indipendenti nello stesso spazio dei nomi di una classe senza dover assolutamente assegnare un nome assoluto alla funzione libera?
EDIT: Forse avrei dovuto andare con un esempio meno astratto:
namespace Toolbox {
namespace Windows {
// deallocates the given PIDL
void Free(ITEMIDLIST ** ppidl);
class Pidl
{
public:
// create empty
Pidl() : m_pidl(NULL) { }
// create a copy of a given PIDL
explicit Pidl(const ITEMIDLIST * pidl);
// create a PIDL from an IShellFolder
explicit Pidl(IShellFolder * folder);
...
// dispose of the underlying ITEMIDLIST* so we can be free to manage another...
void Free();
};
Così ITEMIDLIST * provengono da una varietà di luoghi, e sono distrutti con CoTaskMemFree(). Potrei introdurre Pidl come nome globale - così come tutte le funzioni di aiuto nell'intestazione "Windows Shell.h" che fa parte della mia libreria di toolbox.
Idealmente, segmenterei alcuni degli strumenti nella mia libreria in base a ciò a cui si riferiscono - in questo caso, il tutto si riferisce alla programmazione COM in Windows. Ho scelto Toolbox come spazio dei nomi di base per le mie librerie e stavo pensando che avrei usato Toolbox :: Windows per funzioni, classi, ecc. Di windows-y
Ma lo spazio dei nomi C++ e le regole di risoluzione dei nomi sembrano per rendere questo molto difficile (quindi questa domanda). Rende molto innaturale creare tale segmentazione del mio codice - dato che la ricerca di koenig fallisce (dal momento che ITEMIDLIST non si trova nel mio spazio degli strumenti Toolbox :: Windows), e non ho la possibilità di spostarlo lì! Il linguaggio dovrebbe essere abbastanza flessibile, IMO, per consentire sia alle librerie di estensioni come la mia libreria Toolbox di estendere il codice di altre persone senza dover iniettare le mie estensioni nel loro spazio dei nomi (che, nel caso di Win32 e il vasto generale la maggior parte del codice che esiste oggi, è il GLOBAL NS - che è il punto principale della creazione degli spazi dei nomi in primo luogo: evitare l'affollamento globale di NS/l'inquinamento/l'ambiguità/le sorprese di programmazione).
Quindi, torno in giro a, c'è un modo migliore per fare questo: estendere librerie esistenti di codice, mentre non inquinante loro NS con le mie estensioni, ma ancora consentire la risoluzione del nome intuitivo e utile come ci si aspetterebbe se il mio il codice era nel loro NS ma è stato esplicitamente introdotto dal client del mio codice (cioè non voglio iniettare il mio codice volenti o nolenti, ma solo su richiesta esplicita)?
un altro pensiero: Forse quello che sarebbe soddisfare la mia sopra criterea sarebbe se ho avuto il seguente:
using namespace X {
code here...
}
Dove ho potuto mettere un tale costrutto ovunque, anche in un colpo di testa, e non avrei dovuto essere preoccupato di trascinare X nel codice del mio client, ma avrei la stessa libertà di scrivere il mio codice sorgente come farei se fossi nello spazio dei nomi di root.
Per fare l'avvocato del diavolo. N: free() significherebbe più per me se avessi iniziato a leggere il tuo codice sorgente in una posizione casuale. Soprattutto se ho visto X: libero usato un paio di righe dopo.Mi rende più facile leggere il tuo codice senza i tuoi commenti/documentazione –
Per me, ciò sembra controproducente. In tal caso, si dovrebbero semplicemente mettere tutti i nomi nello spazio dei nomi globale e assicurarsi che siano davvero espliciti - "free_a_thing()," create_a_thing() "ecc., Poiché sembra come accade quando si deve mettere Per me, l'idea delle funzioni di sovraccarico è che il compilatore risolve il problema corretto in base all'utilizzo: un'inferenza intelligente da ciò che si scrive. Albero t; Bear b; Can c; free (t) ; free (b); free (c); perché è peggio di free_a_tree (t), free_a_can (c), ... ???! – Mordachai
Perché non 'eliminare m_thing'? – GManNickG