如何避免c ++和boost :: python docs之间的冗余?

时间:2016-01-20 09:26:36

标签: c++ doxygen boost-python docstring

我正在使用boost :: python在c ++代码中添加一个python模块。使用doxygen记录了c ++项目。 我想为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");
};

这里我在docstring和doxygen评论中说同样的话。有什么想法吗?

编辑: c ++ doc不是公共的,python接口是c ++的子集。

2 个答案:

答案 0 :(得分:3)

我是代码生成的粉丝,我相信这是部署它的合理情况。

如果你在编写Doxygen 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)");
};

现在我们需要一个工具来提取Doxygen 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");
};

在脚本中添加两个小时的抛光,你就可以了。

由于这也可能引起其他人的兴趣,如果有人写过这样的剧本,我也不会感到惊讶。如果没有,将您的自己软件发布为免费软件肯定会受到其他人的欢迎。

答案 1 :(得分:1)

5gon12eder的想法是提取doxygen注释并将它们替换为python docstrings。他提出了一个带有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。