2015-08-12 19 views
5

Ho un problema di ordinamento del costruttore che sto cercando di trovare modi creativi per risolvere.Problema di costruzione costruttore (ambito globale)

Fondamentalmente ho una semplice classe Color che memorizza le informazioni sul colore RGB e consente la manipolazione di detto colore e la conversione in altri spazi colore (24 bit, 16 bit, 4 bit, HSV, XYZ, LAB, ecc.). La classe stessa funziona perfettamente.

Ho anche una libreria di colori predefiniti, come ad esempio:

namespace Colors { 
    const Color Snow     (255,250,250); 
    const Color GhostWhite   (248,248,255); 
    const Color WhiteSmoke   (245,245,245); 
    const Color Gainsboro    (220,220,220); 
    const Color FloralWhite   (255,250,240); 
    const Color OldLace    (253,245,230); 
    const Color Linen     (250,240,230); 
    const Color AntiqueWhite   (250,235,215); 
    const Color PapayaWhip   (255,239,213); 
    const Color BlanchedAlmond  (255,235,205); 
}; 

E tutti funzionano bene anche quando viene utilizzato in un programma normalmente.

Il mio problema si presenta quando provo ad usare quei colori di libreria nel costruttore su un altro oggetto. Non c'è niente da dire che il costruttore per il colore della libreria che uso sia stato eseguito e che i dati del colore siano assegnati (fa qualche piccola pre-elaborazione per calcolare alcuni dei diversi valori dello spazio colore) prima del costruttore per l'altra classe che riceve il Color e lo assegna a una variabile di memoria all'interno di se stesso.

Ad esempio, la classe di colore ha un costruttore:

Color(const Color &c) { 
    setColor(c.getRed(), c.getGreen(), c.getBlue()); 
} 

E un operatore =:

Color &Color::operator=(const Color &rhs) { 
    setColor(rhs.getRed(), rhs.getGreen(), rhs.getBlue()); 
    return *this; 
} 

setColor() è solo una funzione di supporto poco che memorizza i valori e le pre-calcola un pò di colore spazio valori alternativi.

Quando includo uno nel costruttore di un altro oggetto, dire:

Color _storeColor; 
TestClass(const Color &c) { 
    _storeColor = c; 
} 

o:

Color _storeColor; 
TestClass(const Color &c) : _storeColor(c) {} 

con:

TestClass myTest(Colors::WhiteSmoke); 

i dati colore assegnate è (quasi sempre) tutto 0 come se il costruttore per la classe Color non sia stato eseguito ancora, che ottengo totalmente.

Quindi sto cercando idee su come posso creare la mia libreria di colori predefiniti in modo tale che siano disponibili ad altri costruttori nell'ambito globale.

Per inciso, cose come:

TestClass myTest(Color(245,245,245)); 

funzionare perfettamente, anche se preferirei non avere centinaia (ed è) o centinaia #define macro per la libreria di colori in quanto ciò causare un sacco di doppioni inutili oggetto e preferirei tenerlo come sempre facendo riferimento alle stesse istanze globali ogni volta che un colore viene riutilizzato.

risposta

5

Lo standard C++ non definisce l'ordine in cui i costruttori in diverse unità di traduzione vengono richiamati, come sapete.

Ma la maggior parte delle implementazioni C++ in genere fornisce i mezzi per specificare l'ordine di inizializzazione del costruttore, che potrebbe essere in grado di utilizzare a proprio vantaggio.

Ad esempio, gcc ha l'attributo init_priority che è possibile allegare a un costruttore e controllare l'ordine di inizializzazione del costruttore rispetto ad altri costruttori. Questa sarebbe probabilmente la risposta nel caso di GCC.

Controllare la documentazione del compilatore per ulteriori informazioni sulle funzionalità specifiche del compilatore che offre in quest'area.

