C++根据.h文件批量生成需要的函数框架

类似VS中添加类 A的方法 int abc();

会在对应的实现文件.cpp中自动生成,

int A::abc() {

}

初学python,尝试写了一个脚本,自动根据写好的.h文件完成这一工作,也支持模板类,不过没考虑太多模板可能会有bug。

也可能会有很多其他的bug,不过用了下,感觉还可以,有错误再改正:)


用法

比如有一个abc.h,对应的实现文件abc.cc

1.新建一个abc.cc文件

  touch abc.cc

2.运行脚本

  ./prodef.py  abc.h abc.cc

   因为我默认是.cc文件,也可

 ./prodef.py  abc.h

  


用google 开源的代码做了一个实验,

 我们要处理的gtest-test-part.h如下

// Copyright 2008, Google Inc.

// All rights reserved.


#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_

#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_


#include <iosfwd>

#include <gtest/internal/gtest-internal.h>

#include <gtest/internal/gtest-string.h>


namespace testing {


// The possible outcomes of a test part (i.e. an assertion or an

// explicit SUCCEED(), FAIL(), or ADD_FAILURE()).

enum TestPartResultType {

  TPRT_SUCCESS,           // Succeeded.

  TPRT_NONFATAL_FAILURE,  // Failed but the test can continue.

  TPRT_FATAL_FAILURE      // Failed and the test should be terminated.

};


// A copyable object representing the result of a test part (i.e. an

// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()).

//

// Don't inherit from TestPartResult as its destructor is not virtual.

class TestPartResult {

 public:

  // C'tor.  TestPartResult does NOT have a default constructor.

  // Always use this constructor (with parameters) to create a

  // TestPartResult object.

  TestPartResult(TestPartResultType type,

                 const char* file_name,

                 int line_number,

                 const char* message)

      : type_(type),

        file_name_(file_name),

        line_number_(line_number),

        summary_(ExtractSummary(message)),

        message_(message) {

  }


  // Gets the outcome of the test part.

  TestPartResultType type() const { return type_; }


  // Gets the name of the source file where the test part took place, or

  // NULL if it's unknown.

  const char* file_name() const { return file_name_.c_str(); }


  // Gets the line in the source file where the test part took place,

  // or -1 if it's unknown.

  int line_number() const { return line_number_; }


  // Gets the summary of the failure message.

  const char* summary() const { return summary_.c_str(); }


  // Gets the message associated with the test part.

  const char* message() const { return message_.c_str(); }


  // Returns true iff the test part passed.

  bool passed() const { return type_ == TPRT_SUCCESS; }


  // Returns true iff the test part failed.

  bool failed() const { return type_ != TPRT_SUCCESS; }


  // Returns true iff the test part non-fatally failed.

  bool nonfatally_failed() const { return type_ == TPRT_NONFATAL_FAILURE; }


  // Returns true iff the test part fatally failed.

  bool fatally_failed() const { return type_ == TPRT_FATAL_FAILURE; }

 private:

  TestPartResultType type_;


  // Gets the summary of the failure message by omitting the stack

  // trace in it.

  static internal::String ExtractSummary(const char* message);


  // The name of the source file where the test part took place, or

  // NULL if the source file is unknown.

  internal::String file_name_;

  // The line in the source file where the test part took place, or -1

  // if the line number is unknown.

  int line_number_;

  internal::String summary_;  // The test failure summary.

  internal::String message_;  // The test failure message.

};


// Prints a TestPartResult object.

std::ostream& operator<<(std::ostream& os, const TestPartResult& result);


// An array of TestPartResult objects.

//

// We define this class as we cannot use STL containers when compiling

// Google Test with MSVC 7.1 and exceptions disabled.

//

// Don't inherit from TestPartResultArray as its destructor is not

// virtual.

class TestPartResultArray {

 public:

  TestPartResultArray();

  ~TestPartResultArray();


  // Appends the given TestPartResult to the array.

  void Append(const TestPartResult& result);


  // Returns the TestPartResult at the given index (0-based).

  const TestPartResult& GetTestPartResult(int index) const;


  // Returns the number of TestPartResult objects in the array.

  int size() const;

 private:

  // Internally we use a list to simulate the array.  Yes, this means

  // that random access is O(N) in time, but it's OK for its purpose.

  internal::List<TestPartResult>* const list_;


  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray);

};


// This interface knows how to report a test part result.

class TestPartResultReporterInterface {

 public:

  virtual ~TestPartResultReporterInterface() {}


  virtual void ReportTestPartResult(const TestPartResult& result) = 0;

};


