Как избежать избыточности между C ++ и Boost :: Python Docs?

Я добавляю модуль Python в код C ++, используя boost :: python. Проект c ++ документирован с помощью doxygen.
Я хочу создать документацию для модуля Python, но я не знаю, как не быть избыточным, как это:

#include <boost/python.hpp>
using namespace boost::python;

/** @brief Sum two integers
* @param a an integer
* @param b another integer
* @return sum of integers
*/
int sum(int a, int b)
{
return a+b;
}

BOOST_PYTHON_MODULE(pymodule)
{
def("sum",&sum,args("a","b"),
"Sum two integers.\n\n:param a: an integer\n:param b: another integer\n:returns: sum of integers");
};

Здесь я говорю то же самое в комментариях к документам и документам. Есть идеи ?

Редактировать: Документ c ++ не является общедоступным, а интерфейс python является подмножеством c ++.

4

Решение

Я фанат генерации кода и считаю, что это разумная ситуация для его развертывания.

Если вы немного дисциплинированы в написании Doоков DocStrings и воздерживаетесь от сложной разметки в них, нетрудно написать небольшой анализатор, который извлекает их и подставляет обратно в Python DocStrings.

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

Поместите специальный комментарий перед каждой строкой Doxygen DocString, которая дает имя следующему блоку комментариев. Здесь я использую синтаксис

// DocString: sum
/**
* @brief Sum two integers
* @param a an integer
* @param b another integer
* @return sum of integers
*
*/
int sum(int a, int b);

связать имя sum со следующей DocString.

Затем поместите еще одну специальную строку в привязки Python, которая ссылается на это имя. Я использую следующий синтаксис здесь.

BOOST_PYTHON_MODULE(pymodule)
{
def("sum",&sum,args("a","b"), "@DocString(sum)");
};

Теперь нам нужен инструмент для извлечения Doоков DocStrings и замены их в привязках Python.

Как я уже сказал, этот пример придуман, но он должен показать идею и продемонстрировать, что это не так сложно сделать.

import re
import sys

def parse_doc_string(istr):
pattern = re.compile(r'@(\w+)\s+(.*)')
docstring = list()
for line in map(lambda s : s.strip(), istr):
if line == '/**':
continue
if line == '*/':
return docstring
line = line.lstrip('* ')
match = pattern.match(line)
if match:
docstring.append((match.group(1), match.group(2)))

def extract(istr, docstrings):
pattern = re.compile(r'^//\s*DocString:\s*(\w+)$')
for line in map(lambda s : s.strip(), istr):
match = pattern.match(line)
if match:
token = match.group(1)
docstrings[token] = parse_doc_string(istr)

def format_doc_string(docstring):
return '\n'.join('{}: {}'.format(k, v) for (k, v) in docstring)

def escape(string):
return string.replace('\n', r'\n')

def substitute(istr, ostr, docstrings):
pattern = re.compile(r'@DocString\((\w+)\)')
for line in map(lambda s : s.rstrip(), istr):
for match in pattern.finditer(line):
token = match.group(1)
docstring = format_doc_string(docstrings[token])
line = line.replace(match.group(0), escape(docstring))
print(line, file=ostr)

if __name__ == '__main__':
sourcefile = sys.argv[1]
docstrings = dict()
with open(sourcefile) as istr:
extract(istr, docstrings)
with open(sourcefile) as istr:
with sys.stdout as ostr:
substitute(istr, ostr, docstrings)

Запуск этого сценария над исходным файлом приводит к следующему выводу.

#include <boost/python.hpp>
using namespace boost::python;

// DocString: sum
/**
* @brief Sum two integers
* @param a an integer
* @param b another integer
* @return sum of integers
*
*/
int sum(int a, int b)
{
return a+b;
}

BOOST_PYTHON_MODULE(pymodule)
{
def("sum",&sum,args("a","b"), "brief: Sum two integers\nparam: a an integer\nparam: b another integer\nreturn: sum of integers");
};

Добавьте два часа полировки к сценарию, и все готово.

Так как это может быть интересно и другим людям, я не удивлюсь, если кто-то уже написал такой сценарий. И если нет, то публикация вашего как свободного программного обеспечения, безусловно, будет приветствоваться другими.

3

Другие решения

Идея 5gon12eder состоит в том, чтобы извлекать комментарии о кислороде и подставлять их в строки документации Python. Он предложил решение с помощью скрипта Python.

Вот еще один со сценарием CMake, потому что я использую его для создания своего проекта. Я надеюсь, что это может помочь людям с той же проблемой:

set(FUNCTION "sum")
file(READ "pymodule.cpp.in" CONTENTS)

# To find the line with the flag
string(REGEX REPLACE "\n" ";" CONTENTS "${CONTENTS}")
list(FIND CONTENTS "// Docstring_${FUNCTION}" INDEX)

# To extract doxygen comments
math(EXPR INDEX "${INDEX}+1")
list(GET CONTENTS ${INDEX} LINE)
while(${LINE} MATCHES "@([a-z]+) (.*)")
string(REGEX MATCH "@([a-z]+) (.*)" LINE "${LINE}")
set(DOXY_COMMENTS ${DOXY_COMMENTS} ${LINE})
math(EXPR INDEX "${INDEX}+1")
list(GET CONTENTS ${INDEX} LINE)
endwhile()

# To convert doxygen comments into docstrings
foreach(LINE ${DOXY_COMMENTS})
string(REGEX REPLACE "@brief " "" LINE "${LINE}")
if("${LINE}" MATCHES "@param ([a-zA-Z0-9_]+) (.*)")
set(LINE ":param ${CMAKE_MATCH_1}: ${CMAKE_MATCH_2}")
endif()
if ("${LINE}" MATCHES "@return (.+)")
set(LINE ":returns: ${CMAKE_MATCH_1}")
endif()
set(DOCSTRING ${DOCSTRING} ${LINE})
endforeach()
string(REPLACE ";" "\\n" DOCSTRING "${DOCSTRING}")

# To insert docstrings in cpp file
set(Docstring_${FUNCTION} ${DOCSTRING})
configure_file("pymodule.cpp.in" "pymodule.cpp" @ONLY)

pymodule.cpp.in:

/**
* @file pymodule.cpp
*/

#include<boost/python.hpp>
using namespace boost::python;

// Docstring_sum
/** @brief Sum two integers
* @param a an integer
* @param b another integer
* @return sum of integers
*/
int sum(int a, int b) {
return a+b;
}

BOOST_PYTHON_MODULE(pymodule){
def("sum",&sum,args("a","b"),
"@Docstring_sum@");
};

В этом случае скрипт сгенерирует pymodule.cpp с хорошей строкой документации.

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector