2013-01-14 15 views
9

W mojej aplikacji mam do czynienia z klasami o większych rozmiarach (ponad 50 metod), z których każda jest dość złożona. Nie martwię się złożonością, ponieważ są one nadal proste w zakresie izolowania elementów funkcjonalnych na mniejsze metody, a następnie wywoływania ich. W ten sposób liczba metod staje się duża (wiele z tych metod jest prywatna - szczególnie izolowanie elementów funkcjonalności).Znajdź niezaimplementowane metody klasy

Jednak kiedy dochodzę do etapu wdrożenia, stwierdzam, że tracę kontrolę nad tym, które metody zostały wdrożone, a które nie. Następnie na etapie łączenia otrzymuję błędy dla niezaimplementowanych metod. Byłoby to w porządku, ale między klasami istnieje wiele zależności i aby połączyć aplikację, muszę przygotować WSZYSTKO. Mimo to wolałbym zdobyć jedną klasę, zanim przejdziemy do następnej.

Z przyczyn niezależnych od mnie nie mogę używać IDE - tylko zwykły edytor tekstu i kompilator g ++. Czy istnieje sposób na znalezienie niezatwierdzonych metod w jednej klasie bez wykonywania pełnego łączenia? W tej chwili dosłownie wykonuję wyszukiwanie tekstowe na sygnaturach metod w pliku cpp implementacji dla każdej z metod, ale jest to bardzo czasochłonne.

+0

Co przeszkadza Ci po prostu w próbie połączenia i wyszukania komunikatu "niezdefiniowane odniesienie do"? – Agentlien

+0

Tak, to jeden ze sposobów, ale dostanę wiele z tych, które mogą nie odnosić się do danej klasy. Cały projekt ma setki klas, a jego skompilowanie zajmuje około 30 minut. –

+0

Podejrzewałem, że będzie to czysta ilość, która może stanowić problem. Jednak powinieneś być w stanie uzyskać polecenie "niezdefiniowane odwołanie do ClassInQuestion ::", aby uzyskać tylko trafienia dla metod z tej klasy. (Dam odpowiedź na to samo) – Agentlien

Odpowiedz

0

Chociaż nie widzę prostego sposobu, aby to zrobić bez faktycznego próbowania połączenia, można odsunąć wyjściowy linker na "niezdefiniowane odniesienie do ClassInQuestion ::", co powinno dać ci tylko linie związane z tym błędem dla metod danej klasy.

Pozwala to przynajmniej uniknąć przeszukiwania wszystkich komunikatów o błędach z całego procesu łączenia, ale nie wyklucza konieczności korzystania z pełnego łącza.

+0

Tak, z zautomatyzowanego punktu widzenia to najprostsze podejście. Największym problemem jest teraz 30+ minut, które kompilacja ma ... –

+0

@AleksG To jest rzeczywiście problem, a przy tym nie mogę znaleźć odpowiedniego rozwiązania poza pisaniem narzędzia do porównywania plików .h i .cpp , szukanie pasujących/brakujących deklaracji i definicji funkcji. – Agentlien

-1

Nie widzę łatwego sposobu na zrobienie tego. Posiadanie kilku klas bez implementacji łatwo doprowadzi do sytuacji, w której śledzenie w zespole z wieloma członkami będzie koszmarem.

Osobiście chciałbym przetestować każdą klasę, którą piszę, a rozwój oparty na testach jest moją rekomendacją. Jednak wiąże się to z łączeniem kodu za każdym razem, gdy chcesz sprawdzić status. Narzędzia do korzystania z TDD można znaleźć pod linkiem here.

Inną opcją jest napisanie fragmentu kodu, który może przeanalizować źródło i sprawdzić, czy funkcja ma zostać zaimplementowana. GCC_XML to dobry punkt wyjścia.

+1

Proszę podać przyczynę głosowania w dół. Pomoże mi lepiej zrozumieć. Dzięki – Ram

+1

Oznaczałoby to faktyczne wdrożenie każdej funkcji, która pozwoliłaby na połączenie, ale przeniesienie problemu na czas działania.Nie zawsze jest to pożądane w przypadku dużego projektu. Wymusza TDD, co może nie być możliwą zmianą (ponieważ stawia wymagania dla projektu jako całości). – Agentlien

+0

Inną możliwością w tym samym kierunku byłoby początkowe zaimplementowanie funkcji jako manekinów z static_assert (false, "unimplemented function") dla tych, które wymagały implementacji. – Agentlien

3

Można dodać zalążek dla każdej metody, którą zamierzasz realizować i zrobić:

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

z GCC, to wysyła plik, numer linii i komunikat błędu dla każdego z nich. Abyś mógł następnie skompilować dany moduł i grep dla "Nie zaimplementowany", bez konieczności uruchamiania łącznika.

