blob: f340ee834bdee7c4e0158b2be8f9e2ca49a85235 [file] [log] [blame]
Marc Slemkob2039e72006-08-09 01:00:17 +00001import time
2import os
3import os.path
4from string import Template
5from parser import *
6from generator import *
7
8HEADER_COMMENT = """/**
9 * Autogenerated by Thrift
10 * ${date}
11 *
12 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
13 */
14 """
15
16CPP_TYPES_HEADER = Template(HEADER_COMMENT+"""
17#if !defined(${source}_types_h_)
18#define ${source}_types_h_ 1
19
20#include <thrift/Thrift.h>
21""")
22
23CPP_TYPES_FOOTER = Template("""
24#endif // !defined(${source}_types_h_)
25""")
26
27CPP_SERVICES_HEADER = Template(HEADER_COMMENT+"""
28#if !defined(${source}_h_)
29#define ${source}_h_ 1
30
31#include <thrift/Thrift.h>
32""")
33
34CPP_SERVICES_FOOTER = Template("""
35#endif // !defined(${source}_h_)""")
36
37def cpp_debug(arg):
38 print(arg)
39
40class Indenter(object):
41 def __init__(self, level=0, step=4):
42 self.level = level
43 self.step = step
44 self.chunk = ""
45 for i in range(step):
46 self.chunk+= " "
47 self.prefix=""
48
49 def inc(self):
50 self.level+= self.step
51 self.prefix += self.chunk
52
53 def dec(self):
54 self.level-= self.step
55 if(self.level < 0):
56 raise Exception, "Illegal indent level"
57 self.prefix = self.prefix[:self.level]
58
59 def __call__(self):
60 return self.prefix
61
62class CFile(file):
63
64 def __init__(self, name, flags):
65 file.__init__(self, name, flags)
66 self.indent = Indenter()
67 self.newline = True
68
69 def rwrite(self, value):
70 file.write(self, value)
71
72 def write(self, value=""):
73 if self.newline:
74 self.rwrite(self.indent())
75 self.newline = False
76 self.rwrite(value)
77
78 def writeln(self, value=""):
79 self.write(value+"\n")
80 self.newline = True
81
82 def beginBlock(self):
83 self.writeln("{")
84 self.indent.inc();
85
86 def endBlock(self, suffix=""):
87 self.indent.dec();
88 self.writeln("}"+suffix)
89
90CPP_PRIMITIVE_MAP = {
91 "void" : "void",
92 "bool" : "bool",
93 "string": "std::string",
94 "utf7": "std::string",
95 "utf8": "std::wstring",
96 "utf16": "std::utf16",
97 "byte" : "uint8_t",
98 "i08": "int8_t",
99 "i16": "int16_t",
100 "i32": "int32_t",
101 "i64": "int64_t",
102 "u08": "uint8_t",
103 "u16": "uint16_t",
104 "u32": "uint32_t",
105 "u64": "uint64_t",
106 "float": "double"
107}
108
109CPP_CONTAINER_MAP = {
110 Map : "std::map",
111 List: "std::list",
112 Set : "std::set",
113}
114
115def typeToCTypeDeclaration(ttype):
116
117 if isinstance(ttype, PrimitiveType):
118 return CPP_PRIMITIVE_MAP[ttype.name]
119
120 elif isinstance(ttype, CollectionType):
121
122 result = CPP_CONTAINER_MAP[type(ttype)]+"<"
123
124 if isinstance(ttype, Map):
125 result+= typeToCTypeDeclaration(ttype.keyType)+", "+ typeToCTypeDeclaration(ttype.valueType)
126
127 elif isinstance(ttype, Set) or isinstance(ttype, List):
128 result+= typeToCTypeDeclaration(ttype.valueType)
129
130 else:
131 raise Exception, "Unknown Collection Type "+str(ttype)
132
133 result+= "> "
134
135 return result
136
137 elif isinstance(ttype, Struct):
138 return "struct "+ttype.name
139
140 elif isinstance(ttype, TypeDef):
141 return ttype.name;
142
143 elif isinstance(ttype, Enum):
144 return ttype.name;
145
146 elif isinstance(ttype, Function):
147 return typeToCTypeDeclaration(ttype.resultType)+ " "+ttype.name+"("+string.join([typeToCTypeDeclaration(arg) for arg in ttype.argFieldList], ", ")+")"
148
149 elif isinstance(ttype, Field):
150 return typeToCTypeDeclaration(ttype.type)+ " "+ttype.name
151
152 else:
153 raise Exception, "Unknown type "+str(ttype)
154
155def writeTypeDefDefinition(cfile, typedef):
156
157 cfile.writeln("typedef "+typeToCTypeDeclaration(typedef.definitionType)+" "+typedef.name+";")
158
159def writeEnumDefinition(cfile, enum):
160
161 cfile.write("enum "+enum.name+" ");
162
163 cfile.beginBlock();
164
165 first = True
166
167 for ed in enum.enumDefs:
168 if first:
169 first = False
170 else:
171 cfile.writeln(",")
172 cfile.write(ed.name+" = "+str(ed.id))
173
174 cfile.writeln()
175 cfile.endBlock(";");
176
177def writeStructDefinition(cfile, struct):
178
179 cfile.write("struct "+struct.name+" ");
180
181 cfile.beginBlock()
182
183 for field in struct.fieldList:
184 cfile.writeln(typeToCTypeDeclaration(field)+";")
185
186 cfile.endBlock(";")
187
188
189CPP_DEFINITION_WRITER_MAP = {
190 TypeDef : writeTypeDefDefinition,
191 Enum : writeEnumDefinition,
192 Struct : writeStructDefinition,
193 Service : None
194 }
195
196def writeDefinitions(cfile, definitions):
197 for definition in definitions:
198
199 writer = CPP_DEFINITION_WRITER_MAP[type(definition)]
200
201 if writer:
202 writer(cfile, definition)
203
204 cfile.writeln()
205
206CPP_THRIFT_NS = "facebook::thrift"
207
208CPP_INTERFACE_FUNCTION_DECLARATION = Template(""" virtual ${functionDeclaration} = 0;
209""")
210
211CPP_INTERFACE_DECLARATION = Template("""
212class ${service}If {
213 public:
214 ~${service}If() {}
215${functionDeclarations}};
216""")
217
218def writeServiceInterfaceDeclaration(cfile, service, debugp=None):
219
220 functionDeclarations = string.join([CPP_INTERFACE_FUNCTION_DECLARATION.substitute(service=service.name, functionDeclaration=typeToCTypeDeclaration(function)) for function in service.functionList], "")
221
222 cfile.write(CPP_INTERFACE_DECLARATION.substitute(service=service.name, functionDeclarations=functionDeclarations))
223
224CPP_SP = Template("boost::shared_ptr<${klass}> ")
225
226CPP_PROCESSOR = CPP_THRIFT_NS+"::TProcessor"
227CPP_PROCESSORP = CPP_SP.substitute(klass=CPP_PROCESSOR)
228
229CPP_PROTOCOL_NS = CPP_THRIFT_NS+"::protocol"
230CPP_PROTOCOL = CPP_PROTOCOL_NS+"::TProcotol"
231CPP_PROTOCOLP = CPP_SP.substitute(klass=CPP_PROTOCOL)
232
233
234CPP_TRANSPORT_NS = CPP_THRIFT_NS+"::transport"
235CPP_TRANSPORT = CPP_TRANSPORT_NS+"::TTransport"
236CPP_TRANSPORTP = CPP_SP.substitute(klass=CPP_TRANSPORT)
237
238CPP_SERVER_FUNCTION_DECLARATION = Template(""" void process_${function}("""+CPP_TRANSPORTP+""" _itrans, """+CPP_TRANSPORTP+""" _otrans);
239""")
240
241CPP_PROTOCOL_TSTOP = CPP_PROTOCOL_NS+"::T_STOP"
242CPP_PROTOCOL_TTYPE = CPP_PROTOCOL_NS+"::TType"
243
244CPP_SERVER_DECLARATION = Template("""
245class ${service}ServerIf : public ${service}If, public """+CPP_PROCESSOR+""" {
246 public:
247 ${service}ServerIf("""+CPP_PROTOCOLP+""" protocol): _iprot(protocol), _oprot(protocol) {}
248 ${service}ServerIf("""+CPP_PROTOCOLP+""" iprot, """+CPP_PROTOCOLP+""" oprot) : _iprot(iprot), _oprot(oprot) {}
249 virtual ~${service}ServerIf() {}
250 bool process("""+CPP_TRANSPORTP+""" _itrans,"""+CPP_TRANSPORTP+""" _otrans);
251 protected:
252 """+CPP_PROTOCOLP+""" _iprot;
253 """+CPP_PROTOCOLP+""" _oprot;
254 private:
255${functionDeclarations}};
256""")
257
258def writeServerDeclaration(cfile, service, debugp=None):
259
260 functionDeclarations = string.join([CPP_SERVER_FUNCTION_DECLARATION.substitute(function=function.name) for function in service.functionList], "")
261
262 cfile.write(CPP_SERVER_DECLARATION.substitute(service=service.name, functionDeclarations=functionDeclarations))
263
264CPP_CLIENT_FUNCTION_DECLARATION = Template(""" ${functionDeclaration};
265""")
266
267CPP_CLIENT_DECLARATION = Template("""
268class ${service}Client : public ${service}If {
269
270 public:
271
272 ${service}Client("""+CPP_TRANSPORTP+""" transport, """+CPP_PROTOCOLP+""" protocol): _itrans(transport), _otrans(transport), _iprot(protocol), _oprot(protocol {}
273
274 ${service}Client("""+CPP_TRANSPORTP+""" itrans, """+CPP_TRANSPORTP+""" otrans, """+CPP_PROTOCOLP+""" iprot, """+CPP_PROTOCOLP+""" oprot) : _itrans(itrans), _otrans(otrans), _iprot(iprot), _oprot(oprot)x {}
275
276${functionDeclarations}};
277""")
278
279def writeClientDeclaration(cfile, service, debugp=None):
280
281 functionDeclarations = string.join([CPP_CLIENT_FUNCTION_DECLARATION.substitute(functionDeclaration=typeToCTypeDeclaration(function)) for function in service.functionList], "")
282
283 cfile.writeln(CPP_CLIENT_DECLARATION.substitute(service=service.name, functionDeclarations=functionDeclarations))
284
285def writeServiceDeclaration(cfile, service, debugp=None):
286 writeServiceInterfaceDeclaration(cfile, service, debugp)
287 writeServerDeclaration(cfile, service, debugp)
288 writeClientDeclaration(cfile, service, debugp)
289
290def toGenDir(filename, suffix="cpp-gen", debugp=None):
291
292 result = os.path.join(os.path.split(filename)[0], suffix)
293
294 if not os.path.exists(result):
295 os.mkdir(result)
296
297 return result
298
299def toBasename(filename, debugp=None):
300 """ Take the filename minus the path and\".thrift\" extension if present """
301
302 basename = os.path.split(filename)[1]
303
304 tokens = os.path.splitext(basename)
305
306 if tokens[1].lower() == ".thrift":
307 basename = tokens[0]
308
309 if debugp:
310 debugp("toBasename("+str(filename)+") => "+str(basename))
311
312 return basename
313
314def toDefinitionHeaderName(filename, genDir=None, debugp=None):
315
316 if not genDir:
317 genDir = toGenDir(filename)
318
319 basename = toBasename(filename)
320
321 result = os.path.join(genDir, basename+"_types.h")
322
323 if debugp:
324 debugp("toDefinitionHeaderName("+str(filename)+", "+str(genDir)+") => "+str(basename))
325
326 return result
327
328def writeDefinitionHeader(program, filename, genDir=None, debugp=None):
329
330 definitionHeader = toDefinitionHeaderName(filename, genDir)
331
332 if debugp:
333 debugp("definitionHeader: "+str(definitionHeader))
334
335 cfile = CFile(definitionHeader, "w")
336
337 basename = toBasename(filename)
338
339 cfile.writeln(CPP_TYPES_HEADER.substitute(source=basename, date=time.ctime()))
340
341 writeDefinitions(cfile, program.definitions)
342
343 cfile.writeln(CPP_TYPES_FOOTER.substitute(source=basename))
344
345 cfile.close()
346
347def toServicesHeaderName(filename, genDir=None, debugp=None):
348
349 if not genDir:
350 genDir = toGenDir(filename)
351
352 basename = toBasename(filename)
353
354 result = os.path.join(genDir, basename+".h")
355
356 if debugp:
357 debugp("toDefinitionHeaderName("+str(filename)+", "+str(genDir)+") => "+str(basename))
358
359 return result
360
361
362def writeServicesHeader(program, filename, genDir=None, debugp=None):
363
364 servicesHeader = toServicesHeaderName(filename, genDir)
365
366 if debugp:
367 debugp("servicesHeader: "+str(servicesHeader))
368
369 cfile = CFile(servicesHeader, "w")
370
371 basename = toBasename(filename)
372
373 cfile.writeln(CPP_SERVICES_HEADER.substitute(source=basename, date=time.ctime()))
374
375 services = []
376
377 # Build orderered list of service definitions by scanning definitions list for services
378
379 for definition in program.definitions:
380 if isinstance(definition, Service) and definition.name in program.serviceMap:
381 services.append(definition)
382
383 for service in services:
384
385 writeServiceDeclaration(cfile, service)
386
387 cfile.writeln(CPP_SERVICES_FOOTER.substitute(source=basename))
388
389 cfile.close()
390
391
392CPP_STRUCT_READ = Template("""void read${name}Struct("""+CPP_PROTOCOLP+""" _iprot, """+CPP_TRANSPORTP+""" itrans, ${declaration}& value) {
393 std::string name;
394 uint32_t id;
395 uint32_t type;
396 while(true) {
397 _iprot->readFieldBegin(_itrans, name, type, id);
398 if(type == """+CPP_PROTOCOL_TSTOP+""") {
399 break;
400 }
401 switch(id) {
402${readFieldListSwitch}
403 }
404 }
405}
406""")
407
408CPP_PRIMITIVE_TYPE_IO_METHOD_SUFFIX_MAP = {
409 "bool" : "Bool",
410 "string": "String",
411 "utf7": "String",
412 "utf8": "String",
413 "utf16": "String",
414 "i08": "Byte",
415 "i16": "I16",
416 "i32": "I32",
417 "i64": "I64",
418 "u08": "Byte",
419 "u16": "U16",
420 "u32": "U32",
421 "u64": "U64",
422 "float": "Double"
423}
424
425CPP_COLLECTION_TYPE_IO_METHOD_SUFFIX_MAP = {
426 Map : "stdmap",
427 List : "stdlist",
428 Set : "stdset"
429}
430
431def typeToIOMethodSuffix(ttype):
432
433 if isinstance(ttype, PrimitiveType):
434 return CPP_PRIMITIVE_TYPE_IO_METHOD_SUFFIX_MAP[ttype.name]
435
436 elif isinstance(ttype, CollectionType):
437
438 result = CPP_COLLECTION_TYPE_IO_METHOD_SUFFIX_MAP[type(ttype)]+"_"
439
440 if isinstance(ttype, Map):
441 result+= "k_"+typeToIOMethodSuffix(ttype.keyType)+"_"
442
443 result += "v_"+typeToIOMethodSuffix(ttype.valueType)
444
445 return result
446
447 elif isinstance(ttype, Struct):
448 return "struct_"+ttype.name
449
450 elif isinstance(ttype, TypeDef):
451 return typeToIOMethodSuffix(ttype.definitionType)
452
453 elif isinstance(ttype, Enum):
454 return typeToIOMethodSuffix(U32_TYPE)
455
456 else:
457 raise Exception, "Unknown type "+str(ttype)
458
459def toReadCall(value, ttype):
460
461 suffix = typeToIOMethodSuffix(ttype)
462
463 if isinstance(ttype, PrimitiveType):
464 return "iprot->read"+suffix+"(itrans, "+value+")"
465
466 elif isinstance(ttype, CollectionType):
467 return "read_"+suffix+"(iprot, itrans, "+value+")"
468
469 elif isinstance(ttype, Struct):
470 return "read_"+suffix+"(iprot, itrans, "+value+")"
471
472 elif isinstance(ttype, TypeDef):
473 return toReadCall(value, ttype.definitionType)
474
475 elif isinstance(ttype, Enum):
476 return toReadCall(value, U32_TYPE)
477
478 else:
479 raise Exception, "Unknown type "+str(ttype)
480
481CPP_READ_MAP_DEFINITION = Template("""
482void read_${suffix}("""+CPP_PROTOCOLP+""" iprot, """+CPP_TRANSPORTP+""" itrans, ${declaration}& value) {
483
484 uint32_t count;
485 ${keyType} key;
486 ${valueType} elem;
487
488 _iprot->readU32(itrans, count);
489
490 for(int ix = 0; ix < count; ix++) {
491 ${keyReadCall};
492 ${valueReadCall};
493 value.insert(std::make_pair(key, elem));
494 }
495}
496""")
497
498CPP_WRITE_MAP_DEFINITION = Template("""
499void write_${suffix}("""+CPP_PROTOCOLP+""" oprot, """+CPP_TRANSPORTP+""" otrans, ${declaration}& value) {
500
501 uint32_t count;
502 ${keyType} key;
503 ${valueType} elem;
504
505 _oprot->writeU32(otrans, count);
506
507 for(int ix = 0; ix < count; ix++) {
508 ${keyReadCall};
509 ${valueReadCall};
510 value.insert(std::make_pair(key, elem));
511 }
512}
513""")
514
515
516CPP_READ_LIST_DEFINITION = Template("""
517void read_${suffix}("""+CPP_PROTOCOLP+""" iprot, """+CPP_TRANSPORTP+""" itrans, ${declaration}& value) {
518
519 uint32_t count;
520 ${valueType} elem;
521
522 _iprot->readU32(itrans, count);
523
524 for(int ix = 0; ix < count; ix++) {
525 ${valueReadCall};
526 value.insert(elem);
527 }
528}
529""")
530
531def toCollectionReadDefinition(ttype):
532
533 suffix = typeToIOMethodSuffix(ttype)
534
535 if isinstance(ttype, Map):
536 keyReadCall = toReadCall("key", ttype.keyType)
537
538 valueReadCall= toReadCall("elem", ttype.valueType)
539
540 if isinstance(ttype, Map):
541 return CPP_READ_MAP_DEFINITION.substitute(suffix=suffix, declaration=typeToCTypeDeclaration(ttype),
542 keyType=typeToCTypeDeclaration(ttype.keyType),
543 keyReadCall=keyReadCall,
544 valueType=typeToCTypeDeclaration(ttype.valueType),
545 valueReadCall=valueReadCall)
546
547 else:
548 return CPP_READ_LIST_DEFINITION.substitute(suffix=suffix, declaration=typeToCTypeDeclaration(ttype),
549 valueReadCall=valueReadCall,
550 valueType=typeToCTypeDeclaration(ttype.valueType))
551
552
553CPP_READ_STRUCT_DEFINITION = Template("""
554void read_${suffix}("""+CPP_PROTOCOLP+""" iprot, """+CPP_TRANSPORTP+""" itrans, ${declaration}& value) {
555
556 std::string name;
557 """+CPP_PROTOCOL_TTYPE+""" type;
558 uint16_t id;
559
560 while(true) {
561
562 _iprot->readFieldBegin(itrans, name, type, id);
563
564 if(type == """+CPP_PROTOCOL_TSTOP+""") {break;}
565
566 switch(id) {
567${fieldSwitch}
568 default:
569 iprot->skip(itrans, type);
570 break;
571 }
572
573 _iprot->readFieldEnd(itrans);
574 }
575}
576""")
577
578def toStructReadDefinition(ttype):
579
580 suffix = typeToIOMethodSuffix(ttype)
581
582 # Sort field list in order of increasing ids
583
584 fieldList = []
585 fieldList+= ttype.fieldList
586
587 fieldList.sort(lambda a,b: a.id - b.id)
588
589 fieldSwitch=""
590
591 for field in fieldList:
592 fieldSwitch+= " case "+str(field.id)+": "
593 fieldSwitch+= toReadCall("value."+field.name, field.type)+"; break;\n"
594
595 return CPP_READ_STRUCT_DEFINITION.substitute(suffix=suffix, declaration=typeToCTypeDeclaration(ttype), fieldSwitch=fieldSwitch)
596
597def toReadDefinition(ttype):
598 if isinstance(ttype, CollectionType):
599 return toCollectionReadDefinition(ttype)
600
601 elif isinstance(ttype, Struct):
602 return toStructReadDefinition(ttype)
603
604class CPPGenerator(Generator):
605
606 def __call__(self, program, filename, genDir=None, debugp=None):
607
608 writeDefinitionHeader(program, filename, gendir, debugp)
609
610 writeServicesHeader(program, filename, gendir, debugp)
611
612 writeClientHeader(program, filename, gendir, debugp)
613