2013-01-14 8 views
9

Nella mia applicazione, mi occupo di classi di dimensioni maggiori (oltre 50 metodi) ciascuna delle quali è ragionevolmente complessa. Non sono preoccupato per la complessità in quanto sono ancora semplici in termini di isolare parti di funzionalità in metodi più piccoli e quindi chiamarli. Questo è il modo in cui il numero di metodi diventa grande (molti di questi metodi sono privati, in particolare isolando parti di funzionalità).Trova metodi di classe non implementati

Tuttavia, quando arrivo alla fase di implementazione, scopro che ho perso traccia di quali metodi sono stati implementati e quali non sono stati implementati. Poi a livello di link ricevo errori per i metodi non implementati. Questo andrebbe bene, ma ci sono molte interdipendenze tra le classi e per collegare l'app avrei bisogno di avere TUTTO pronto. Tuttavia preferirei prendere una lezione prima di passare alla prossima.

Per ragioni indipendenti dalla mia volontà, non posso utilizzare un IDE - solo un editor di testo semplice e un compilatore g ++. C'è un modo per trovare metodi non implementati in una classe senza fare un collegamento completo? In questo momento faccio letteralmente ricerca di testo sulle firme dei metodi nel file cpp di implementazione per ciascuno dei metodi, ma questo richiede molto tempo.

+0

Che cosa ti impedisce semplicemente di tentare di collegare e grepping per il messaggio "riferimento non definito"? – Agentlien

+0

Sì, questo è un modo per farlo, ma ne otterrò molti che potrebbero non riguardare la classe in questione. Il progetto complessivo ha centinaia di classi e la compilazione dura circa 30 minuti. –

+0

Sospettavo che sarebbe stata la pura quantità a costituire un problema. Tuttavia, dovresti essere in grado di grep per "riferimento non definito a ClassInQuestion ::", per ottenere solo hit per i metodi da quella classe. (Farò una risposta affermando lo stesso) – Agentlien

risposta

0

Anche se non riesco a vedere un modo semplice per farlo senza effettivamente tentare di collegare, è possibile grep l'output del linker per "riferimento non definito a ClassInQuestion ::", che dovrebbe fornire solo le righe relative a questo errore per i metodi della classe data.

Questo almeno consente di evitare di passare in rassegna tutti i messaggi di errore dell'intero processo di collegamento, sebbene non impedisca il passaggio a un collegamento completo.

+0

Sì, dal punto di vista automatizzato questo sembra essere l'approccio più semplice. Il problema più grande ora sono i 30+ minuti che la compilazione richiede ... –

+0

@AleksG Questo è davvero un problema, e con quello in atto non riesco a trovare una soluzione adeguata oltre a scrivere uno strumento per confrontare i file .h e .cpp , cercando le dichiarazioni e le definizioni delle funzioni corrispondenti/mancanti. – Agentlien

-1

Non riesco a vedere un modo semplice per farlo. Avere diverse classi senza implementazione porterà facilmente a una situazione in cui tenere traccia in una squadra di più membri sarà un incubo.

Personalmente vorrei testare ogni classe che scrivo e lo sviluppo guidato da test è la mia raccomandazione. Tuttavia questo implica il collegamento del codice ogni volta che si desidera controllare lo stato. Per gli strumenti di utilizzo del TDD fare riferimento al collegamento here.

Un'altra opzione è scrivere un pezzo di codice che può analizzare tramite l'origine e verificare le funzionalità che devono essere implementate. GCC_XML è un buon punto di partenza.

+1

Per favore fammi sapere il motivo del voto negativo. Mi aiuterà a capire meglio. Grazie – Ram

+1

Ciò significherebbe in realtà l'implementazione di ogni funzione, che consentirebbe il collegamento, ma spostare il problema in fase di esecuzione.Questo non è sempre auspicabile in un grande progetto. Costringe il TDD, che potrebbe non essere una possibile transizione (poiché pone le richieste sul progetto nel suo complesso). – Agentlien

+0

Un'altra possibilità sulla stessa linea sarebbe quella di implementare inizialmente le funzioni come manichini con static_assert (false, "funzione non implementata") per quelle che devono essere implementate. – Agentlien

3

si potrebbe aggiungere uno stub per ogni metodo che si intende implementare e fare:

void SomeClass::someMethod() { 
    #error Not implemented 
} 

con GCC, questo genera file, il numero di linea e il messaggio di errore per ognuno di essi. Quindi potresti semplicemente compilare il modulo in questione e grep per "Non implementato", senza richiedere un'esecuzione del linker.

Sebbene sia ancora necessario aggiungere questi stub ai file di implementazione, che potrebbero essere parte di ciò che si stava tentando di eludere in primo luogo.

+0

Ma questo richiede ancora che io implementi tutti i metodi. Inoltre, devo essere in grado di compilare una classe non completa per verificare errori di sintassi, ecc. Prima che tutto sia pronto. –

+0

Bene, è vero. Sebbene tu possa aggiungere tutti gli stub e poi iniziare l'implementazione, avrai un modo semplice per vedere quali metodi sono ancora stub. Ma vedo come potrebbe non essere esattamente quello che stai cercando. –