namespace internal {


// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a

// statement generates new fatal failures. To do so it registers itself as the

// current test part result reporter. Besides checking if fatal failures were

// reported, it only delegates the reporting to the former result reporter.

// The original result reporter is restored in the destructor.

// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.

class HasNewFatalFailureHelper : public TestPartResultReporterInterface {

 public:

  HasNewFatalFailureHelper();

  virtual ~HasNewFatalFailureHelper();

  virtual void ReportTestPartResult(const TestPartResult& result);

  bool has_new_fatal_failure() const { return has_new_fatal_failure_; }

 private:

  bool has_new_fatal_failure_;

  TestPartResultReporterInterface* original_reporter_;


  GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper);

};


}  // namespace internal


}  // namespace testing


#endif  // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_



运行 ./prodef.py gtest-test-part.h

生成的 gtest-test-part.cc 函数框架如下

#include "gtest-test-part.h"


namespace testing {


// Gets the summary of the failure message by omitting the stack

// trace in it.

internal::String TestPartResult::ExtractSummary(const char* message) {


}


// Prints a TestPartResult object.

std::ostream& operator<<(std::ostream& os, const TestPartResult& result) {


}


TestPartResultArray::TestPartResultArray() {


}


TestPartResultArray::~TestPartResultArray() {


}


// Appends the given TestPartResult to the array.

void TestPartResultArray::Append(const TestPartResult& result) {


}


// Returns the TestPartResult at the given index (0-based).

const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const {


}


// Returns the number of TestPartResult objects in the array.

int TestPartResultArray::size() const {


}


void TestPartResultReporterInterface::ReportTestPartResult(const TestPartResult& result) {


}


namespace internal {


HasNewFatalFailureHelper::HasNewFatalFailureHelper() {


}


HasNewFatalFailureHelper::~HasNewFatalFailureHelper() {


}


void HasNewFatalFailureHelper::ReportTestPartResult(const TestPartResult& result) {


}


}  // namespace internal


}  // namespace testing








下面是程序源码


#!/usr/local/bin/python3.0


#prodef.py

#automate generate the .cc file according to .h file
#generate all the functions need to be implemented 
#defining in .h file


#autor goldenlock@pku

#05.13.09

#since c++ is complex
#there may be bugs... 
#I assume you define class as below
#class A {
#};
#I assume you define function as below
#int abc();
#or
#int abc(int x,int y
# int z);
#Below is not allowed
#int
#abc (int x,int y,int z);
#I assume you always use c++ style comment //

def usage():
    """ 
        Run the program like this,notice need to touch a empty .cc or .cpp by yourself
we assume .cc by defualt
1.touch abc.cc                
        2./prodef.py abc.h abc.cc
          or ./prodef.py abc.h 

1.touch a.cpp
2./prodef.py a.h a.cpp

        It will read abc.h and append the fuctions 
        to be implemented to abc.cc
        """

func_list_exist = []
def AnalyzeOutputFile(file_handle):
    print('Analze output file right now ,the reslt of existing functions is below\n')
    file_handle.seek(0,0)
    m = file_handle.readlines()
    i = 0
    while (i < len(m)):
        line = m[i]
        find1 = re.search('[(]',line)
        find2 = re.search('[)]',line)
        if (find1 and (not find2)):
            i += 1
            line += m[i] 
            while (i < len(m) and (not re.search('[)]',line))):
                i += 1
                line += m[i]
        match = re.search('^.*\)',line,re.MULTILINE|re.DOTALL)
        if match:
            print(match.group())
            func_list_exist.append(match.group())
        i += 1
    print('Output file analze done!\n')
   

