2013-08-08 2 views
14

Одним из (так много) неудачных недостатков дизайна C++ является то, что в принципе невозможно отделить реализацию от интерфейса при использовании метапрограммирования шаблонов.Скрыть детали реализации шаблона от Doxygen

Все над моей библиотекой у меня есть такие вещи, как:

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); 
} 

Если это нечитаемое, я не виню тебя. Большая часть этого беспорядка просто определяет тип возвращаемого значения как bool, если аргументы являются матрицами и соответствующими размерами, и не определены, если они что-то другое (таким образом, полагаясь на SFINAE, чтобы этот оператор не скрывал другие важные вещи).

Так как кинематика по существу статической проверки типов функция теперь встроена в подпись моей обычной функции C++, эти реализационные кишки появятся в сгенерированной документации.

Я не хочу, чтобы пользователь должен был прочитать это. Все, что им нужно знать, это то, что эта функция возвращает bool (что почти невозможно сказать, прочитав выше). В документах я могу кратко, на простом английском языке, объяснить, что этот оператор принимает только матрицы.

Есть ли способ уговорить Doxygen сделать этот беспорядок типа bool? (Я предполагаю, что более или менее нет возможности очистить это в коде напрямую, но идеи приветствуются, если вы можете что-то придумать).

+0

Есть причины, почему я предпочитаю рукописную документацию по «автоматически генерируется» документации. Я почти всегда трачу больше времени на создание системы документации/исправление ее приемлемости, в то время как я мог бы написать всю документацию в действительно хорошем формате. – orlp

+7

@nightcracker: до тех пор, пока вы ничего не измените, например параметры, функции и т. Д., Не обновляя документацию. Затем он выходит из строя и становится хуже, чем бесполезно. Кроме того, Doxygen поддерживает рукописную документацию просто отлично. –

+2

Возможно, это вопрос [this] (http://stackoverflow.com/questions/3435225/c-meta-programming-doxygen-documentation). – elemakil

ответ

0

Я думаю, что это может сработать для вас. Очевидность - это более простой пример, чем ваш, но основная идея использования документированной функции шаблона без enable_if для вызова другой «скрытой» функции, которая не документирована, но предоставляет 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 применяется только к шаблонам, а не к шаблонам, поэтому это не эквивалентно. Компилятор не будет использовать другие шаблоны, если расширение тела шаблона недействительно; это просто ошибка и остановка. – trbabb

+0

Я согласен, что этот метод имеет некоторые ограничения. Однако в моем примере компилятор попробует различные реализации 'test_hidden', чего может быть достаточно в некоторых случаях. Я обновил свой ответ, чтобы показать это. – dunc123

2

Ну, единственный способ, я могу добиться этого путем дублирования определение функции, а не использовать функцию автоматического из Doxygen, и с помощью команды @fn вместо этого. Для вашего примера что-то вроде

/*[email protected] template <typename Ma, typename Mb> bool operator==(const Ma &a, const Mb &b) 
* @brief My equality operator 
* @note The operator is available if the types @c Ma and @c Mb match. 
*  It will be discarded otherwise 
*/ 
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); 
} 

должно делать.

1

насчет:

#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); } 

ИМХО, это даже легче читать & понять, чем исходный код C++. Обратите внимание на двойную скобку в первом аргументе макроса, чтобы компилятор не разбивал запятую в «Тесте». Вы можете избавиться от него, если сначала измените порядок возвращаемого типа (Type1) и используйте переменную arg macro для теста.

+1

Я не поклонник документации, которая меняет код. – Raffi

+1

Обычно я согласен, но в этом случае макрос фактически набирает видимость. Смысл кода более ясен с помощью макроса, чем без него. Тот факт, что макрос изменяет поведение при работе Doxygen, является хорошим преимуществом для кода (это можно сделать в сценарии Doxygen, если вы не хотите загрязнять свой код) – xryl669

1

Re убедить Doxygen показать bool как возвращаемый тип: Единственный способ, которым я знаю, - это ответ Раффи, добавив, что вы, вероятно, захотите скрыть действительную функцию от Doxygen (несколько способов сделать это).

Re очистки: Это может выглядеть как

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

Где bool_isEqual инкапсулирует всю логику типа шаблона и typedef s type к bool при адекватной. (Имя bool_isEqual выбрано потому, что предполагается, что другие функции шаблона имеют аналогичную структуру, которые возвращают bool, но имеют другие условия.)

Если это делается последовательно, это, вероятно, достаточно читаемо.

1

я нашел следующий метод очень ясно:

  1. В Doxyfile добавить предопределенный = Doxygen
  2. В исходном коде окружать функции SFINAE с ///@cond .... ///@endcond
  3. Put простого шаблонного объявления функции в источнике код в пределах #ifdef DOXYGEN, поэтому он будет невидим для обычной компиляции. Обратите внимание:

    ///@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 
    
Смежные вопросы