+0

Forse potresti scrivere un breve script python 'add_method' o qualcosa, che inserisce lo stub precedente alla fine di un file cpp? Non dovrebbe essere troppo difficile, a meno che non si utilizzino diversi livelli di nidificazione dei namespace in file diversi. –

0

In passato ho costruito un eseguibile per ogni classe:

#include "klass.h" 
int main() { 
    Klass object; 
    return 0;   
} 

Questo riduce costruire tempo, può lasciare ci si concentra su una classe alla volta, accelera il vostro ciclo di feedback.

Può essere facilmente automatizzato.

Mi piacerebbe davvero ridurre le dimensioni di quella classe!

modificare

Se ci sono ostacoli, si può andare a forza bruta:

#include "klass.h" 

Klass createObject() { 
    return *reinterpret_cast<Klass>(0); 
}  

int main() { 
    Klass object = createObject(); 
    return 0;   
} 
+0

Ovviamente funziona solo per le classi con costruttori predefiniti (o almeno solo per quelli che non dipendono da altre classi), e non lo farà verificare che tutte le funzioni siano implementate. –

+0

È possibile automatizzarlo senza costruttori predefiniti. Includete i file oggetto necessari per collegarlo. Verifica che tutte le funzioni virtuali siano state implementate. Questo è lo schema dell'idea, non una piena implementazione. –

+0

Sarebbe bello se Klass non dipendesse da altre 50 classi da compilare. –

0

Questo è ciò che unit test e gli strumenti di copertura di test sono per: scrivere dei test minimi per tutte le funzioni di up-front. I test per le funzioni mancanti non si collegheranno. Il rapporto sulla copertura del test ti comunicherà se tutte le funzioni sono state visitate.

Ovviamente questo è solo un po 'di aiuto, non è assolutamente infallibile. La tua metodologia di sviluppo suona un po 'dubbia per me: lo sviluppo di classi uno per uno in isolamento non funziona in pratica: le classi che dipendono l'una dall'altra (e ricorda: ridurre le dipendenze!) Devono essere sviluppate in una certa misura. Non puoi sfornare un'implementazione completa per una classe e passare alla successiva, senza mai voltarti indietro.

+0

Non voglio "visitare" ogni funzione. Voglio, in fase di montaggio, di vedere ciò che non ho implementato. Per eseguire i test unitari, ho bisogno di compilare e collegare - e il punto della mia domanda era vedere se c'è un modo per trovare le implementazioni mancanti senza collegamento. –

+0

@Aleks Beh, certo che c'è. In un IDE. Cosa che non vuoi esplicitamente. E certo, ci sono altri strumenti per darti queste informazioni. La mia risposta li descrive. –

0

È possibile scrivere un piccolo script che analizza il file di intestazione per le implementazioni del metodo (le espressioni regolari lo rendono molto semplice), quindi esegue la scansione del file di implementazione per le stesse implementazioni del metodo.

Per esempio in Ruby (per un C++ unità di compilazione):

className = "" # Either hard-code or Regex /class \w+/ 
allMethods = [] 

# Scan header file for methods 
File.open(<headerFile>, "r") do |file| 
    allLines = file.map { |line| line } 
    allLines.each do |line| 
     if (line =~ /(\);)$/) # Finds lines ending in ");" (end of method decl.) 
      allMethods << line.strip! 
     end 
    end 
end 

implementedMethods = [] 
yetToImplement = [] 

# Scan implementation file for same methods 
File.open(<implementationFile>, "r") do |file| 
    contents = file.read 
    allMethods.each do |method| 
     if (contents.include?(method)) # Or (className + "::" + method) 
      implementedMethods << method 
     else 
      yetToImplement << method 
     end 
    end 
end 

# Print the results (may need to scroll the code window) 
print "Yet to implement:\n" 
yetToImplement.each do |method| 
    print (method + "\n") 
end 

print "\nAlready implemented:\n" 
implementedMethods.each do |method 
    print (method + "\n") 
end 

qualcun altro sarà in grado di dirvi come automatizzare questo nel processo di generazione, ma questo è un modo per controllare velocemente quali metodi non sono ancora stati implementati

0

La parola chiave di eliminazione di C++ 11 fa il trucco

struct S{ 
    void f()=delete; //unimplemented 
}; 

Se C++ 11 non è avaiable, è possibile utilizzare privata come una soluzione

struct S{ 
    private: //unimplemented 
    void f(); 
}; 

Con questo metodo di due, puoi scrivere del codice di prova in un file .cpp

//test_S.cpp 
#include "S.hpp" 
namespace{ 
    void test(){ 
    S* s; 
    s->f(); //will trigger a compilation error 
    } 
} 

Nota che il tuo codice di test non verrà mai eseguito. Lo spazio dei nomi {} dice al linker che questo codice non viene mai usato al di fuori dell'unità di compilazione corrente (cioè test_S.cpp) e verrà quindi eliminato subito dopo il controllo della compilazione.

Poiché questo codice non viene mai eseguito, non è necessario creare un oggetto S reale nella funzione di test. Vuoi solo ingannare il compilatore per verificare se un oggetto S ha una funzione callable f().