2015-12-23 9 views
7

Immaginate di avere una classe C++ chiamata MyClass.Differimento della costruzione di oggetti statici C++ - GCC su Linux

Immagina di non avere accesso al codice sorgente di MyClass ... è contenuto in una libreria e vengono forniti solo la libreria e il file di intestazione per MyClass.

Immaginate che la classe stessa richieda la pre-configurazione dell'ambiente ... per esempio ... prima che il costruttore della classe possa essere chiamato, ho bisogno di fare qualche setup. La classe è normalmente pensato per essere utilizzato come segue:

void func() { 
    doGlobalSetup(); 
    MyClass myInstance(1,2,3); 
    myInstance.doSomething(); 
    ... 
} 

Ora ho la situazione in cui abbiamo bisogno di creare un'istanza globale della classe come ad esempio:

MyClass myInstance(1,2,3); 

int main(int argc, char *argv[]) { 
    doGlobalSetup(); 
    myInstance.doSomething(); 
} 

Il problema è che in questo storia, l'istanza di MyClass viene creata prima della la chiamata a doGlobalSetup(). Viene istanziata prima che venga chiamato main(). Quello che voglio fare è differire la creazione di myInstance() fino a più tardi o essere in grado di eseguire doGlobalSetup() in qualche modo prima dell'istanza della classe.

questa è una semplificazione della storia reale ... quindi supponiamo:

  1. Non posso cambiare la struttura interna di MyClass.
  2. Deve essere presente una variabile di istanza denominata myInstance di tipo MyClass (Impossibile modificare la logica su MyClass *pMyInstance).

Mille grazie per la lettura.

+0

È possibile copiare 'MyClass'? Correlati, considererei davvero se si desidera creare un'istanza globale ... Tuttavia, se è possibile copiare, penso che ci sia una soluzione facile. –

+1

Si prega di smettere di usare 'void main', ISO C++ permette' int main() 'o' int main (int, char **) 'solo – Danh

+0

@Danh - Scusa Danh ... doveva essere uno pseudo codice in contrapposizione a una vera app. Tuttavia ho cambiato il testo come richiesto. – Kolban

risposta

0

All'interno dell'ambiente compilatore GCC c'è una capacità di attributo di funzione chiamata constructor. Ciò consente di contrassegnare una definizione di funzione con la possibilità che venga invocata automaticamente prima che venga chiamato il numero principale e, soprattutto, prima del.

Facendo nuovamente riferimento alla definizione problema originale ... se la funzione doGlobalSetup() viene modificata da:

void doGlobalSetup() { ... } 

a

__attribute__((constructor)) void doGlobalSetup() { ... } 

quindi la sua invocazione si verificherà prima principale si chiama e anche prima che vengano chiamati tutti i costruttori di istanze di classi statiche. Anche la chiamata esplicita a questa funzione verrebbe rimossa da main() poiché il suo lavoro è stato eseguito implicitamente.

3

soddisfa le tue esigenze?

namespace 
{ 
    int doStaticGlobalSetup() 
    { 
     doGlobalSetup(); 
     return 0; 
    } 
} 
MyClass myInstance(doStaticGlobalSetup() + 1,2,3); 

int main() { 
    myInstance.doSomething(); 
    return 0; 
} 
+0

Corretto, creativo e straordinariamente hackish. Hai il mio upvote. –

5

Poiché il problema è stato limitato in modo che non sia possibile utilizzare new. Dovresti essere in grado di creare l'oggetto come sempre e copiarlo nell'istanza globale. Per esempio:

MyClass createMyClass() 
{ 
    doGlobalSetup(); 
    return MyClass(1, 2, 3); 
} 

MyClass myInstance = createMyClass(); 

int main() 
{ 
    myInstance.doSomething(); 

    return 0; 
} 
+0

Questo presuppone che 'MyClass' abbia un costruttore di copia ... – Arkadiy

+0

@Arkadiy Sì, presuppone che' MyClass' sia copiabile e che la domanda non elenchino un vincolo che indica altrimenti. Ho anche chiesto all'OP di questo, ma non ho avuto risposta. –

1