Mimo to nadal musisz dodać te kody pośredniczące do plików implementacji, które mogą być częścią tego, co próbujesz omijać w pierwszej kolejności.

+0

Ale to nadal wymaga zastosowania wszystkich metod. Ponadto, zanim wszystko będzie gotowe, muszę mieć możliwość skompilowania nieskompletnej klasy, aby sprawdzić błędy składniowe. –

+0

Cóż, to prawda. Chociaż można dodać wszystkie kody pośredniczące, a następnie rozpocząć wdrażanie, a będziesz mieć łatwy sposób, aby zobaczyć, które metody są jeszcze stubs. Ale widzę, jak to może nie być dokładnie to, czego szukasz. –

+0

Może możesz napisać krótki skrypt python 'add_method' lub coś, co wstawia powyższy kod na końcu pliku cpp? Nie powinno być zbyt trudne, chyba że używasz różnych poziomów zagnieżdżania przestrzeni nazw w różnych plikach. –

0

W przeszłości zbudowali wykonywalny dla każdej klasy:

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

Redukuje to czas budowy, może pozwolić skupić się na jednej klasy w czasie, przyspiesza pętlę sprzężenia zwrotnego.

Można go łatwo zautomatyzować.

Naprawdę chciałbym jednak ograniczyć rozmiar tej klasy!

edit

Jeżeli istnieją przeszkody, można iść brutalnej siły:

#include "klass.h" 

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

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

Oczywiście działa to tylko dla klas z domyślnymi konstruktorami (lub przynajmniej tylko tymi, które nie zależą od innych klas), i nie będzie sprawdź, czy wszystkie funkcje są zaimplementowane. –

+0

Możesz zautomatyzować to bez domyślnych konstruktorów. Uwzględniasz pliki obiektów niezbędne do utworzenia łącza. Weryfikuje wszystkie funkcje wirtualne zostały zaimplementowane. To jest zarys idei, a nie pełna implementacja. –

+0

To byłoby miłe, gdyby Klass nie zależał od 50 innych klas do kompilacji. –

0

To właśnie testy jednostkowe i narzędzia pokrycia testy są do: napisać minimalne testy dla wszystkich funkcji z góry. Testy na brakujące funkcje nie będą się łączyć. Raport pokrycia testu powie, czy wszystkie funkcje zostały odwiedzone.

Oczywiście, to tylko w pewnym stopniu pomaga, nie jest to dowód na 100% głupi. Twoja metodologia rozwoju brzmi dla mnie nieco niepewnie: tworzenie klas jeden po drugim w izolacji nie działa w praktyce: klasy, które są od siebie zależne (i pamiętaj: zmniejsz zależności!) Muszą być w pewnym stopniu rozwijane w lockstepie. Nie możesz wymyślić kompletnej implementacji dla jednej klasy i przejść do następnej, nigdy się nie oglądając.

+0

Nie chcę "odwiedzać" każdej funkcji. Chcę, na etapie edycji, zobaczyć, co nie zaimplementowałem. Aby uruchomić testy jednostkowe, muszę skompilować i połączyć - i cały punkt mojego pytania polegał na sprawdzeniu, czy istnieje sposób na znalezienie brakujących implementacji bez łączenia. –

+0

@Aleks Cóż, nie ma. W IDE. Które wyraźnie nie chcesz. Na pewno są też inne narzędzia, które dostarczą ci tych informacji. Moja odpowiedź opisuje je. –

0

Można napisać mały skrypt, który analizuje plik nagłówkowy dla implementacji metod (wyrazy regularne sprawią, że jest to bardzo proste), a następnie skanuje plik implementacji dla tych samych implementacji metod.

Na przykład w Ruby (dla C++ jednostki kompilacji):

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 

Ktoś inny będzie w stanie powiedzieć, jak zautomatyzować ten do procesu kompilacji, ale to jest jeden sposób, aby szybko sprawdzić, które metody nie zostały jeszcze wdrożone.

0

delete kluczowe C++ 11 załatwia sprawę

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

Jeśli C++ 11 nie jest dostarczony, można użyć prywatny jako obejście

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

Z tych dwóch metod, możesz napisać kod testowy w pliku .cpp

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

Pamiętaj, że Twój kod testowy nigdy nie zostanie wykonany. Przestrzeń nazw {} mówi do linkera, że ​​ten kod nigdy nie jest używany poza bieżącą jednostką kompilacji (tj. Test_S.cpp) i dlatego zostanie usunięty zaraz po sprawdzeniu kompilacji.

Ponieważ ten kod nigdy nie jest wykonywany, nie trzeba tworzyć prawdziwego obiektu S w funkcji testu. Po prostu chcesz oszukać kompilatora, aby sprawdzić, czy obiekty S mają funkcję wywoływalną f().