Скрыть детали реализации шаблонов от 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, если аргументы являются матрицами и соответствием размерности, и undefined, если они что-то другое (таким образом, полагаясь на SFINAE, чтобы этот оператор не скрывал другие важные вещи).

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

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

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

Ответы

Ответ 1

Ну, единственный способ добиться этого - дублировать определение функции, а не использовать автоматическую функцию 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);
 }

.

Ответ 2

Как насчет:

#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, это еще проще читать и понимать, чем исходный код на С++. Обратите внимание на двойную скобку в первом аргументе макроса, чтобы компилятор не разбивал запятую в "Тесте". Вы можете избавиться от него, если сначала измените порядок возвращаемого типа (Type1) и используйте переменную arg macro для теста.

Ответ 3

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

Повторная очистка: это может выглядеть примерно так:

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

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

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

Ответ 4

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

  • В Doxyfile добавить PREDEFINED = DOXYGEN
  • В исходном коде функции объемного звучания SFINAE с помощью ///@cond .... ///@endcond
  • Поместите простое выражение о шаблонных функциях в исходный код в #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
    

Ответ 5

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