Se dovete assolutamente rinviare qualsiasi costruzione chiama fino a dopo l'inizializzazione globale è fatto, e vogliono essere sicuri che non statico ordine di inizializzazione fiasco accade, c'è un modo: fare myInstance un riferimento al blocco di memoria non inizializzato e creare oggetti in esso utilizzando il posizionamento nuovo dopo l'inizializzazione globale.

#include <iostream> 
#include <type_traits> 

struct foo 
{ 
    foo() { std::cout << "created\n"; } 
    void meow() { std::cout << "used\n"; } 
    ~foo() { std::cout << "destroyed\n"; } 
}; 
void doGlobalSetup() { std::cout << "Global setup\n"; } 


//Actual implementation 
namespace { 
    typename std::aligned_storage<sizeof(foo), alignof(foo)>::type bar; 
} 
foo& instance = reinterpret_cast<foo&>(bar); 

//Allows automatic creation and destruction 
struct initializer 
{ 
    initializer() 
    { 
     if (!initialized) 
      new (&instance) foo(); 
     initialized = true; 
    } 
    ~initializer() 
    { 
     if(initialized) 
      instance.~foo(); 
     initialized = false; 
    } 
    private: 
     static bool initialized; 
}; 
bool initializer::initialized = false; 

int main() 
{ 
    doGlobalSetup(); 
    initializer _; 
    instance.meow(); 
} 
+0

Non sono sicuro che risponda alla domanda. Lo fa? – YSC

+2

@ YSC Perché no: il posizionamento di nuovi suoni è una grande idea, soprattutto se l'oggetto non è copiabile. –

+0

Ho capito. All'inizio non era ovvio come la nuova posizione abbia risposto alla domanda. Lo fa. – YSC

2

Utilizzare una variabile statica all'interno di una funzione.

MyClass &myInstance() { 
    doGlobalSetup(); 
    static MyClass myInstance(1,2,3); 
    return myInstance; 
} 

void func() { 
    myInstance().doSomething(); 
} 
1

Probabilmente hai già la risposta che volevi. Ma solo per coprire l'intero spettro: se, per qualche ragione, vuoi assicurarti che altri posti nel codice non costruiscano accidentalmente MyClass indipendentemente dalla tua variabile globale - e prima che la configurazione globale sia stata fatta - è necessario per risolvere questo con il collegamento.

Se sei su Linux, puoi LD_PRELOAD un oggetto condiviso contenente solo il simbolo per il costruttore di MyClass.In esso dichiari la funzione di configurazione di conseguenza e lascia che il linker dinamico faccia il lavoro per te. Quindi, all'interno del costruttore, si richiama la funzione di configurazione, quindi si esegue uno dlsym("...", RTLD_NEXT) per ottenere il puntatore al costruttore originale e lo si chiama, passandogli gli argomenti ottenuti. Naturalmente, si mantiene e si controlla un flag statico per verificare se l'installazione è già stata eseguita.

Ancora una volta, questo è probabilmente eccessivo per voi, ma lo sto postando nel caso qualcuno abbia bisogno (ed è in grado di usare) questo tipo di soluzione.

P.S. Questo è ciò che ottieni quando ti affidi allo stato globale! :)

+0

Errr ... NO questo è ESATTAMENTE quello che stavo cercando. Nel mio rompicapo del mondo reale, mi capita di non avere il controllo su effettivamente chiamare doGlobalSetup(). Immagina di lavorare in un framework in cui devo scrivere una funzione C++ con un nome ben noto. Nella mia funzione C++, ho l'istanza di classe stato globale.Quando collego la mia funzione C++ con il framework, l'oggetto di classe globale è intantiated prima che il framework sia pronto e prima che il mio collegamento nella funzione di codice utente venga dato il controllo. – Kolban

0

Innanzitutto, tenere presente che data una funzione di inizializzazione della libreria come doGlobalSetup, esiste una netta possibilità diversa per il fatto che la libreria non funzioni semplicemente se si crea un'istanza globale.

altrimenti è super facile per creare un inizializzatore con l'operatore virgola:

bool do_my_setup = (doGlobalSetup(), true); 
MyClass myInstance(1,2,3); 
Problemi correlati