2016-01-20 77 views
4

我正在使用boost :: python在C++代碼中添加一個python模塊。 C++項目是用doxygen記錄的。 我想創建一個爲Python模塊文檔,但我不知道怎麼不這樣是多餘的:如何避免C++和boost :: 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"); 
}; 

我在這裏說,在文檔字符串同樣的事情,Doxygen註釋。有任何想法嗎 ?

編輯: C++文檔不公開,python界面是C++的一個子集。

回答

2

我是代碼生成的粉絲,我相信這是一個合理的情況來部署它。

如果你在編寫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

感謝您的想法!我想我會在cmake中編寫一個腳本並將其添加到我的構建鏈中。 – cromod

1

5gon12eder的想法是提取doxygen註釋並將其替換爲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。在:

/** 
* @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"), 
     "@[email protected]"); 
}; 

在這種情況下,腳本將生成具有良好文檔字符串的pymodule.cpp。