def ConvertDot_h2Dot_CC(input_file,output_file):
    """
        kernal function given a .h file
        convert to a .cc one with
        all the functions properly listed
        """
    print('The file you want to deal with is '+input_file + \
        '\n It is converted to ' + output_file)
   
    pattern = re.compile(r"""(^[\s]*)             #leading withe space,we will find and delete after
                     ([a-zA-Z~_]            # void int likely the first caracter v or i...
     .* 
     [)]                   #we find )
     #(?!\s*=\s*0)          #if we find = 0  which means pur virtual we will not match after
                            #(?=\s*=\s*0) 
     (?!.*{)              # we do not want the case int abc() const { return 1;}
                            .*)
     (;.*)                 #we want to find ; and after for we will replace these later
     \n$
     """,re.VERBOSE | re.MULTILINE | re.DOTALL)
    
    pattern2 = re.compile(r'(virtual\s+|explicit\s+|friend\s+|static\s+)')   
    
    leading_space_pattern = re.compile('(^[\s]*)[a-zA-Z~]')   
    
    pattern_func_name = re.compile(r'([a-zA-Z0-9~_\-]+\s*|operator.*)[(]') 

    pattern_comment = re.compile(r'^\s*//')
    
    pattern_template = re.compile(r'(class|typename)\s+([a-zA-Z0-9_\-]+)')
    
    p1 = re.compile(r'namespace.*{')      
    p2 = re.compile(r'(class|struct)\s+([a-zA-Z0-9_\-]+).*{')
    p3 = re.compile('{')
    p4 = re.compile('}')
    
    stack = []
    stack_class = []
    stack_template = []
    f_out = open(output_file,'r+')    
    
    AnalyzeOutputFile(f_out)                        
    
    print('Below functions will be added\n')
    f_out.write('#include "' + input_file + '"\n\n')
    with open(input_file,'r') as f:
        m = f.readlines()
        i = 0
        while i < len(m):
            line = m[i]
            if line == '\n' or re.search('^\s*//',line) :
                i += 1
                continue
            
            f1 = p1.search(line)
            if f1:                                
                f_out.write(line+'\n')
                stack.append('namespace_now')
                i += 1
                continue
            f2 = p2.search(line)   
            if f2:                 
                if(re.search(r'\s*template',m[i-1])):  
                    stack_template.append(m[i-1])
                else:
                    stack_template.append('')
                stack.append('class_now')
                class_name = f2.group(2)   
                stack_class.append(class_name)
                i += 1
                continue
            f3 = p3.search(line)
            f4 = p4.search(line)
            if f3:
                stack.append('normal_now')
            if f4:
                top_status = stack.pop()
                if top_status == 'namespace_now':
                    f_out.write(line+'\n')
                elif top_status == 'class_now':
                    stack_class.pop()
                    stack_template.pop()
            if f3 or f4:
                i += 1
                continue
            if len(stack) >0 and stack[-1] == 'normal_now':  
                i += 1
                continue
        
            find1 = re.search('[(]',line)
            find2 = re.search('[)]',line)
            if (find1 and (not find2)):
                space = leading_space_pattern.search(line).group(1)
                i += 1
                line2 = m[i]
                line2 = re.sub('^'+space,'',line2)     
                line += line2
                while (i < len(m) and (not re.search('[)]',line2))):
                    i += 1
                    line2 = m[i]
                    line2 = re.sub('^'+space,'',line2)
                    line += line2
            
            (line,match) = pattern.subn(r'\2 {\n\n}\n\n',line)  
            if match:
                friend_match = re.search('friend ',line)
                line = pattern2.sub('',line)            
                
            
                define_match = re.search(r'^([a-zA-Z0-9~_/-]+)\s*[(]',line)
                if define_match:
                    if len(stack_class) == 0 or (stack_class[-1] != define_match.group(1) and ('~'+stack_class[-1]) != define_match.group(1)):
                        i += 1
                        continue
    
                func_name = ''
                if len(stack_class) > 0 and not friend_match :                
                    template_line = ''
                    x = ''
                    if (stack_template[-1] != ''):
                        template_line = re.sub(r'\s*template','template',stack_template[-1])
                        x = re.sub(r'template\s*\<','<',template_line)
                        x = x.rstrip()             
                        x = re.sub(r'(class|typename)\s+','',x)
                    line = pattern_func_name.sub(stack_class[-1] + x + '::' + r'\1(',line)
                    line = re.sub(r'\s*=\s*0','',line)                 
                    func_name = re.search('^.*\)',line).group()
                    line = template_line + line;                       
                else:
                    func_name = re.search('^.*\)',line,re.MULTILINE|re.DOTALL).group()
               
                comment_line = ''                                   
                for j in range(i-1,0,-1):
                    c_line = m[j]
                    if pattern_comment.search(c_line):
                        c_line = re.sub('\s*//','//',c_line)
                        comment_line = c_line + comment_line
                    else:
                        break
                line = comment_line + line
                if not(func_name in func_list_exist):
                    print(func_name)
                    f_out.write(line)
            i += 1
    print('Sucessfully converted,please see '+output_file)

def main(argv):                         
    try:                                
        opts, args = getopt.getopt(argv, "h", ["help"]) 
    except getopt.GetoptError:           
        print(usage.__doc__) 
        sys.exit(2)

    if len(opts) > 0:
        for o, a in opts: 
            if o in ("-h", "--help"): 
                print(usage.__doc__) 
                sys.exit()
    elif len(args) > 0:
        input_file = args[0]
        if len(args) == 1:
            output_file = re.sub('.h','.cc',input_file)
        else:
            output_file = args[1]
            
        ConvertDot_h2Dot_CC(input_file,output_file)
    else:
        print(usage.__doc__)
        sys.exit()


if __name__ == "__main__":
    main(sys.argv[1:])

原文地址:https://www.cnblogs.com/rocketfan/p/1456151.html