Per un approccio più portabile, potrebbe essere possibile fare qualcosa con il fatto che i POD vengono inizializzati prima di istanze di classe non banali, nell'ambito dello spazio dei nomi. Potrebbe essere possibile sfruttare questo, per avere qualche approccio qui, ma suggerirò di indagare sulle capacità del tuo compilatore, in primo luogo. Non c'è niente di sbagliato sfruttando le funzionalità aggiuntive offerte dal compilatore.

+0

Questo funziona bene, ma questo progetto è una libreria per gli altri a incorporare nel loro codice, e Non posso garantire il compilatore che useranno. La maggior parte di essi sarà basata su GCC, ma quale versione e cosa supporta è ipotizzata da chiunque. – Majenko

+0

L'ABI C++ dipende dal compilatore. Se crei la tua libreria con gcc, sarà utilizzabile in modo affidabile solo dalla stessa versione di gcc. La garanzia ABI binario di gcc è generalmente, ma non sempre, compatibile solo con le versioni successive. E, in ogni caso, è improbabile che qualsiasi libreria C++ creata con gcc possa essere collegata con il codice creato da un altro compilatore. –

+0

Mi fraintendimi. La "libreria" è distribuita come codice sorgente e compilata a croce dall'utente finale. Può essere compilato per un certo numero di diverse architetture utilizzando un numero di diversi ambienti di programmazione su un numero di diversi sistemi operativi. – Majenko

1

Se è possibile utilizzare C++11, si può anche provare ad organizzare la vostra pre-elaborazione in modo tale che può essere calcolata al momento della compilazione e rendere le definizioni di colore un constexpr:

class Color { 
    public: 
     int m_r, m_g, m_b; 
     constexpr Color(int r, int g, int b) : m_r(r), m_g(g), m_b(b) {} 
     constexpr Color(Color const& o) : m_r(o.m_r), m_g(o.m_g), m_b(o.m_b) {} 
}; 

constexpr const Color RED = Color(255,0,0); 
constexpr const Color BLUE = Color(0,255,0); 
constexpr const Color GREEN = Color(0,0,255); 

Questo dovrebbe guarrante che i colori vengono inizializzati prima che vengano chiamati altri costruttori (l'inizializzazione costante avviene prima dell'inizializzazione dinamica). Ha l'ulteriore vantaggio che la compilazione può eseguire la preelaborazione in fase di compilazione, quindi potrebbe anche essere un po 'più efficiente (tuttavia, ciò significa anche che non funzionerà se la pre-elaborazione dipende da valori disponibili solo durante l'esecuzione).

+0

Sfortunatamente questo è un progetto di libreria e non posso garantire che l'utente utilizzi un compilatore abbastanza moderno per C++ 11. – Majenko

3

Quello che si incontra viene talvolta chiamato "fiasco di inizializzazione statica".

Uno dei modi per gestirlo sarebbe utilizzare Costruisci al primo utilizzo Idiom, implementato per es. modificando le definizioni di colori alle soluzioni getter funzioni:

const Color & Snow(void) { 
    static Color snow(255,250,250); 
    return snow; 
} 

Si può leggere di più in this article.


Edit: per evitare eccessive quantità di codice, si può semplicemente definire una macro aiutante:

#define DEF_COLOR(name, r, g, b) \ 
const Color & name(void) { \ 
    static Color name(r,g,b); \ 
    return name; \ 
} 

DEF_COLOR(Snow,  255,250,250) 
DEF_COLOR(GhostWhite, 248,248,255) 
// ... 
+0

Mi piace molto questo metodo poiché non si basa su alcuna specifica funzione del compilatore, sebbene richieda molta programmazione aggiuntiva. Forse dovrei scrivere un piccolo script Perl per generarlo per me ... – Majenko

+0

@ Majenko Puoi facilmente definirlo come una macro e aggiungere nuovi colori come ad es. 'DEF_COLOR (name, r, g, b)' –

+1

Altrettanto facile far sì che Perl restituisca completamente le funzioni poiché probabilmente scriverei uno script Perl per produrre comunque le righe DEF_COLOR per l'inclusione. – Majenko

Problemi correlati