Domanda

Sto scrivendo un livello di astrazione in cima ad alcune API grafica (DirectX9 e DirectX11) e vorrei il vostro parere.

Tradizionalmente vorrei creare una classe di base per ogni concetto che voglio astratta.
Quindi, in tipico stile OO avrei ad esempio una classe Shader e 2 sottoclassi DX9Shader e DX11Shader.

Vorrei ripetere il processo per le texture, ecc ... e quando ho bisogno di creare un'istanza di loro ho una fabbrica di astratto che restituirà la sottoclasse appropriata in funzione della corrente API grafiche.
Dopo Raii, il puntatore restituito sarebbe stato incapsulato in uno std :: shared_ptr.

Fin qui tutto bene, ma nel mio caso ci sono alcuni problemi con questo approccio:

  1. ho bisogno di venire con un'interfaccia pubblica che incapsulano la funzionalità di entrambe le API (e altre API in futuro).
  2. La classe derivata sono memorizzati in DLL separate (una per DX9, uno per DX11, ecc ...) e con una shared_ptr a loro nel client è una maledizione: all'uscita DLL grafici sono scaricati e se il cliente ha ancora uno shared_ptr a uno degli oggetti grafici braccio , crash a causa di chiamare il codice da DLL a vuoto.

Questo mi ha spinto a ri-progettare il mio modo di fare le cose: Ho pensato che avrei potuto solo tornare puntatori prime alle risorse ed avere l'API grafica pulita dopo se stessa, ma c'è ancora la questione della penzoloni puntatori sul lato client e il problema di interfaccia. Ho anche considerato il conteggio manuale di riferimento come COM, ma ho pensato che sarebbe un passo indietro (correggetemi se sbaglio, provenienti dal mondo shared_ptr, il conteggio manuale di riferimento sembra primitivo).

Poi ho visto il lavoro di Humus in cui tutti i suoi corsi di grafica sono rappresentate da ID interi (molto simile a quello che fa OpenGL). La creazione di un nuovo oggetto restituisce solo il suo ID intero, e memorizza il puntatore internamente; è tutto perfettamente opaco!

Le classi che rappresentano l'astrazione (come DX9Shader ecc ...) sono tutti nascosti dietro le API dispositivo che è l'unica interfaccia.
Se si vuole impostare una texture, è solo una questione di chiamare dispositivo-> SetTexture (ID) e il resto succede dietro le quinte.

La caduta è che la parte nascosta delle API è gonfio, c'è un sacco di codice refusi necessario per farlo funzionare e io non sono un fan di un fai-da-tutte le classi.

Tutte le idee / pensieri?

È stato utile?

Soluzione

Si dice che il problema principale è che una DLL viene scaricata, pur avendo un puntatore ai suoi interni. Beh ... non fanno che . Si dispone di un'istanza di classe, che è soci sono implementato in quella DLL. Si tratta fondamentalmente di un Errore per quella DLL da scaricare finché esistono quelle istanze di classe.

È quindi bisogno di essere responsabili nel modo in cui si utilizza questa astrazione. Così come è necessario essere responsabile con il qualsiasi il codice si carica da una DLL: roba che viene dalla DLL deve essere pulita prima si scarica la DLL. Come si fa che è fino a voi. Si potrebbe avere un conteggio di riferimento interno che viene incrementato per ogni oggetto che restituisce la DLL e scaricare solo la DLL dopo che tutti gli oggetti di riferimento vanno via. O nulla, in realtà.

Dopo tutto, anche se si utilizzano questi numeri opachi o qualsiasi altra cosa, che cosa succede se si chiama una di quelle funzioni API su quel numero quando la DLL viene scaricata? Ops ... Quindi in realtà non si acquista alcuna protezione. Devi essere responsabile in entrambi i casi.

Gli aspetti negativi del metodo numero che non si può pensare di sono:

  • Ridotta capacità di sapere che cosa in realtà un oggetto è . chiamate API possono fallire perché avete passato un numero che in realtà non è un oggetto. O peggio, che cosa succede se si passa un oggetto Shader in una funzione che prende una consistenza? Forse stiamo parlando di una funzione che prende uno shader e una consistenza, e si dimentica accidentalmente l'ordine degli argomenti? Le regole del C ++ non avrebbe permesso che il codice di compilazione, anche se quelli erano i puntatori di oggetto. Ma con numeri interi? Va tutto bene; che ci si ottiene solo gli errori di runtime.

  • Prestazioni. Ogni chiamata API dovrà guardare questo numero in una tabella hash o qualcosa per ottenere un puntatore reale con cui lavorare. Se si tratta di una tabella hash (vale a dire: un array), allora è probabilmente abbastanza minore. Ma è ancora un riferimento indiretto. E dal momento che il vostro astrazione sembra molto basso livello, qualsiasi perdita di prestazioni a questo livello può davvero male in situazioni di performance-critical.

  • Mancanza di RAII e altri meccanismi di ambito. Certo, si potrebbe scrivere un oggetto shared_ptr-esque che avrebbe creato ed eliminarli. Ma non avrebbe dovuto farlo se si stesse utilizzando un puntatore reale.

Semplicemente non sembra utile.

Altri suggerimenti

Ha importanza? All'utente dell'oggetto, è solo una maniglia opaca. il suo tipo di implementazione effettiva non importa, fintanto che posso passare la maniglia per le funzioni API e farli fare roba con l'oggetto.

È possibile modificare l'implementazione di queste maniglie facilmente, in modo da rendere più tutto ciò che è più facile per voi oggi .

Basta dichiarare il tipo di maniglia come un typedef di un puntatore o sia un intero, e assicurarsi che tutto il codice client utilizza il nome typedef, quindi il codice del client non dipende dal tipo specifico che si è scelto di rappresentare i vostri maniglie.

Vai per la soluzione semplice ora, e se / quando si esegue in problemi perché quello era anche semplice, cambiarlo.

Per quanto riguarda il vostro p. 2:. Cliente è sempre scarica prima librerie

Ogni processo ha il suo albero delle dipendenze biblioteca, con exe come radice dell'albero, l'utente Dll a livelli intermedi, e librerie di sistema a basso livello. Processo viene caricato da basso ad alto livello, radice dell'albero (exe) viene caricato per ultimo. Processo viene scaricato a partire dalla radice, librerie di basso livello vengono scaricati scorso. Questo viene fatto per prevenire situazioni si sta parlando.

Naturalmente, se si carica / scaricamento librerie manualmente, questo ordine è cambiato, e l'utente è responsabile per mantenere i puntatori validi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top