C++根据头文件自动生成实现文件框架(支持模版)
以前写了一个,也贴出来了不过对模版支持不好,不带模版的函数和类成员函数还是支持的。
最近写模版类的头文件,而模版类的话如果不想inline在外部实现的化,书写起来格式很麻烦。尤其是如果返回变量的类型是在类内部typedef的类型。
所以对以前的程序修改了以下,还是利用Python正则表示生成对应的实现文件,对于模版类其实也可以生产.cc文件的将函数的实现写在.cc文件中,
如在MeshT.h文件的最后include的实现文件MeshT.cc就可以了(Openmesh就是这么做的,下面有例子),
这样避免代码都写在.h中比较乱,而且头文件太长。
嗯,现在的h2cc.py基本能完成要求,可能有些特殊情况会出问题,待验证,另外代码有多个地方重复的地方待整理。
下一步要做的,
1.进一步测试对各种情况正确无误,包括更复杂的模版测试,
2.当前完全按照格式处理,不考虑语义理解,所以对于SGI STL源代码那样头文件中把函数返回变量单独写一行的无法处理。这个以后有好的办法再处理。
(感觉做编译器的完全能写个.h到.cc的自动转换工具为啥我搜不到呢,还得自己写....肯定不如人家的精确)
3. 当前如果存在生成文件了已经,比如用户开始生成了.cc然后又在.h中增加方法,再次生成.cc的时候我会将新的方法统一写在原来.cc文件的最后,具体合适
的位置用户再改动。 下一步考虑自动将新的方法插入到原来.cc文件的合适位置,不过用户如果改动如函数格式,nameapce名称,新添加namespace等等肯定会带来问题
那个用户自己处理吧(注意这里的.cc文件都是按照h2cc.py生成)
4.像java或者python那样,将所有的代码都写到一起,即不存在头文件的写法,这样代码写起来更方便。对于C++代码可以全部写到.h中。然后调用转换
工具,自动生成.cc文件,将所有没有声明inline的函数并且实现代码长度超过n行的(如10行)自动在.h中转换为声明格式,在.cc中写成实现格式
并把实现代码拷贝过去,
可以用户紧挨函数在上面一行开头加注释如//*标明下面的函数不做转换处理在.h中inline。
个人觉得这样还是能提高写程序的效率的,不知道现在有没有现成的转换工具。
5.要做上面的工作需要对代码修改,当前的代码可扩充性太差....,扩展功能麻烦,首先把各个重复的地方提前出来统一处理。如去掉=的模式。
试图写子函数,但是问题是我需要对每一行代码做太多的情形分析,这样while循环太长了,又不好分子程序,
尤其是 python不支持类似c++的引用,写子函数修改输入变量如输入行号i并在子函数中修改,用Python还得返回i才行,别扭:(
嗯,最后决定暂时就这样了,程序不改了,当前的做法对于{}的处理采用统一忽略的做法包括类似if abc() {}的处理,修改实现其它的功能比较麻烦。。。
//h2cc.py
1 #!/usr/local/bin/python3.1
3 #author pku_goldenlock@qq.com
4 #date 2009-10-23 18:19:38.970028
5
6 #.h convert to .cpp framework
7 #注意!!
8 #1. 我们假定你的.h代码书写正确 当前不做异常,错误处理
9 #2. 函数的返回类型如 int 与 与函数名称要写到一行,否则会有问题,因为不太好判断函数名称.
10 #3. 如果已经存在要生成的文件,会只写入你在.h中新加入的函数,
11 # 但是注意原来已经存在的实现文件也应该是用h2cc.py生成的,因为我不是按语意判断,只是按照格式判断
12 # 新写的实现函数框架,会附加到原来的生成文件最后面,需要自己调整到需要的位置,尤其是有namespace的时候
13 # TODO 新写的函数直接放到原来生成文件中适当的位置
14 #4. 在生成的实现文件中,可能会把返回值单独一行,取决于行的长度,过长则分行
15 #5. 如果template模板写成多行,确保最后一行只有一个单独的>
16 # template <
17 # typename T = char,
18 # template<typename> class _Decoder = HuffDecoder<int> #pattern like this might cause error
19 # >
20
21
22 #在某行发现) ; 之后,删除该行;之后的内容,该行开始的空白去掉
23 #改为 \n\{\n\}
24 # virtual = 0 对于纯虚函数也生成实体,如不需要自己去掉. TODO 用户选项
25 #TODO 允许向SGI STL源代码那样,将返回值单独一行,在函数名称上面,不过似乎比较困难,因为当前
26 #我的程序基本按照(){};template class typdef define // /* */识别这些字符的,仅仅考虑格式
27 #不考虑语意如果返回值
28 #单独一行,如何判断什么样的是返回值类型呢?比较麻烦.比如还要区分仅仅挨着占两行的两个函数声明.
29
30 #最常用的pattern
31 #查找如下 ) ; 或者 ) const ; 但是不包括纯虚函数
32 #()z中如找到 ; //从队尾删除
33 #开始的空白要找到并删除,
34 #pattern = re.compile(r"([\s]*?).*[)](?!\s*=\s*0).*(;.*)\n$")
35
36 #class A : public 读出A未解决 done
37 #template done 支持模板类,模板函数,带有默认参数的模板,模板类中的模板函数,特化和偏特化
38 # TODO 更复杂的情况可能有问题,比如模板类中嵌套模板类
39 #vitrual = 0 done
40 #支持 const done
41 #宏 DFDFDFEE(ABC); done
42 #把注释拷贝 done
43 #已经有.cpp了 仅仅加新的接口的实现 partly done
44 #忽略返回类型,返回类型不能重载
45 #operator done
46
47 import sys,re,getopt
48
49 def usage():
50 """
51 Run the program like this
52
53 ./h2cc.py abc.h ok.cc
54 It will read abc.h and append the fuctions
55 to be implemented to ok.cc
56
57 or
58 ./h2cc.py abc.h #will create abc.cc
59 ./h2cc.py abc.h cpp #will create abc.cpp
60 """
61 pattern_comment = re.compile(r'^\s*//')
62
63 #传入一个打开的文件(我们要生成的或者已经存在要分析处理)的handle,如abc.cc
64 #调用该函数者负责关闭该文件,该函数只负责处理
65 func_list_exist = []
66 def AnalyzeOutputFile(file_handle):
67 print('Analyze output file right now ,the reslt of existing functions is below\n')
68 file_handle.seek(0,0)
69 m = file_handle.readlines()
70 i = 0
71 #---------------------------------------------------逐行处理
72 while (i < len(m)):
73 line = m[i]
74 #---------------------判断是注释则略过 re.search(r'^s*$',line) 空行判断
75 if re.search(r'^s*$',line) or pattern_comment.search(line): #null line or comment using //
76 i += 1
77 continue
78 if re.search('^\s*/[*]', line): #comment using /*
79 while (not re.search('[*]/\s*$',line)): # */
80 i += 1
81 line = m[i]
82 i += 1
83 continue
84 #-------------------------find exists functions
85 find1 = re.search('[(]',line)
86 find2 = re.search('[)]',line)
87 find3 = re.search('template',line)
88 find4 = re.search('::',line)
89 if ((find1 or find3 or find4) and (not find2)):
90 i += 1
91 line += m[i]
92 while (i < len(m) and (not re.search('[)]',line))):
93 i += 1
94 line += m[i]
95 match = re.search('^.*\)',line,re.MULTILINE|re.DOTALL)
96 if match:
97 print(match.group())
98 func_list_exist.append(match.group())
99 i += 1
100 print('Output file analze done!')
101 print('There are already %d functions exit\n'%len(func_list_exist))
102
103 #转化核心函数,当前比较乱,代码重复,一个循环中代码太长了 TODO
104 #基本实现就是按照栈处理名称,区分是否class域的函数,忽略.h中已经实现函数{}内部的内容
105 def h2cc(input_file,output_file):
106 """
107 kernal function given a .h file
108 convert to a .cc one with
109 all the functions properly listed
110 """
111 print('The file you want to deal with is '+input_file + \
112 '\n It is converted to ' + output_file)
113
114 #----核心的函数匹配模式
115 pattern = re.compile(r"""(^[\s]*) #leading withe space,we will find and delete after
116 ([a-zA-Z~_] # void int likely the first caracter v or i
117 .*
118 [)] #we find )
119 #(?!\s*=\s*0) #if we find = 0 which means pur virtual we will not match after
120 #(?=\s*=\s*0)
121 (?!.*{) # we do not want the case int abc() const { return 1;}
122 .*)
123 (;.*) #we want to find ; and after for we will replace these later
124 \n$
125 """,re.VERBOSE | re.MULTILINE | re.DOTALL)
126
127 #----处理virtual,explicit,friend,static
128 pattern2 = re.compile(r'(virtual\s+|explicit\s+|friend\s+|static\s+)')
129
130 #----开头的空白
131 leading_space_pattern = re.compile('(^[\s]*)[a-zA-Z~]')
132
133 #我们默认函数都会有 如 abc( abc ( 这样的模式存在
134 #但是operator 是个例外,类名要加在operaotr前面,而且不存在上面的模式
135 #operator = () ClassName::operator = ()
136 #pattern_func_name = re.compile(r'([a-zA-Z0-9~_\-]+\s*[(]|operator.*[(])')
137 #难道替换不是仅仅替换括号里面的 而是全部替换? 恩,大括号 必须的 然后用\1没有\0
138 pattern_func_name = re.compile(r'([a-zA-Z0-9~_\-]+\s*|operator.*)[(]')
139
140 pattern_template = re.compile('^\s*template')
141 #pattern_template_end = re.compile('^\s*>\s*$') #TODO why wrong?
142 pattern_template_end = re.compile('>\s*$')
143
144 pattern_namespace = re.compile(r'namespace.*{') #判断该行是否是 namespace出现
145 #p2 = re.compile(r'class\s*(.*?)\s*{|struct\s*(.*?)\s*{')
146 #.*? 最小匹配 是否class出现,并记录class 名称
147 pattern_class = re.compile(r'^[\s]*(class|struct)\s+([a-zA-Z0-9_\-]+<?)(?!.*;)')
148 #modify 09.6.6 可以处理classa a 和 { 不在同一行,但是如果class 后发现;不处理
149 #class一定是行开始或者前面可有空白
150
151 pattern_start = re.compile('{')
152 pattern_end = re.compile('}')
153
154 stack = [] #----状态可能是normal_now(位于{}中间的时候),class_now,namespace_now
155 stack_class = [] #----存储class name
156 stack_template = [] #----存储template name
157 stack_typedef = [] #----存储当前class 作用域下的所有typedef得到的名称,函数返回类型需要
158
159 first_convert = True #是否是第一次生成的实现文件
160
161 #--------------------------------------文件处理
162 try: #09.6.6 处理要转换生成的.cc 或 .cpp文件不存在的情况,添加读异常
163 f_out = open(output_file,'r+') #r+ 可读写但要求文件已经存在
164 print(output_file + ' exists already, we will anlyze it to find the existing functions to avoid adding twice')
165 AnalyzeOutputFile(f_out) #对输出文件进行预处理分析
166 first_convert = False
167 except IOError:
168 print(output_file + ' does not exist yet, we will create am empty ' + output_file)
169 f_out = open(output_file,'a') #追加打开,这里因为用了异常先判断了是否文件存在,所以也可以用 w
170 #如果没有前面的判断只能用 a ,w 会自动删除前面已有的内容
171 print('Below functions will be added\n')
172 if first_convert:
173 f_out.write('#include "' + input_file + '"\n\n') #注意 如果out文件已存在 那么经过前面的分析处理 指针已经指向了文件尾部
174
175 func_sum = 0
176 #--------------------------------------------------核心处理循环,逐行处理输入.h文件
177 with open(input_file,'r') as f:
178 m = f.readlines()
179 i = 0
180 while i < len(m):
181 line = m[i]
182 #-------------------------------------------判断是注释则略过 re.search(r'^s*$',line) 空行判断
183 if re.search(r'^s*$',line) or pattern_comment.search(line): #/n or comment using //
184 i += 1
185 continue
186 if re.search('^\s*/[*]', line): #comment using /*
187 while (not re.search('[*]/\s*$',line)): # */
188 i += 1
189 line = m[i]
190 i += 1
191 continue
192 #---------------------------------------------判断是则define略过
193 define_match = re.search(r'^\s*#define',line)
194 if define_match:
195 while re.search(r'^\s*$',line) or re.search(r'\\\s*$', line):
196 i += 1
197 line = m[i]
198 i += 1
199 continue
200 #-----------------------------------------------判断是否namespace
201 match_namespace = pattern_namespace.search(line)
202 if match_namespace: #we face namespace
203 if first_convert:
204 f_out.write(line+'\n')
205 stack.append('namespace_now')
206 i += 1
207 continue
208 #----------------------------------------------------判断并处理类里面的typedef
209 if (len(stack) > 0 and stack[-1] == 'class_now'):
210 pattern_typedef = re.compile(r'typedef\s+.*\s+(.*);')
211 match_typedef = pattern_typedef.search(line)
212 if match_typedef:
213 stack_typedef.append(match_typedef.group(1))
214 #----------------------------------------------------判断并处理模板情况
215 match_template = pattern_template.search(line)
216 template_string = ''
217 if match_template:
218 match_template_end = pattern_template_end.search(line)
219 template_string = line
220 while(not match_template_end):
221 i += 1
222 line = m[i]
223 template_string += line
224 match_template_end = pattern_template_end.search(line)
225 i += 1
226 line = m[i]
227 #--------------------------------------------判断是否是class 或者遇到 { start
228 match_class = pattern_class.search(line)
229 match_start = pattern_start.search(line)
230
231 if match_class: #we face a class
232 stack_template.append(template_string)
233 stack.append('class_now')
234 class_name = match_class.group(2) #TODO f2.group(1)如果为空则异常
235 #-----------模板类特化或者偏特化的情况 如 class A<u,Node<u> > 为了获得整个名称
236 if '<' in class_name:
237 k = line.index('<')
238 fit = 1;
239 for l in range(k+1, len(line)):
240 if line[l] == '<':
241 fit += 1
242 if line[l] == '>':
243 fit -= 1
244 if (fit == 0):
245 break
246 class_name += line[k+1:l+1]
247 stack_class.append(class_name)
248 while not match_start:
249 i += 1
250 line = m[i]
251 match_start = pattern_start.search(line)
252 i += 1
253 continue
254 #-------------------------------------------------判断是否是结束符号 }
255 match_end = pattern_end.search(line)
256 if match_start:
257 stack.append('normal_now')
258 if match_end:
259 top_status = stack.pop()
260 if top_status == 'namespace_now':
261 if first_convert:
262 f_out.write(line+'\n')
263 elif top_status == 'class_now':
264 stack_class.pop()
265 stack_template.pop()
266 stack_typedef = []
267 if match_start or match_end:
268 i += 1
269 continue
270 #注意我判断是函数只是根据 我看到该行有) 然后 后面有; important!!
271 #------------------------------------------------就像忽略注释一样忽略normal_now状态下的行,因为那是在{}中间的实现
272 if len(stack) >0 and stack[-1] == 'normal_now':
273 i += 1
274 continue
275 #---------------------------------------------------------下面我们该处理需要生成实体框架的函数了,
276 #deal with
277 #int abc(int a,
278 # int b) #能够处理这种(与)不在同一行的情况
279 find1 = re.search('[(]',line)
280 if not find1:
281 i += 1
282 continue
283 find2 = re.search('[)]',line)
284 start_i = i
285 if (find1 and (not find2)):
286 #space = leading_space_pattern.search(line).group(1)
287 space_match = leading_space_pattern.search(line)
288 i += 1
289 line2 = m[i]
290 if space_match:
291 line2 = re.sub('^'+space_match.group(1),'',line2)
292 #注意sub会替换所有的匹配,这里我们只让它替换第一个,count=1,或者对于space 前面加^
293 line += line2
294 while (i < len(m) and (not re.search('[)]',line2))):
295 i += 1
296 line2 = m[i]
297 line2 = re.sub('^'+space_match.group(1),'',line2)
298 line += line2
299
300 match_start = pattern_start.search(m[i])
301 match_end = pattern_end.search(m[i])
302 if (match_start): # like ) { or ) {} int the last line
303 if not match_end:
304 stack.append('normal_now')
305 i += 1
306 continue
307
308 #here we do the kernel sub #--------------------------------如果找到,先进行了替换abc();->abc{}
309 (line,match) = pattern.subn(r'\2 \n{\n\n}\n\n',line)
310 if (not match): # like abc() not ; after 就是说{在函数名下面单独一行的情况
311 i += 1
312 continue
313
314 #-------------------------------------------------------------OK,找到了函数,下面进行处理后输出
315 if match:
316 friend_match = re.search('friend ',line)
317 line = pattern2.sub('',line) #--------------------delete virtural explict friend!
318 func_name = ''
319 template_line = ''
320 if len(stack_class) > 0 and not friend_match : #-----类成员函数class status if friend we will not add class name
321 x = ''
322 if (template_string != ''): #-----类中模板函数,多一个模板
323 template_string = re.sub(r'\s*template','template',template_string)
324 #template_string = re.sub(r'\s*=\s*[\'a-zA-Z0-9_\-\.]+', '', template_string)
325 #------------------delete < class T = a, class U = A(3)> -> <class T, class U>
326 template_string = re.sub('\s*=.*>(\s*)$',r'>\1',template_string)
327 template_string = re.sub(r'\s*=.*,',',',template_string)
328 template_string = re.sub(r'\s*=.*','',template_string)
329 if (stack_template[-1] != ''):
330 if not (re.search(r'<\s*>', stack_template[-1])): #----不是全特化!template<>
331 template_line = re.sub(r'^\s*template','template',stack_template[-1])
332 if not (re.search(r'<.*>', stack_class[-1])): #----不是偏特化!,like template<typename T> class A<T,int>
333 #------for x we get like template<class T, typename U> -> <T,U>
334 x = re.sub(r'template\s*<','<',template_line) #remove template -> <class T, typename U>
335 x = re.sub(r'\n','',x) #取消分行,合并成一行
336 #去掉 = 及其后面的东西
337 x = re.sub(r'\s*=.*,', ',', x)
338 x = re.sub(r'\s*=.*\>', '>', x)
339 x = x.rstrip() #remove \n
340 x = re.sub(r'(class|typename)\s+|(<class>|<typename>\s*class)','',x) #去掉class,typename -> <T, U>
341 x = re.sub(r'<\s+','<',x)
342 x = re.sub(r'\s+>','>',x)
343 x = re.sub(r'\s+,', ',',x)
344 x = re.sub(r',\s+', ', ',x)
345 line = re.sub(r'\s*=\s*0','',line) #纯虚函数我们也给函数实现体,这里特别判断一下去掉 = 0
346 line = re.sub(r'\s*=.*,',',', line) #去掉=后面的东西 foo(int a = cd(32));foo(int x =(3), int y= 4);
347 line = re.sub(r'\s*=.*\)',')', line) #先去掉所有的(),最后去掉()),这两个步骤都需要不能颠倒
348 #---------如果函数较长,在void ABC::foo() 断开成两行 void ABC::\n foo()
349 temp_line = pattern_func_name.sub(stack_class[-1] + x + '::' + r'\1(',line)
350 if len(temp_line) > 60:
351 line = pattern_func_name.sub(stack_class[-1] + x + '::\n' + r'\1(',line)
352 else:
353 line = temp_line
354 #----------得到返回变量的类型
355 return_type = re.search('^(\S+)', line).group(1)
356 if (return_type in stack_typedef): #------对于返回值,需要指明是在该类中typedef指定的名称的情况
357 if (x == ''): #----全特化的情况特殊处理
358 line = re.sub('^'+return_type+r'\s+', stack_class[-1]+'::'+return_type+'\\n', line)
359 else:
360 line = re.sub('^'+return_type+r'\s+', 'typename '+stack_class[-1]+'::'+return_type+'\\n', line)
361 #----------add template as the above if there is one
362 #template_line = re.sub(r'\s*=\s*[\'a-zA-Z0-9_\-\.]+', '', template_line)
363 #------------------delete < class T = a, class U = A(3)> -> <class T, class U>
364 template_line = re.sub('\s*=.*>(\s*)$',r'>\1',template_line)
365 template_line = re.sub(r'\s*=.*,',',',template_line)
366 template_line = re.sub(r'\s*=.*','',template_line) #最后,不换行删除后面所有的
367 line = template_line + template_string + line;
368 func_name = re.search('^.*\)',line,re.MULTILINE|re.DOTALL).group()
369 else: #--------------------------------普通函数(非类成员函数)的情况!
370 stack_template.append(template_string)
371 if (stack_template[-1] != ''):
372 template_line = re.sub(r'\s*template','template',stack_template[-1])
373 #template_line = re.sub(r'\s*=\s*[\'a-zA-Z0-9_\-\.]+', '', template_line)
374 #------------------delete < class T = a, class U = A(3)> -> <class T, class U>
375 template_line = re.sub('\s*=.*>(\s*)$',r'>\1',template_line) #代码重复,TODO以后整合
376 template_line = re.sub(r'\s*=.*,',',',template_line)
377 template_line = re.sub(r'\s*=.*','',template_line)
378 line = re.sub(r'\s*=.*,', ',', line)
379 line = re.sub(r'\s*=.*\)', ')', line)
380 line = template_line + line
381 stack_template.pop()
382 func_name = re.search('^.*\)',line,re.MULTILINE|re.DOTALL).group()
383 #-------------------------------------------------------------------------加上上面的注释也拷贝过去
384 comment_line = ''
385 #假定函数的注释在函数声明的紧上面,把他们拷贝过去
386 #TODO 如果是模板函数,模板占多于1行注释没拷贝
387 k = start_i - 1 #one line befor this func start
388 if pattern_template.search(m[k]):
389 k -= 1
390 if re.search('[*]/\s*$',m[k]):
391 comment_line = m[k].lstrip()
392 while not re.search('^\s*/[*]',m[k]):
393 k -= 1
394 comment_line = m[k].lstrip() + comment_line
395 else:
396 for j in range(k,0,-1):
397 c_line = m[j]
398 if pattern_comment.search(c_line):
399 c_line = re.sub('\s*//','//',c_line) #TODO use strtip is ok?
400 comment_line = c_line + comment_line
401 else:
402 break
403 line = comment_line + line #------------------我们最终要输出的东东
404 #----------------------------------------------如果函数已经在实现文件中存在,不输出
405 if not(func_name in func_list_exist):
406 #if not first_convert and func_sum == 0:
407 # f_out.write("//newly added functions, you may need to move them to the right position\n")
408 f_out.write(line)
409 func_sum += 1
410 print(func_name)
411 i += 1 #-----------------------------------------------------------------------next line处理下一行
412 #------------------------------loop done 处理结束
413 print('\nSucessfully converted,please see '+output_file)
414 print('Added %d functions'%func_sum)
415
416
417 #-----------------------------------------------------------user input
418 def main(argv):
419 try:
420 opts, args = getopt.getopt(argv, "h", ["help"])
421 except getopt.GetoptError:
422 print(usage.__doc__)
423 sys.exit(2)
424
425 if len(opts) > 0:
426 for o, a in opts:
427 if o in ("-h", "--help"):
428 print(usage.__doc__)
429 sys.exit()
430 elif len(args) > 0:
431 input_file = args[0]
432 if len(args) == 1 or args[1] == 'cc':
433 output_file = re.sub('.h*$','.cc',input_file) # 默认转的是对应的.cc文件
434 elif (args[1] == 'cpp'):
435 output_file = re.sub('.h*$','.cpp',input_file)
436 else:
437 output_file = args[1]
438
439 h2cc(input_file,output_file)
440 else:
441 print(usage.__doc__)
442 sys.exit()
443
444
445 if __name__ == "__main__":
446 main(sys.argv[1:])
447
下面看一下常用的几种函数声明,其对应函数实现的写法,包括模版类函数,模版类中的模版函数等等。
1 /*演示函数定义的格式,主要演示在模版类外面定义成员函数的写法*/
3 using namespace std;
4
5 int abc(int x = 3, int y = 4);
6
7 template<typename T>
8 struct HuffDecoder {
9
10 };
11 template <
12 typename T = char,
13 template<typename> class _Decoder = HuffDecoder
14 >
15 struct Hand{
16 bool operator > (const Hand<T,_Decoder>& other);
17 static void print();
18 };
19
20 template<typename T, template<typename> class _Decoder>
21 void Hand<T, _Decoder>::print()
22 {
23 cout << "this is hand" << endl;
24 }
25
26 template<typename T = char>
27 class Foo {
28 public:
29 typedef T Node;
30 typedef Hand<T> Handle;
31 typedef int size;
32
33 Handle foo(Node a);
34
35 //Handle foo(Node a) {
36 // cout << "haha" << endl;
37 //}
38
39 //static Handle foo2(Node a = 'a') {
40 // cout << "haha" << endl;
41 //}
42 static Handle foo2(Node a = 'a');
43
44 Hand<T> foo3();
45
46 template <typename U>
47 void swap(U x);
48
49 //template <typename U>
50 //class abc {
51 // public:
52 // static swap()
53 //};
54 };
55
56 template<typename T = char>
57 class Derive : public Foo<T>
58 {
59 public:
60 typedef typename Foo<T>::size size;
61 static void print() {
62 size l = 3333;
63 cout << l << endl;
64 }
65 };
66 template<>
67 class Foo<int>
68 {
69 public:
70 typedef int Node;
71 typedef Hand<int> Handle;
72 Handle foo(Node a);
73 //Handle foo(Node a) {
74 // cout << "int foo" << endl;
75 //}
76
77 };
78
79 template <typename T>
80 class Foo<T*>
81 {
82 public:
83 typedef Hand<int> Handle;
84 typedef int Node;
85 Handle foo(Node a);
86 };
87
88 template <typename T>
89 typename Foo<T*>::Handle Foo<T*>::foo(Node a) {
90 cout << "T * foo" << endl;
91 }
92
93 //template<>
94 //typename Foo<int>::Handle Foo<int>::foo(Node a)
95 //{
96 // cout << "int foo" << endl;
97 //}
98 //
99 Foo<int>::Handle Foo<int>::foo(Node a)
100 {
101 cout << "int foo" << endl;
102 }
103
104 //Hand<int> Foo<int>::foo(Node a)
105 //{
106 // cout << "int foo" << endl;
107 //}
108 //
109 template <typename T>
110 void print(T x);
111
112 template <typename T>
113 void print(T x)
114 {
115 cout << x << endl;
116 }
117
118 template<typename T>
119 template<typename U>
120 void Foo<T>::swap(U x)
121 {
122 cout << "hehe" << endl;
123 }
124
125 template<typename T>
126 typename Foo<T>::Handle Foo<T>::foo(Node a)
127 //Handle Foo<T>::foo(Node a)
128 {
129 size x = 23;
130 cout << x << endl;
131 cout << "haha" << endl;
132 }
133
134 template<typename T>
135 typename Foo<T>::Handle Foo<T>::foo2(Node a)
136 {
137 cout << "haha" << endl;
138 }
139
140 template<typename T>
141 Hand<T> Foo<T>::foo3()
142 {
143 cout << "haha" << endl;
144 }
145
146
147 int main(int argc, char *argv[])
148 {
149
150 Derive<>::print();
151
152 Hand<int> hand;
153 hand.print();
154
155 Foo<>::foo2();
156 Foo<> foo;
157 foo.foo('a');
158
159 foo.foo3();
160
161 foo.swap(3);
162
163 ::print(10);
164
165 Foo<int> fooint;
166 fooint.foo(4);
167
168 Foo<int *> foopint;
169 foopint.foo(4);
170
171 return 0;
172 }
173
//用h2cc.py脚本生成的实现文件框架
1 #include "test.h"
3 int abc(int x, int y)
4 {
5
6 }
7
8 template <
9 typename T,
10 template<typename> class _Decoder
11 >
12 bool Hand<T, _Decoder>::
13 operator > (const Hand<T,_Decoder>& other)
14 {
15
16 }
17
18 template <
19 typename T,
20 template<typename> class _Decoder
21 >
22 void Hand<T, _Decoder>::print()
23 {
24
25 }
26
27 template<typename T>
28 typename Foo::Handle
29 Foo<T>::foo(Node a)
30 {
31
32 }
33
34 //static Handle foo2(Node a = 'a') {
35 // cout << "haha" << endl;
36 //}
37 template<typename T>
38 typename Foo::Handle
39 Foo<T>::foo2(Node a)
40 {
41
42 }
43
44 template<typename T>
45 Hand<T> Foo<T>::foo3()
46 {
47
48 }
49
50 template<typename T>
51 template <typename U>
52 void Foo<T>::swap(U x)
53 {
54
55 }
56
57 Foo<int>::Handle
58 Foo<int>::foo(Node a)
59 {
60
61 }
62
63 template <typename T>
64 Foo<T*>::Handle
65 Foo<T*>::foo(Node a)
66 {
67
68 }
69
70 //Hand<int> Foo<int>::foo(Node a)
71 //{
72 // cout << "int foo" << endl;
73 //}
74 //
75 template <typename T>
76 void print(T x)
77 {
78
79 }
80
// openmesh 中的一个头文件PolyMeshT.hh生成实现文件框架的示例
//PolyMeshT.hh
2 * *
3 * OpenMesh *
4 * Copyright (C) 2001-2009 by Computer Graphics Group, RWTH Aachen *
5 //=============================================================================
6 //
7 // CLASS PolyMeshT
8 //
9 //=============================================================================
10
11
12 #ifndef OPENMESH_POLYMESHT_HH
13 #define OPENMESH_POLYMESHT_HH
14
15
16 //== INCLUDES =================================================================
17
18
19 #include <OpenMesh/Core/System/config.h>
20 #include <OpenMesh/Core/Geometry/MathDefs.hh>
21 #include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
22 #include <vector>
23
24
25 //== NAMESPACES ===============================================================
26
27
28 namespace OpenMesh {
29
30
31 //== CLASS DEFINITION =========================================================
32
33
34 /** \class PolyMeshT PolyMeshT.hh <OpenMesh/Mesh/PolyMeshT.hh>
35
36 Base type for a polygonal mesh.
37
38 This is the base class for a polygonal mesh. It is parameterized
39 by a mesh kernel that is given as a template argument. This class
40 inherits all methods from its mesh kernel.
41
42 \param Kernel: template argument for the mesh kernel
43 \note You should use the predefined mesh-kernel combinations in
44 \ref mesh_types_group
45 \see \ref mesh_type
46 */
47
48 template <class Kernel>
49 class PolyMeshT : public Kernel
50 {
51 public:
52
53 /// Self type. Used to specify iterators/circulators.
54 typedef PolyMeshT<Kernel> This;
55 //--- item types ---
56
57 //@{
58 /// Determine whether this is a PolyMeshT or TriMeshT ( This function does not check the per face vertex count! It only checks if the datatype is PolyMeshT or TriMeshT )
59 enum { IsPolyMesh = 1 };
60 enum { IsTriMesh = 0 };
61 static bool is_polymesh() { return true; }
62 static bool is_trimesh() { return false; }
63 //@}
64
65 /// \name Mesh Items
66 //@{
67 /// Scalar type
68 typedef typename Kernel::Scalar Scalar;
69 /// Coordinate type
70 typedef typename Kernel::Point Point;
71 /// Normal type
72 typedef typename Kernel::Normal Normal;
73 /// Color type
74 typedef typename Kernel::Color Color;
75 /// TexCoord1D type
76 typedef typename Kernel::TexCoord1D TexCoord1D;
77 /// TexCoord2D type
78 typedef typename Kernel::TexCoord2D TexCoord2D;
79 /// TexCoord3D type
80 typedef typename Kernel::TexCoord3D TexCoord3D;
81 /// Vertex type
82 typedef typename Kernel::Vertex Vertex;
83 /// Halfedge type
84 typedef typename Kernel::Halfedge Halfedge;
85 /// Edge type
86 typedef typename Kernel::Edge Edge;
87 /// Face type
88 typedef typename Kernel::Face Face;
89 //@}
90
91 //--- handle types ---
92
93 /// Handle for referencing the corresponding item
94 typedef typename Kernel::VertexHandle VertexHandle;
95 typedef typename Kernel::HalfedgeHandle HalfedgeHandle;
96 typedef typename Kernel::EdgeHandle EdgeHandle;
97 typedef typename Kernel::FaceHandle FaceHandle;
98
99
100
101 typedef typename Kernel::VertexIter VertexIter;
102 typedef typename Kernel::HalfedgeIter HalfedgeIter;
103 typedef typename Kernel::EdgeIter EdgeIter;
104 typedef typename Kernel::FaceIter FaceIter;
105
106 typedef typename Kernel::ConstVertexIter ConstVertexIter;
107 typedef typename Kernel::ConstHalfedgeIter ConstHalfedgeIter;
108 typedef typename Kernel::ConstEdgeIter ConstEdgeIter;
109 typedef typename Kernel::ConstFaceIter ConstFaceIter;
110 //@}
111
112 //--- circulators ---
113
114 /** \name Mesh Circulators
115 Refer to OpenMesh::Mesh::Iterators or \ref mesh_iterators
116 for documentation.
117 */
118 //@{
119 /// Circulator
120 typedef typename Kernel::VertexVertexIter VertexVertexIter;
121 typedef typename Kernel::VertexOHalfedgeIter VertexOHalfedgeIter;
122 typedef typename Kernel::VertexIHalfedgeIter VertexIHalfedgeIter;
123 typedef typename Kernel::VertexEdgeIter VertexEdgeIter;
124 typedef typename Kernel::VertexFaceIter VertexFaceIter;
125 typedef typename Kernel::FaceVertexIter FaceVertexIter;
126 typedef typename Kernel::FaceHalfedgeIter FaceHalfedgeIter;
127 typedef typename Kernel::FaceEdgeIter FaceEdgeIter;
128 typedef typename Kernel::FaceFaceIter FaceFaceIter;
129
130 typedef typename Kernel::ConstVertexVertexIter ConstVertexVertexIter;
131 typedef typename Kernel::ConstVertexOHalfedgeIter ConstVertexOHalfedgeIter;
132 typedef typename Kernel::ConstVertexIHalfedgeIter ConstVertexIHalfedgeIter;
133 typedef typename Kernel::ConstVertexEdgeIter ConstVertexEdgeIter;
134 typedef typename Kernel::ConstVertexFaceIter ConstVertexFaceIter;
135 typedef typename Kernel::ConstFaceVertexIter ConstFaceVertexIter;
136 typedef typename Kernel::ConstFaceHalfedgeIter ConstFaceHalfedgeIter;
137 typedef typename Kernel::ConstFaceEdgeIter ConstFaceEdgeIter;
138 typedef typename Kernel::ConstFaceFaceIter ConstFaceFaceIter;
139 //@}
140
141
142 // --- constructor/destructor
143 PolyMeshT() {}
144 virtual ~PolyMeshT() {}
145
146 /** Uses default copy and assignment operator.
147 Use them to assign two meshes of \b equal type.
148 If the mesh types vary, use PolyMeshT::assign() instead. */
149
150 // --- creation ---
151 inline VertexHandle new_vertex()
152 { return Kernel::new_vertex(); }
153
154 inline VertexHandle new_vertex(const Point& _p)
155 {
156 VertexHandle vh(Kernel::new_vertex());
157 set_point(vh, _p);
158 return vh;
159 }
160
161 inline VertexHandle add_vertex(const Point& _p)
162 { return new_vertex(_p); }
163
164 // --- normal vectors ---
165
166 /** \name Normal vector computation
167 */
168 //@{
169
170 /** Calls update_face_normals() and update_vertex_normals() if
171 these normals (i.e. the properties) exist */
172 void update_normals();
173
174 /// Update normal for face _fh
175 void update_normal(FaceHandle _fh)
176 { set_normal(_fh, calc_face_normal(_fh)); }
177
178 /** Update normal vectors for all faces.
179 \attention Needs the Attributes::Normal attribute for faces. */
180 void update_face_normals();
181
182 /** Calculate normal vector for face _fh. */
183 Normal calc_face_normal(FaceHandle _fh) const;
184
185 /** Calculate normal vector for face (_p0, _p1, _p2). */
186 Normal calc_face_normal(const Point& _p0, const Point& _p1,
187 const Point& _p2) const;
188 // calculates the average of the vertices defining _fh
189 void calc_face_centroid(FaceHandle _fh, Point& _pt) const;
190 /// Update normal for vertex _vh
191 void update_normal(VertexHandle _vh)
192 { set_normal(_vh, calc_vertex_normal(_vh)); }
193
194 /** Update normal vectors for all vertices. \attention Needs the
195 Attributes::Normal attribute for faces and vertices. */
196 void update_vertex_normals();
197
198 /** Calculate normal vector for vertex _vh by averaging normals
199 of adjacent faces. Face normals have to be computed first.
200 \attention Needs the Attributes::Normal attribute for faces. */
201 Normal calc_vertex_normal(VertexHandle _vh) const;
202
203 /** Different methods for calculation of the normal at _vh:
204 - -"-_fast - the default one - the same as calc vertex_normal()
205 - needs the Attributes::Normal attribute for faces
206 - -"-_correct - works properly for non-triangular meshes
207 - does not need any attributes
208 - -"-_loop - calculates loop surface normals
209 - does not need any attributes */
210 void calc_vertex_normal_fast(VertexHandle _vh, Normal& _n) const;
211 void calc_vertex_normal_correct(VertexHandle _vh, Normal& _n) const;
212 void calc_vertex_normal_loop(VertexHandle _vh, Normal& _n) const;
213
214
215 //@}
216
217 // --- Geometry API - still in development ---
218
219 /** Calculates the edge vector as the vector defined by
220 the halfedge with id #0 (see below) */
221 void calc_edge_vector(EdgeHandle _eh, Normal& _edge_vec) const
222 { calc_edge_vector(halfedge_handle(_eh,0), _edge_vec); }
223
224 /** Calculates the edge vector as the difference of the
225 the points defined by to_vertex_handle() and from_vertex_handle() */
226 void calc_edge_vector(HalfedgeHandle _heh, Normal& _edge_vec) const
227 {
228 _edge_vec = point(to_vertex_handle(_heh));
229 _edge_vec -= point(from_vertex_handle(_heh));
230 }
231
232 // Calculates the length of the edge _eh
233 Scalar calc_edge_length(EdgeHandle _eh) const
234 { return calc_edge_length(halfedge_handle(_eh,0)); }
235
236 /** Calculates the length of the edge _heh
237 */
238 Scalar calc_edge_length(HalfedgeHandle _heh) const
239 { return (Scalar)sqrt(calc_edge_sqr_length(_heh)); }
240
241 Scalar calc_edge_sqr_length(EdgeHandle _eh) const
242 { return calc_edge_sqr_length(halfedge_handle(_eh,0)); }
243
244 Scalar calc_edge_sqr_length(HalfedgeHandle _heh) const
245 {
246 Normal edge_vec;
247 calc_edge_vector(_heh, edge_vec);
248 return edge_vec.sqrnorm();
249 }
250
251 /** defines a consistent representation of a sector geometry:
252 the halfedge _in_heh defines the sector orientation
253 the vertex pointed by _in_heh defines the sector center
254 _vec0 and _vec1 are resp. the first and the second vectors defining the sector */
255 void calc_sector_vectors(HalfedgeHandle _in_heh, Normal& _vec0, Normal& _vec1) const
256 {
257 calc_edge_vector(next_halfedge_handle(_in_heh), _vec0);//p2 - p1
258 calc_edge_vector(opposite_halfedge_handle(_in_heh), _vec1);//p0 - p1
259 }
260
261 /** calculates the sector angle.\n
262 * The vertex pointed by _in_heh defines the sector center
263 * The angle will be calculated between the given halfedge and the next halfedge.\n
264 * Seen from the center vertex this will be the next halfedge in clockwise direction.\n
265 NOTE: only boundary concave sectors are treated correctly */
266 Scalar calc_sector_angle(HalfedgeHandle _in_heh) const
267 {
268 Normal v0, v1;
269 calc_sector_vectors(_in_heh, v0, v1);
270 Scalar denom = v0.norm()*v1.norm();
271 if (is_zero(denom))
272 {
273 return 0;
274 }
275 Scalar cos_a = (v0 | v1) / denom;
276 if (is_boundary(_in_heh))
277 {//determine if the boundary sector is concave or convex
278 FaceHandle fh(face_handle(opposite_halfedge_handle(_in_heh)));
279 Normal f_n(calc_face_normal(fh));//this normal is (for convex fh) OK
280 Scalar sign_a = dot(cross(v0, v1), f_n);
281 return angle(cos_a, sign_a);
282 }
283 else
284 {
285 return acos(sane_aarg(cos_a));
286 }
287 }
288
289 // calculate the cos and the sin of angle <(_in_heh,next_halfedge(_in_heh))
290 /*
291 void calc_sector_angle_cos_sin(HalfedgeHandle _in_heh, Scalar& _cos_a, Scalar& _sin_a) const
292 {
293 Normal in_vec, out_vec;
294 calc_edge_vector(_in_heh, in_vec);
295 calc_edge_vector(next_halfedge_handle(_in_heh), out_vec);
296 Scalar denom = in_vec.norm()*out_vec.norm();
297 if (is_zero(denom))
298 {
299 _cos_a = 1;
300 _sin_a = 0;
301 }
302 else
303 {
304 _cos_a = dot(in_vec, out_vec)/denom;
305 _sin_a = cross(in_vec, out_vec).norm()/denom;
306 }
307 }
308 */
309 /** calculates the normal (non-normalized) of the face sector defined by
310 the angle <(_in_heh,next_halfedge(_in_heh)) */
311 void calc_sector_normal(HalfedgeHandle _in_heh, Normal& _sector_normal) const
312 {
313 Normal vec0, vec1;
314 calc_sector_vectors(_in_heh, vec0, vec1);
315 _sector_normal = cross(vec0, vec1);//(p2-p1)^(p0-p1)
316 }
317
318 /** calculates the area of the face sector defined by
319 the angle <(_in_heh,next_halfedge(_in_heh))
320 NOTE: special cases (e.g. concave sectors) are not handled correctly */
321 Scalar calc_sector_area(HalfedgeHandle _in_heh) const
322 {
323 Normal sector_normal;
324 calc_sector_normal(_in_heh, sector_normal);
325 return sector_normal.norm()/2;
326 }
327
328 /** calculates the dihedral angle on the halfedge _heh
329 \attention Needs the Attributes::Normal attribute for faces */
330 Scalar calc_dihedral_angle_fast(HalfedgeHandle _heh) const
331 {
332 CHECK(Kernel::has_face_normals());
333 if (is_boundary(edge_handle(_heh)))
334 {//the dihedral angle at a boundary edge is 0
335 return 0;
336 }
337 const Normal& n0 = normal(face_handle(_heh));
338 const Normal& n1 = normal(face_handle(opposite_halfedge_handle(_heh)));
339 Normal he;
340 calc_edge_vector(_heh, he);
341 Scalar da_cos = dot(n0, n1);
342 //should be normalized, but we need only the sign
343 Scalar da_sin_sign = dot(cross(n0, n1), he);
344 return angle(da_cos, da_sin_sign);
345 }
346
347 /** calculates the dihedral angle on the edge _eh
348 \attention Needs the Attributes::Normal attribute for faces */
349 Scalar calc_dihedral_angle_fast(EdgeHandle _eh) const
350 { return calc_dihedral_angle_fast(halfedge_handle(_eh,0)); }
351
352 // calculates the dihedral angle on the halfedge _heh
353 Scalar calc_dihedral_angle(HalfedgeHandle _heh) const
354 {
355 if (is_boundary(edge_handle(_heh)))
356 {//the dihedral angle at a boundary edge is 0
357 return 0;
358 }
359 Normal n0, n1, he;
360 calc_sector_normal(_heh, n0);
361 calc_sector_normal(opposite_halfedge_handle(_heh), n1);
362 calc_edge_vector(_heh, he);
363 Scalar denom = n0.norm()*n1.norm();
364 if (denom == Scalar(0))
365 {
366 return 0;
367 }
368 Scalar da_cos = dot(n0, n1)/denom;
369 //should be normalized, but we need only the sign
370 Scalar da_sin_sign = dot(cross(n0, n1), he);
371 return angle(da_cos, da_sin_sign);
372 }
373
374 // calculates the dihedral angle on the edge _eh
375 Scalar calc_dihedral_angle(EdgeHandle _eh) const
376 { return calc_dihedral_angle(halfedge_handle(_eh,0)); }
377
378 /** tags an edge as a feature if its dihedral angle is larger than _angle_tresh
379 returns the number of the found feature edges, requires edge_status property*/
380 uint find_feature_edges(Scalar _angle_tresh = OpenMesh::deg_to_rad(44.0));
381 // --- misc ---
382
383 /// Face split (= 1-to-n split)
384 inline void split(FaceHandle _fh, const Point& _p)
385 { Kernel::split(_fh, add_vertex(_p)); }
386
387 inline void split(FaceHandle _fh, VertexHandle _vh)
388 { Kernel::split(_fh, _vh); }
389
390 inline void split(EdgeHandle _eh, const Point& _p)
391 { Kernel::split(_eh, add_vertex(_p)); }
392
393 inline void split(EdgeHandle _eh, VertexHandle _vh)
394 { Kernel::split(_eh, _vh); }
395 };
396
397
398 //=============================================================================
399 } // namespace OpenMesh
400 //=============================================================================
401 #if defined(OM_INCLUDE_TEMPLATES) && !defined(OPENMESH_POLYMESH_C)
402 # define OPENMESH_POLYMESH_TEMPLATES
403 # include "PolyMeshT.cc"
404 #endif
405 //=============================================================================
406 #endif // OPENMESH_POLYMESHT_HH defined
407 //=============================================================================
408
//用h2cc.py生成的实现文件框架PolyMeshT.cpp
//PolyMeshT.cpp #h2cc.py自动生成
1 #include "PolyMeshT.hh"
3 namespace OpenMesh {
4
5 /** Calls update_face_normals() and update_vertex_normals() if
6 these normals (i.e. the properties) exist */
7 template <class Kernel>
8 void PolyMeshT<Kernel>::update_normals()
9 {
10
11 }
12
13 /** Update normal vectors for all faces.
14 \attention Needs the Attributes::Normal attribute for faces. */
15 template <class Kernel>
16 void PolyMeshT<Kernel>::update_face_normals()
17 {
18
19 }
20
21 /** Calculate normal vector for face _fh. */
22 template <class Kernel>
23 typename PolyMeshT::Normal
24 PolyMeshT<Kernel>::
25 calc_face_normal(FaceHandle _fh) const
26 {
27
28 }
29
30 /** Calculate normal vector for face (_p0, _p1, _p2). */
31 template <class Kernel>
32 typename PolyMeshT::Normal
33 PolyMeshT<Kernel>::
34 calc_face_normal(const Point& _p0, const Point& _p1,
35 const Point& _p2) const
36 {
37
38 }
39
40 // calculates the average of the vertices defining _fh
41 template <class Kernel>
42 void PolyMeshT<Kernel>::
43 calc_face_centroid(FaceHandle _fh, Point& _pt) const
44 {
45
46 }
47
48 /** Update normal vectors for all vertices. \attention Needs the
49 Attributes::Normal attribute for faces and vertices. */
50 template <class Kernel>
51 void PolyMeshT<Kernel>::update_vertex_normals()
52 {
53
54 }
55
56 /** Calculate normal vector for vertex _vh by averaging normals
57 of adjacent faces. Face normals have to be computed first.
58 \attention Needs the Attributes::Normal attribute for faces. */
59 template <class Kernel>
60 typename PolyMeshT::Normal
61 PolyMeshT<Kernel>::
62 calc_vertex_normal(VertexHandle _vh) const
63 {
64
65 }
66
67 /** Different methods for calculation of the normal at _vh:
68 - -"-_fast - the default one - the same as calc vertex_normal()
69 - needs the Attributes::Normal attribute for faces
70 - -"-_correct - works properly for non-triangular meshes
71 - does not need any attributes
72 - -"-_loop - calculates loop surface normals
73 - does not need any attributes */
74 template <class Kernel>
75 void PolyMeshT<Kernel>::
76 calc_vertex_normal_fast(VertexHandle _vh, Normal& _n) const
77 {
78
79 }
80
81 template <class Kernel>
82 void PolyMeshT<Kernel>::
83 calc_vertex_normal_correct(VertexHandle _vh, Normal& _n) const
84 {
85
86 }
87
88 template <class Kernel>
89 void PolyMeshT<Kernel>::
90 calc_vertex_normal_loop(VertexHandle _vh, Normal& _n) const
91 {
92
93 }
94
95 /** tags an edge as a feature if its dihedral angle is larger than _angle_tresh
96 returns the number of the found feature edges, requires edge_status property*/
97 template <class Kernel>
98 uint PolyMeshT<Kernel>::
99 find_feature_edges(Scalar _angle_tresh)
100 {
101
102 }
103
104 } // namespace OpenMesh
105
//Openmesh提供的对应PolyMeshT.hh的实现文件PolyMeshT.cc可以和上面自动生成的框架对比一下:)
1 //=============================================================================
3 // CLASS PolyMeshT - IMPLEMENTATION
4 //
5 //=============================================================================
6
7
8 #define OPENMESH_POLYMESH_C
9
10
11 //== INCLUDES =================================================================
12
13 #include <OpenMesh/Core/Mesh/PolyMeshT.hh>
14 #include <OpenMesh/Core/Geometry/LoopSchemeMaskT.hh>
15 #include <OpenMesh/Core/Utils/vector_cast.hh>
16 #include <OpenMesh/Core/System/omstream.hh>
17 #include <vector>
18
19
20 //== NAMESPACES ===============================================================
21
22
23 namespace OpenMesh {
24
25 //== IMPLEMENTATION ==========================================================
26
27 template <class Kernel>
28 uint PolyMeshT<Kernel>::find_feature_edges(Scalar _angle_tresh)
29 {
30 assert(Kernel::has_edge_status());//this function needs edge status property
31 uint n_feature_edges = 0;
32 for (EdgeIter e_it = Kernel::edges_begin(); e_it != Kernel::edges_end(); ++e_it)
33 {
34 if (fabs(calc_dihedral_angle(e_it)) > _angle_tresh)
35 {//note: could be optimized by comparing cos(dih_angle) vs. cos(_angle_tresh)
36 status(e_it).set_feature(true);
37 n_feature_edges++;
38 }
39 else
40 {
41 status(e_it).set_feature(false);
42 }
43 }
44 return n_feature_edges;
45 }
46
47 //-----------------------------------------------------------------------------
48
49 template <class Kernel>
50 typename PolyMeshT<Kernel>::Normal
51 PolyMeshT<Kernel>::
52 calc_face_normal(FaceHandle _fh) const
53 {
54 assert(halfedge_handle(_fh).is_valid());
55 ConstFaceVertexIter fv_it(cfv_iter(_fh));
56
57 const Point& p0(point(fv_it)); ++fv_it;
58 const Point& p1(point(fv_it)); ++fv_it;
59 const Point& p2(point(fv_it));
60
61 return calc_face_normal(p0, p1, p2);
62 }
63
64
65 //-----------------------------------------------------------------------------
66
67
68 template <class Kernel>
69 typename PolyMeshT<Kernel>::Normal
70 PolyMeshT<Kernel>::
71 calc_face_normal(const Point& _p0,
72 const Point& _p1,
73 const Point& _p2) const
74 {
75 #if 1
76 // The OpenSG <Vector>::operator -= () does not support the type Point
77 // as rhs. Therefore use vector_cast at this point!!!
78 // Note! OpenSG distinguishes between Normal and Point!!!
79 Normal p1p0(_p0); p1p0 -= vector_cast<Normal>(_p1);
80 Normal p1p2(_p2); p1p2 -= vector_cast<Normal>(_p1);
81
82 Normal n = cross(p1p2, p1p0);
83 Scalar norm = n.length();
84
85 // The expression ((n *= (1.0/norm)),n) is used because the OpenSG
86 // vector class does not return self after component-wise
87 // self-multiplication with a scalar!!!
88 return (norm != Scalar(0)) ? ((n *= (Scalar(1)/norm)),n) : Normal(0,0,0);
89 #else
90 Point p1p0 = _p0; p1p0 -= _p1;
91 Point p1p2 = _p2; p1p2 -= _p1;
92
93 Normal n = vector_cast<Normal>(cross(p1p2, p1p0));
94 Scalar norm = n.length();
95
96 return (norm != 0.0) ? n *= (1.0/norm) : Normal(0,0,0);
97 #endif
98 }
99
100 //-----------------------------------------------------------------------------
101
102 template <class Kernel>
103 void
104 PolyMeshT<Kernel>::
105 calc_face_centroid(FaceHandle _fh, Point& _pt) const
106 {
107 _pt.vectorize(0);
108 uint valence = 0;
109 for (ConstFaceVertexIter cfv_it = cfv_iter(_fh); cfv_it; ++cfv_it, ++valence)
110 {
111 _pt += point(cfv_it);
112 }
113 _pt /= valence;
114 }
115 //-----------------------------------------------------------------------------
116
117
118 template <class Kernel>
119 void
120 PolyMeshT<Kernel>::
121 update_normals()
122 {
123 if (Kernel::has_face_normals()) update_face_normals();
124 if (Kernel::has_vertex_normals()) update_vertex_normals();
125 }
126
127
128 //-----------------------------------------------------------------------------
129
130
131 template <class Kernel>
132 void
133 PolyMeshT<Kernel>::
134 update_face_normals()
135 {
136 FaceIter f_it(Kernel::faces_begin()), f_end(Kernel::faces_end());
137
138 for (; f_it != f_end; ++f_it)
139 set_normal(f_it.handle(), calc_face_normal(f_it.handle()));
140 }
141
142
143 //-----------------------------------------------------------------------------
144
145
146 template <class Kernel>
147 typename PolyMeshT<Kernel>::Normal
148 PolyMeshT<Kernel>::
149 calc_vertex_normal(VertexHandle _vh) const
150 {
151 Normal n;
152 calc_vertex_normal_fast(_vh,n);
153
154 Scalar norm = n.length();
155 if (norm != 0.0) n *= (1.0/norm);
156
157 return n;
158 }
159
160 //-----------------------------------------------------------------------------
161 template <class Kernel>
162 void PolyMeshT<Kernel>::
163 calc_vertex_normal_fast(VertexHandle _vh, Normal& _n) const
164 {
165 _n.vectorize(0.0);
166 for (ConstVertexFaceIter vf_it=cvf_iter(_vh); vf_it; ++vf_it)
167 _n += normal(vf_it.handle());
168 }
169
170 //-----------------------------------------------------------------------------
171 template <class Kernel>
172 void PolyMeshT<Kernel>::
173 calc_vertex_normal_correct(VertexHandle _vh, Normal& _n) const
174 {
175 _n.vectorize(0.0);
176 ConstVertexIHalfedgeIter cvih_it = cvih_iter(_vh);
177 if (!cvih_it)
178 {//don't crash on isolated vertices
179 return;
180 }
181 Normal in_he_vec;
182 calc_edge_vector(cvih_it, in_he_vec);
183 for ( ; cvih_it; ++cvih_it)
184 {//calculates the sector normal defined by cvih_it and adds it to _n
185 if (is_boundary(cvih_it))
186 {
187 continue;
188 }
189 HalfedgeHandle out_heh(next_halfedge_handle(cvih_it));
190 Normal out_he_vec;
191 calc_edge_vector(out_heh, out_he_vec);
192 _n += cross(in_he_vec, out_he_vec);//sector area is taken into account
193 in_he_vec = out_he_vec;
194 in_he_vec *= -1;//change the orientation
195 }
196 }
197
198 //-----------------------------------------------------------------------------
199 template <class Kernel>
200 void PolyMeshT<Kernel>::
201 calc_vertex_normal_loop(VertexHandle _vh, Normal& _n) const
202 {
203 static const LoopSchemeMaskDouble& loop_scheme_mask__ =
204 LoopSchemeMaskDoubleSingleton::Instance();
205
206 Normal t_v(0.0,0.0,0.0), t_w(0.0,0.0,0.0);
207 unsigned int vh_val = valence(_vh);
208 unsigned int i = 0;
209 for (ConstVertexOHalfedgeIter cvoh_it = cvoh_iter(_vh); cvoh_it; ++cvoh_it, ++i)
210 {
211 VertexHandle r1_v(to_vertex_handle(cvoh_it));
212 t_v += (typename Point::value_type)(loop_scheme_mask__.tang0_weight(vh_val, i))*point(r1_v);
213 t_w += (typename Point::value_type)(loop_scheme_mask__.tang1_weight(vh_val, i))*point(r1_v);
214 }
215 _n = cross(t_w, t_v);//hack: should be cross(t_v, t_w), but then the normals are reversed?
216 }
217
218 //-----------------------------------------------------------------------------
219
220
221 template <class Kernel>
222 void
223 PolyMeshT<Kernel>::
224 update_vertex_normals()
225 {
226 VertexIter v_it(Kernel::vertices_begin()), v_end(Kernel::vertices_end());
227
228 for (; v_it!=v_end; ++v_it)
229 set_normal(v_it.handle(), calc_vertex_normal(v_it.handle()));
230 }
231
232 //=============================================================================
233 } // namespace OpenMesh
234 //=============================================================================
235
//看一个非模版类的示例,google test的一个头文件 gtest-filepath.h
2 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
3 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
4
5 #include <gtest/internal/gtest-string.h>
6
7 namespace testing {
8 namespace internal {
9
10 // FilePath - a class for file and directory pathname manipulation which
11 // handles platform-specific conventions (like the pathname separator).
12 // Used for helper functions for naming files in a directory for xml output.
13 // Except for Set methods, all methods are const or static, which provides an
14 // "immutable value object" -- useful for peace of mind.
15 // A FilePath with a value ending in a path separator ("like/this/") represents
16 // a directory, otherwise it is assumed to represent a file. In either case,
17 // it may or may not represent an actual file or directory in the file system.
18 // Names are NOT checked for syntax correctness -- no checking for illegal
19 // characters, malformed paths, etc.
20
21 class FilePath {
22 public:
23 FilePath() : pathname_("") { }
24 FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { }
25
26 explicit FilePath(const char* pathname) : pathname_(pathname) {
27 Normalize();
28 }
29
30 explicit FilePath(const String& pathname) : pathname_(pathname) {
31 Normalize();
32 }
33
34 FilePath& operator=(const FilePath& rhs) {
35 Set(rhs);
36 return *this;
37 }
38
39 void Set(const FilePath& rhs) {
40 pathname_ = rhs.pathname_;
41 }
42
43 String ToString() const { return pathname_; }
44 const char* c_str() const { return pathname_.c_str(); }
45
46 // Returns the current working directory, or "" if unsuccessful.
47 static FilePath GetCurrentDir();
48
49 // Given directory = "dir", base_name = "test", number = 0,
50 // extension = "xml", returns "dir/test.xml". If number is greater
51 // than zero (e.g., 12), returns "dir/test_12.xml".
52 // On Windows platform, uses \ as the separator rather than /.
53 static FilePath MakeFileName(const FilePath& directory,
54 const FilePath& base_name,
55 int number,
56 const char* extension);
57
58 // Given directory = "dir", relative_path = "test.xml",
59 // returns "dir/test.xml".
60 // On Windows, uses \ as the separator rather than /.
61 static FilePath ConcatPaths(const FilePath& directory,
62 const FilePath& relative_path);
63
64 // Returns a pathname for a file that does not currently exist. The pathname
65 // will be directory/base_name.extension or
66 // directory/base_name_<number>.extension if directory/base_name.extension
67 // already exists. The number will be incremented until a pathname is found
68 // that does not already exist.
69 // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
70 // There could be a race condition if two or more processes are calling this
71 // function at the same time -- they could both pick the same filename.
72 static FilePath GenerateUniqueFileName(const FilePath& directory,
73 const FilePath& base_name,
74 const char* extension);
75
76 // Returns true iff the path is NULL or "".
77 bool IsEmpty() const { return c_str() == NULL || *c_str() == '\0'; }
78
79 // If input name has a trailing separator character, removes it and returns
80 // the name, otherwise return the name string unmodified.
81 // On Windows platform, uses \ as the separator, other platforms use /.
82 FilePath RemoveTrailingPathSeparator() const;
83
84 // Returns a copy of the FilePath with the directory part removed.
85 // Example: FilePath("path/to/file").RemoveDirectoryName() returns
86 // FilePath("file"). If there is no directory part ("just_a_file"), it returns
87 // the FilePath unmodified. If there is no file part ("just_a_dir/") it
88 // returns an empty FilePath ("").
89 // On Windows platform, '\' is the path separator, otherwise it is '/'.
90 FilePath RemoveDirectoryName() const;
91
92 // RemoveFileName returns the directory path with the filename removed.
93 // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
94 // If the FilePath is "a_file" or "/a_file", RemoveFileName returns
95 // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
96 // not have a file, like "just/a/dir/", it returns the FilePath unmodified.
97 // On Windows platform, '\' is the path separator, otherwise it is '/'.
98 FilePath RemoveFileName() const;
99
100 // Returns a copy of the FilePath with the case-insensitive extension removed.
101 // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
102 // FilePath("dir/file"). If a case-insensitive extension is not
103 // found, returns a copy of the original FilePath.
104 FilePath RemoveExtension(const char* extension) const;
105
106 // Creates directories so that path exists. Returns true if successful or if
107 // the directories already exist; returns false if unable to create
108 // directories for any reason. Will also return false if the FilePath does
109 // not represent a directory (that is, it doesn't end with a path separator).
110 bool CreateDirectoriesRecursively() const;
111
112 // Create the directory so that path exists. Returns true if successful or
113 // if the directory already exists; returns false if unable to create the
114 // directory for any reason, including if the parent directory does not
115 // exist. Not named "CreateDirectory" because that's a macro on Windows.
116 bool CreateFolder() const;
117
118 // Returns true if FilePath describes something in the file-system,
119 // either a file, directory, or whatever, and that something exists.
120 bool FileOrDirectoryExists() const;
121
122 // Returns true if pathname describes a directory in the file-system
123 // that exists.
124 bool DirectoryExists() const;
125
126 // Returns true if FilePath ends with a path separator, which indicates that
127 // it is intended to represent a directory. Returns false otherwise.
128 // This does NOT check that a directory (or file) actually exists.
129 bool IsDirectory() const;
130
131 // Returns true if pathname describes a root directory. (Windows has one
132 // root directory per disk drive.)
133 bool IsRootDirectory() const;
134
135 // Returns true if pathname describes an absolute path.
136 bool IsAbsolutePath() const;
137
138 private:
139 // Replaces multiple consecutive separators with a single separator.
140 // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
141 // redundancies that might be in a pathname involving "." or "..".
142 //
143 // A pathname with multiple consecutive separators may occur either through
144 // user error or as a result of some scripts or APIs that generate a pathname
145 // with a trailing separator. On other platforms the same API or script
146 // may NOT generate a pathname with a trailing "/". Then elsewhere that
147 // pathname may have another "/" and pathname components added to it,
148 // without checking for the separator already being there.
149 // The script language and operating system may allow paths like "foo//bar"
150 // but some of the functions in FilePath will not handle that correctly. In
151 // particular, RemoveTrailingPathSeparator() only removes one separator, and
152 // it is called in CreateDirectoriesRecursively() assuming that it will change
153 // a pathname from directory syntax (trailing separator) to filename syntax.
154
155 void Normalize();
156
157 String pathname_;
158 }; // class FilePath
159
160 } // namespace internal
161 } // namespace testing
162
163 #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
164
//由h2cc.py生成的 gtest-filepath.cc实现框架
1 #include "gtest-filepath.h"
3 namespace testing {
4
5 namespace internal {
6
7 // Returns the current working directory, or "" if unsuccessful.
8 FilePath FilePath::GetCurrentDir()
9 {
10
11 }
12
13 // Given directory = "dir", base_name = "test", number = 0,
14 // extension = "xml", returns "dir/test.xml". If number is greater
15 // than zero (e.g., 12), returns "dir/test_12.xml".
16 // On Windows platform, uses \ as the separator rather than /.
17 FilePath FilePath::
18 MakeFileName(const FilePath& directory,
19 const FilePath& base_name,
20 int number,
21 const char* extension)
22 {
23
24 }
25
26 // Given directory = "dir", relative_path = "test.xml",
27 // returns "dir/test.xml".
28 // On Windows, uses \ as the separator rather than /.
29 FilePath FilePath::
30 ConcatPaths(const FilePath& directory,
31 const FilePath& relative_path)
32 {
33
34 }
35
36 // Returns a pathname for a file that does not currently exist. The pathname
37 // will be directory/base_name.extension or
38 // directory/base_name_<number>.extension if directory/base_name.extension
39 // already exists. The number will be incremented until a pathname is found
40 // that does not already exist.
41 // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
42 // There could be a race condition if two or more processes are calling this
43 // function at the same time -- they could both pick the same filename.
44 FilePath FilePath::
45 GenerateUniqueFileName(const FilePath& directory,
46 const FilePath& base_name,
47 const char* extension)
48 {
49
50 }
51
52 // If input name has a trailing separator character, removes it and returns
53 // the name, otherwise return the name string unmodified.
54 // On Windows platform, uses \ as the separator, other platforms use /.
55 FilePath FilePath::
56 RemoveTrailingPathSeparator() const
57 {
58
59 }
60
61 // Returns a copy of the FilePath with the directory part removed.
62 // Example: FilePath("path/to/file").RemoveDirectoryName() returns
63 // FilePath("file"). If there is no directory part ("just_a_file"), it returns
64 // the FilePath unmodified. If there is no file part ("just_a_dir/") it
65 // returns an empty FilePath ("").
66 // On Windows platform, '\' is the path separator, otherwise it is '/'.
67 FilePath FilePath::RemoveDirectoryName() const
68 {
69
70 }
71
72 // RemoveFileName returns the directory path with the filename removed.
73 // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
74 // If the FilePath is "a_file" or "/a_file", RemoveFileName returns
75 // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
76 // not have a file, like "just/a/dir/", it returns the FilePath unmodified.
77 // On Windows platform, '\' is the path separator, otherwise it is '/'.
78 FilePath FilePath::RemoveFileName() const
79 {
80
81 }
82
83 // Returns a copy of the FilePath with the case-insensitive extension removed.
84 // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
85 // FilePath("dir/file"). If a case-insensitive extension is not
86 // found, returns a copy of the original FilePath.
87 FilePath FilePath::
88 RemoveExtension(const char* extension) const
89 {
90
91 }
92
93 // Creates directories so that path exists. Returns true if successful or if
94 // the directories already exist; returns false if unable to create
95 // directories for any reason. Will also return false if the FilePath does
96 // not represent a directory (that is, it doesn't end with a path separator).
97 bool FilePath::CreateDirectoriesRecursively() const
98 {
99
100 }
101
102 // Create the directory so that path exists. Returns true if successful or
103 // if the directory already exists; returns false if unable to create the
104 // directory for any reason, including if the parent directory does not
105 // exist. Not named "CreateDirectory" because that's a macro on Windows.
106 bool FilePath::CreateFolder() const
107 {
108
109 }
110
111 // Returns true if FilePath describes something in the file-system,
112 // either a file, directory, or whatever, and that something exists.
113 bool FilePath::FileOrDirectoryExists() const
114 {
115
116 }
117
118 // Returns true if pathname describes a directory in the file-system
119 // that exists.
120 bool FilePath::DirectoryExists() const
121 {
122
123 }
124
125 // Returns true if FilePath ends with a path separator, which indicates that
126 // it is intended to represent a directory. Returns false otherwise.
127 // This does NOT check that a directory (or file) actually exists.
128 bool FilePath::IsDirectory() const
129 {
130
131 }
132
133 // Returns true if pathname describes a root directory. (Windows has one
134 // root directory per disk drive.)
135 bool FilePath::IsRootDirectory() const
136 {
137
138 }
139
140 // Returns true if pathname describes an absolute path.
141 bool FilePath::IsAbsolutePath() const
142 {
143
144 }
145
146 void FilePath::Normalize()
147 {
148
149 }
150
151 } // namespace internal
152
153 } // namespace testing
154