2013-08-08 13 views
14

Jedną z (tak bardzo wielu) niefortunnych wad projektowych C++ jest to, że zasadniczo nie można oddzielić implementacji od interfejsu podczas korzystania z metaprogramowania szablonów.Ukrywanie szczegółów implementacji szablonu z Doxygen

całej mojej bibliotece mam rzeczy jak:

template <typename Ma, typename Mb> 
typename boost::enable_if_c< 
      detail::IsMatrix<Ma>::val and detail::IsMatrix<Mb>::val and 
      detail::MatrixDimensionCheck<Ma,Mb>::isStaticMatch, 
     bool>::type 
operator==(const Ma &a, const Mb &b) { 
    return detail::matrixEqual(a,b); 
} 

Jeśli jest nieczytelny, nie winię cię. Większość tego bałaganu polega po prostu na określeniu typu zwracanego, który ma być bool, jeśli argumenty są macierzami i dopasowanym wymiarem, i są niezdefiniowane, jeśli są czymś innym (w ten sposób polegają na SFINAE, aby uniemożliwić temu operatorowi ukrywanie innych ważnych rzeczy).

Odkąd istota, która jest zasadniczo statyczną funkcją sprawdzania typu , funkcja jest teraz wbudowana w sygnaturę mojej zwykłej funkcji C++, te odwzorowania implementacji pojawią się w wygenerowanej dokumentacji.

Nie chcę, aby użytkownik musiał to przeczytać. Wszystko, co muszą wiedzieć, to ta funkcja zwraca bool (co jest prawie niemożliwe do odczytania przez przeczytanie powyższego). W dokumentach mogę wyjaśnić zwięźle, w prostym języku angielskim, że ten operator akceptuje tylko matryce.

Czy istnieje sposób, aby przekonać Doxygen do renderowania tego typu bałaganu jako bool? (Przypuszczam, że nie ma sposobu, żeby to oczyścić bezpośrednio z kodu, ale pomysły są mile widziane, jeśli coś wymyślisz).

+0

Istnieją powody, dlaczego wolę odręczny dokumentację Over „automatycznie wygenerowanego” dokumentacji. Niemal zawsze spędzam więcej czasu na konfigurowaniu systemu dokumentacji/naprawieniu go, aby był akceptowalny, podczas gdy w międzyczasie mogłem już napisać całą dokumentację w faktycznym, ładnym formacie. – orlp

+7

@noccracker: Do czasu, gdy zmienisz cokolwiek, takie jak parametry, funkcje, itp., Bez aktualizacji dokumentacji. Potem staje się niezsynchronizowany i staje się gorszy niż bezużyteczny. Doxygen wspiera również dobrze napisaną dokumentację. –

+2

