Документация по кислородному программированию на языке С++
Я документирую код, который сильно использует метапрограммирование, например:
template<rysq::type A, rysq::type B, rysq::type C, rysq::type D, class Transform>
struct Kernel<meta::braket<A,B,C,D>, Transform,
typename boost::enable_if<
quadrature<meta::braket<A,B,C,D>, Transform> >::type>
: Eri <Transform> {
что такое хороший способ документировать такую конструкцию, используя doxygen?
Ответы
Ответ 1
Использовать макросы препроцессора. Вот пример из еще не официальной библиотеки Boost.XInt (в настоящее время поставлен в очередь для просмотра для включения в Boost):
#ifdef BOOST_XINT_DOXYGEN_IGNORE
// The documentation should see a simplified version of the template
// parameters.
#define BOOST_XINT_INITIAL_APARAMS ...
#define BOOST_XINT_CLASS_APARAMS ...
#define BOOST_XINT_CLASS_BPARAMS other
#define BOOST_XINT_APARAMS ...
#define BOOST_XINT_BPARAMS other
#else
#define BOOST_XINT_INITIAL_APARAMS \
class A0 = parameter::void_, \
class A1 = parameter::void_, \
class A2 = parameter::void_, \
class A3 = parameter::void_, \
class A4 = parameter::void_, \
class A5 = parameter::void_
#define BOOST_XINT_CLASS_APARAMS class A0, class A1, class A2, class A3, \
class A4, class A5
#define BOOST_XINT_APARAMS A0, A1, A2, A3, A4, A5
#define BOOST_XINT_CLASS_BPARAMS class B0, class B1, class B2, class B3, \
class B4, class B5
#define BOOST_XINT_BPARAMS B0, B1, B2, B3, B4, B5
#endif
Используйте имена макросов #define
d вместо параметров шаблона, везде, где они вам нужны, например:
/*! \brief The integer_t class template.
This class implements the standard aribitrary-length %integer type.
[...lots more documentation omitted...]
*/
template<BOOST_XINT_INITIAL_APARAMS>
class integer_t: virtual public detail::integer_t_data<BOOST_XINT_APARAMS>,
public detail::nan_functions<detail::integer_t_data<BOOST_XINT_APARAMS>::
NothrowType::value, // ...lots more base classes omitted...
{
// ...etcetera
И поместите такие строки в Doxyfile:
PREDEFINED = BOOST_XINT_DOXYGEN_IGNORE
EXPAND_AS_DEFINED = BOOST_XINT_INITIAL_APARAMS \
BOOST_XINT_CLASS_APARAMS \
BOOST_XINT_CLASS_BPARAMS \
BOOST_XINT_APARAMS \
BOOST_XINT_BPARAMS
В результате Doxygen видит либо "...", либо "другое" для параметров шаблона, а компилятор видит реальные. Если вы описываете параметры шаблона в документации для самого класса, то пользователю библиотеки нужно будет только увидеть их в одном месте, которое, вероятно, будет искать их; они будут скрыты везде.
В качестве дополнительного преимущества для этого проекта, если вам когда-либо понадобится внести изменения в списки параметров шаблона, вам нужно только изменить их в определениях макросов и функциях, которые фактически используют измененные параметры. Все остальное автоматически адаптируется.
Ответ 2
Вот мой пример:
///
/// \defgroup Kernel Kernel
///
/// \brief Kernel does this and that
/// \{
/// \brief Kernel template class brief description.
template<Braket,Transform,Boolean>
struct Kernel
{};
/// \brief Kernel partial template specialization brief description.
///
/// More detailed description...<br>
/// Partially specializes Kernel with meta::braket<A,B,C,D\>.<br>
/// If quadrature<meta::braket<A,B,C,D\>, Transform\> is true then enable
/// this algorithm, otherwise enable this other algorithm.<br>
/// Inherits privately from template class Eri<Transform\><br>
/// \tparam A template parameter A of type rysq::type, documentation and concepts
/// \tparam B template parameter B of type rysq::type, documentation and concepts
/// \tparam C template parameter C of type rysq::type, documentation and concepts
/// \tparam D template parameter D of type rysq::type, documentation and concepts
/// \tparam Transform template parameter class Transform documentation and concepts
/// \see Kernel\<Braket,Transform,Boolean\>
/// \see Eri
/// \see meta::braket
/// \see quadrature
#ifdef DOXY
// This is the documentation version
template<A,B,C,D,Transform>
struct Kernel<Braket,Transform,Boolean>
#else
// This is what gets compiled
template<rysq::type A, rysq::type B, rysq::type C, rysq::type D, class Transform>
struct Kernel<meta::braket<A,B,C,D>, Transform,typename boost::enable_if<quadrature<meta::braket<A,B,C,D>, Transform> >::type>
#endif
: Eri <Transform> {};
/// \}
Не забудьте добавить DOXY в раздел PREDEFINED препроцессора Doxygen.
Обычно я предпочитаю скрывать детали реализации от пользователя моего кода, поэтому я изменяю то, что видит Doxygen.
В этом случае вы найдете все свои специализации под одной группой, группой Kernel и в списке классов, все специализации будут сгруппированы вместе и не будут иметь очень длинное и непонятное имя.
Надеюсь, что это поможет.
Ответ 3
Мне не нравится решение с дополнительными макросами/кодом, например свиньи.
Вот мое решение, основанное на пост-обработке HTML-страниц, созданных Doxygen.
Это python script, который работает под Linux. Он подавляет все "<... > " на страницах справки по шаблонам и структурам ссылок, а также в их "Список всех страниц участников".
Маленький и менее инвазивный "шаблон <... > " перед каждым документированным методом остается.
Запустите script с каталогом "html/" (где была сгенерирована документация) в качестве аргумента.
#!/usr/bin/python
import subprocess
import sys
import re
def processFile( fileName):
f = open( fileName, 'r')
content = f.read();
f.close();
regex = re.compile( "(<.*>).*(Template Reference|Member List)")
match = re.search( regex, content)
if not match:
return
toRemove = match.group(1)
regex = re.compile(toRemove)
content = re.sub( regex, "", content)
f = open( fileName, 'w')
f.write( content)
f.close()
path = sys.argv[1]
finder = subprocess.Popen(["find", path, "-name", "class*.html", "-o", "-name", "struct*.html"], stdout = subprocess.PIPE)
(files, junk) = finder.communicate()
files = files.splitlines()
print files
for fname in files:
if fname == "":
continue
processFile(fname)
Ответ 4
Мета-программирование, похоже, реализует математику. Я напишу описывающие математические формулы с латексными экранами в документации doxygen.