類似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 yourselfwe assume .cc by defualt1.touch abc.cc 2./prodef.py abc.h abc.cc or ./prodef.py abc.h 1.touch a.cpp2./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:])