Może być powiązane z tym pytaniem (http://stackoverflow.com/questions/3435225/c-meta-programming-doxygen-documentation). – elemakil

Odpowiedz

0

Myślę, że to może działać dla Ciebie. Oczywiście jest to prostszy przykład niż twój, ale podstawowa idea użycia udokumentowanej funkcji szablonu bez enable_if wywoływania innej "ukrytej" funkcji, która nie jest udokumentowana, ale zapewnia SFINAE.

// Ignore this function in doxygen 
template <typename T> 
typename boost::enable_if<boost::is_unsigned<T>, bool>::type 
test_hidden(T t) { 
    return true; 
} 

template <typename T> 
typename boost::disable_if<boost::is_unsigned<T>, bool>::type 
test_hidden(T t) { 
    return false; 
} 

// Document this function 
template <typename T> 
bool test(T t) 
{ 
    return test_hidden(t); 
} 

int main() 
{ 
    unsigned int a = 1; 
    int b = 0; 

    std::cout << test(a) << std::endl; // true 
    std::cout << test(b) << std::endl; // false 

    return 0; 
} 
+0

SFINAE stosuje się tylko do sygnatur szablonów, a nie do szablonów, więc nie jest to równoważne. Kompilator nie wypróbuje innych szablonów, jeśli rozszerzenie treści szablonu jest nieprawidłowe; to po prostu błąd i zatrzymanie. – trbabb

+0

Zgadzam się, że ta metoda ma pewne ograniczenia. Jednak w moim przykładzie kompilator wypróbuje różne implementacje 'test_hidden', które w niektórych przypadkach mogą wystarczyć. Zaktualizowałem moją odpowiedź, aby to pokazać. – dunc123

2

Cóż, jedynym sposobem mogę osiągnąć to poprzez powielenie definicji funkcji raczej niż przy użyciu funkcji automatycznego z doxygen i za pomocą komendy @fn zamiast. Dla Twojego przykładu powinieneś zrobić coś podobnego do tego, co:

1

Co o:

#ifdef DOXYGEN 
    #define RETURN_TYPE(Test, Type1) Type1 
#else 
    #define RETURN_TYPE(Test, Type1) typename boost::enable_if_c< Test, Type1 >::type 
#endif 

template <typename Ma, typename Mb> 
RETURN_TYPE((detail::IsMatrix<Ma>::val 
     and detail::IsMatrix<Mb>::val 
     and detail::MatrixDimensionCheck<Ma,Mb>::isStaticMatch), bool) 
operator==(const Ma &a, const Mb &b) { return detail::matrixEqual(a,b); } 

IMHO, to nawet łatwiejsze do zrozumienia niż czytać & początkowego kodu C++. Proszę zwrócić uwagę na podwójny nawias w pierwszym argumencie makra, aby uniknąć kompilatora do podziału na przecinek w "Test". Możesz go pozbyć się, jeśli najpierw zmienisz typ zwracanego elementu (Typ1) i użyjesz zmiennej arg makro dla testu.

+1

Nie jestem fanem dokumentacji, która wymusza zmianę w kodzie. – Raffi

+1

Zazwyczaj się zgadzam, ale w takim przypadku makro rzeczywiście zyskuje widoczność. Znaczenie kodu jest wyraźniejsze w przypadku makra, niż bez. Fakt, że makro zmienia zachowanie po uruchomieniu przez Doxygen, jest dobrą korzyścią dla kodu (można to zrobić w skrypcie Doxygen, jeśli nie chcesz zanieczyszczać kodu) – xryl669

1

Ponowne przekonanie Doxygena, aby pokazać bool jako typ zwrotu: Jedynym sposobem, jaki znam, jest odpowiedź Raffiego, dodając, że prawdopodobnie chcesz ukryć faktyczną funkcję z Doxygen (kilka sposobów, aby to zrobić).

Re oczyszczania: To może wyglądać jak

template <typename Ma, typename Mb> 
typename bool_isEqual<Ma, Mb>::type 
operator==(const Ma &a, const Mb &b) 
... 

Gdzie bool_isEqual obudowuje wszystkie logiki typu szablonu i typedef s type do bool gdy wystarczające. (Nazwa bool_isEqual została wybrana, ponieważ zakłada się, że są to inne funkcje szablonu o podobnej strukturze, które zwracają bool, ale mają inne warunki.)

Jeśli odbywa się to konsekwentnie, prawdopodobnie jest dostatecznie czytelny.

1

znalazłem następujące metody, aby być bardzo jasne:

  1. W Doxyfile dodać predefiniowany = doxygen
  2. W kodzie źródłowym otaczają funkcje SFINAE z ///@cond .... ///@endcond
  3. umieścić prostą szablonie deklarację funkcji w źródle kod w obrębie #ifdef DOXYGEN, więc będzie on niewidoczny dla normalnej kompilacji. Przestrzegać:

    ///@cond 
    template <typename Ma, typename Mb> 
    typename boost::enable_if_c< 
        detail::IsMatrix<Ma>::val and detail::IsMatrix<Mb>::val and 
        detail::MatrixDimensionCheck<Ma,Mb>::isStaticMatch, 
        bool>::type 
    operator==(const Ma &a, const Mb &b) { 
        return detail::matrixEqual(a,b); 
    } 
    ///@endcond 
    #ifdef DOXYGEN 
        ///Documentation for your function... 
        template<typename Ma, typename> operator==(const Ma &a, const Mb &b); 
    #endif 
    
Powiązane problemy