1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 * Contains some contributions under the Thrift Software License.
20 * Please see doc/old-thrift-license.txt in the Thrift distribution for
21 * details.
22 */
23
24 #include <cassert>
25
26 #include <fstream>
27 #include <iomanip>
28 #include <iostream>
29 #include <limits>
30 #include <sstream>
31 #include <string>
32 #include <vector>
33
34 #include <sys/stat.h>
35
36 #include "thrift/platform.h"
37 #include "thrift/generate/t_oop_generator.h"
38
39 using std::map;
40 using std::ofstream;
41 using std::ostream;
42 using std::string;
43 using std::vector;
44
45 static const string endl = "\n"; // avoid ostream << std::endl flushes
46
47 /**
48 * C++ code generator. This is legitimacy incarnate.
49 *
50 */
51 class t_cpp_generator : public t_oop_generator {
52 public:
t_cpp_generator(t_program * program,const std::map<std::string,std::string> & parsed_options,const std::string & option_string)53 t_cpp_generator(t_program* program,
54 const std::map<std::string, std::string>& parsed_options,
55 const std::string& option_string)
56 : t_oop_generator(program) {
57 (void)option_string;
58 std::map<std::string, std::string>::const_iterator iter;
59
60
61 gen_pure_enums_ = false;
62 use_include_prefix_ = false;
63 gen_cob_style_ = false;
64 gen_no_client_completion_ = false;
65 gen_no_default_operators_ = false;
66 gen_templates_ = false;
67 gen_templates_only_ = false;
68 gen_moveable_ = false;
69 gen_no_ostream_operators_ = false;
70 gen_no_skeleton_ = false;
71 has_members_ = false;
72
73 for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
74 if( iter->first.compare("pure_enums") == 0) {
75 gen_pure_enums_ = true;
76 } else if( iter->first.compare("include_prefix") == 0) {
77 use_include_prefix_ = true;
78 } else if( iter->first.compare("cob_style") == 0) {
79 gen_cob_style_ = true;
80 } else if( iter->first.compare("no_client_completion") == 0) {
81 gen_no_client_completion_ = true;
82 } else if( iter->first.compare("no_default_operators") == 0) {
83 gen_no_default_operators_ = true;
84 } else if( iter->first.compare("templates") == 0) {
85 gen_templates_ = true;
86 gen_templates_only_ = (iter->second == "only");
87 } else if( iter->first.compare("moveable_types") == 0) {
88 gen_moveable_ = true;
89 } else if ( iter->first.compare("no_ostream_operators") == 0) {
90 gen_no_ostream_operators_ = true;
91 } else if ( iter->first.compare("no_skeleton") == 0) {
92 gen_no_skeleton_ = true;
93 } else {
94 throw "unknown option cpp:" + iter->first;
95 }
96 }
97
98 out_dir_base_ = "gen-cpp";
99 }
100
101 /**
102 * Init and close methods
103 */
104
105 void init_generator() override;
106 void close_generator() override;
107 std::string display_name() const override;
108
109 void generate_consts(std::vector<t_const*> consts) override;
110
111 /**
112 * Program-level generation functions
113 */
114
115 void generate_typedef(t_typedef* ttypedef) override;
116 void generate_enum(t_enum* tenum) override;
117 void generate_enum_ostream_operator_decl(std::ostream& out, t_enum* tenum);
118 void generate_enum_ostream_operator(std::ostream& out, t_enum* tenum);
119 void generate_enum_to_string_helper_function_decl(std::ostream& out, t_enum* tenum);
120 void generate_enum_to_string_helper_function(std::ostream& out, t_enum* tenum);
121 void generate_forward_declaration(t_struct* tstruct) override;
generate_struct(t_struct * tstruct)122 void generate_struct(t_struct* tstruct) override { generate_cpp_struct(tstruct, false); }
generate_xception(t_struct * txception)123 void generate_xception(t_struct* txception) override { generate_cpp_struct(txception, true); }
124 void generate_cpp_struct(t_struct* tstruct, bool is_exception);
125
126 void generate_service(t_service* tservice) override;
127
128 void print_const_value(std::ostream& out, std::string name, t_type* type, t_const_value* value);
129 std::string render_const_value(std::ostream& out,
130 std::string name,
131 t_type* type,
132 t_const_value* value);
133
134 void generate_struct_declaration(std::ostream& out,
135 t_struct* tstruct,
136 bool is_exception = false,
137 bool pointers = false,
138 bool read = true,
139 bool write = true,
140 bool swap = false,
141 bool is_user_struct = false);
142 void generate_struct_definition(std::ostream& out,
143 std::ostream& force_cpp_out,
144 t_struct* tstruct,
145 bool setters = true,
146 bool is_user_struct = false);
147 void generate_copy_constructor(std::ostream& out, t_struct* tstruct, bool is_exception);
148 void generate_move_constructor(std::ostream& out, t_struct* tstruct, bool is_exception);
149 void generate_constructor_helper(std::ostream& out,
150 t_struct* tstruct,
151 bool is_excpetion,
152 bool is_move);
153 void generate_assignment_operator(std::ostream& out, t_struct* tstruct);
154 void generate_move_assignment_operator(std::ostream& out, t_struct* tstruct);
155 void generate_assignment_helper(std::ostream& out, t_struct* tstruct, bool is_move);
156 void generate_struct_reader(std::ostream& out, t_struct* tstruct, bool pointers = false);
157 void generate_struct_writer(std::ostream& out, t_struct* tstruct, bool pointers = false);
158 void generate_struct_result_writer(std::ostream& out, t_struct* tstruct, bool pointers = false);
159 void generate_struct_swap(std::ostream& out, t_struct* tstruct);
160 void generate_struct_print_method(std::ostream& out, t_struct* tstruct);
161 void generate_exception_what_method(std::ostream& out, t_struct* tstruct);
162
163 /**
164 * Service-level generation functions
165 */
166
167 void generate_service_interface(t_service* tservice, string style);
168 void generate_service_interface_factory(t_service* tservice, string style);
169 void generate_service_null(t_service* tservice, string style);
170 void generate_service_multiface(t_service* tservice);
171 void generate_service_helpers(t_service* tservice);
172 void generate_service_client(t_service* tservice, string style);
173 void generate_service_processor(t_service* tservice, string style);
174 void generate_service_skeleton(t_service* tservice);
175 void generate_process_function(t_service* tservice,
176 t_function* tfunction,
177 string style,
178 bool specialized = false);
179 void generate_function_helpers(t_service* tservice, t_function* tfunction);
180 void generate_service_async_skeleton(t_service* tservice);
181
182 /**
183 * Serialization constructs
184 */
185
186 void generate_deserialize_field(std::ostream& out,
187 t_field* tfield,
188 std::string prefix = "",
189 std::string suffix = "");
190
191 void generate_deserialize_struct(std::ostream& out,
192 t_struct* tstruct,
193 std::string prefix = "",
194 bool pointer = false);
195
196 void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
197
198 void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
199
200 void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
201
202 void generate_deserialize_list_element(std::ostream& out,
203 t_list* tlist,
204 std::string prefix,
205 bool push_back,
206 std::string index);
207
208 void generate_serialize_field(std::ostream& out,
209 t_field* tfield,
210 std::string prefix = "",
211 std::string suffix = "");
212
213 void generate_serialize_struct(std::ostream& out,
214 t_struct* tstruct,
215 std::string prefix = "",
216 bool pointer = false);
217
218 void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
219
220 void generate_serialize_map_element(std::ostream& out, t_map* tmap, std::string iter);
221
222 void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
223
224 void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
225
226 void generate_function_call(ostream& out,
227 t_function* tfunction,
228 string target,
229 string iface,
230 string arg_prefix);
231 /*
232 * Helper rendering functions
233 */
234
235 std::string namespace_prefix(std::string ns);
236 std::string namespace_open(std::string ns);
237 std::string namespace_close(std::string ns);
238 std::string type_name(t_type* ttype, bool in_typedef = false, bool arg = false);
239 std::string base_type_name(t_base_type::t_base tbase);
240 std::string declare_field(t_field* tfield,
241 bool init = false,
242 bool pointer = false,
243 bool constant = false,
244 bool reference = false);
245 std::string function_signature(t_function* tfunction,
246 std::string style,
247 std::string prefix = "",
248 bool name_params = true);
249 std::string cob_function_signature(t_function* tfunction,
250 std::string prefix = "",
251 bool name_params = true);
252 std::string argument_list(t_struct* tstruct, bool name_params = true, bool start_comma = false);
253 std::string type_to_enum(t_type* ttype);
254
255 void generate_enum_constant_list(std::ostream& f,
256 const vector<t_enum_value*>& constants,
257 const char* prefix,
258 const char* suffix,
259 bool include_values);
260
261 void generate_struct_ostream_operator_decl(std::ostream& f, t_struct* tstruct);
262 void generate_struct_ostream_operator(std::ostream& f, t_struct* tstruct);
263 void generate_struct_print_method_decl(std::ostream& f, t_struct* tstruct);
264 void generate_exception_what_method_decl(std::ostream& f,
265 t_struct* tstruct,
266 bool external = false);
267
is_reference(t_field * tfield)268 bool is_reference(t_field* tfield) { return tfield->get_reference(); }
269
is_complex_type(t_type * ttype)270 bool is_complex_type(t_type* ttype) {
271 ttype = get_true_type(ttype);
272
273 return ttype->is_container() || ttype->is_struct() || ttype->is_xception()
274 || (ttype->is_base_type()
275 && (((t_base_type*)ttype)->get_base() == t_base_type::TYPE_STRING));
276 }
277
set_use_include_prefix(bool use_include_prefix)278 void set_use_include_prefix(bool use_include_prefix) { use_include_prefix_ = use_include_prefix; }
279
280 /**
281 * The compiler option "no_thrift_ostream_impl" can be used to prevent
282 * the compiler from emitting implementations for operator <<. In this
283 * case the consuming application must provide any needed to build.
284 *
285 * To disable this on a per structure bases, one can alternatively set
286 * the annotation "cpp.customostream" to prevent thrift from emitting an
287 * operator << (std::ostream&).
288 *
289 * See AnnotationTest for validation of this annotation feature.
290 */
has_custom_ostream(t_type * ttype) const291 bool has_custom_ostream(t_type* ttype) const {
292 return (gen_no_ostream_operators_) ||
293 (ttype->annotations_.find("cpp.customostream") != ttype->annotations_.end());
294 }
295
296 /**
297 * Determine if all fields of t_struct's storage do not throw
298 * Move/Copy Constructors and Assignments applicable for 'noexcept'
299 * Move defaults to 'noexcept'
300 */
301 bool is_struct_storage_not_throwing(t_struct* tstruct) const;
302
303 private:
304 /**
305 * Returns the include prefix to use for a file generated by program, or the
306 * empty string if no include prefix should be used.
307 */
308 std::string get_include_prefix(const t_program& program) const;
309
310 /**
311 * Returns the legal program name to use for a file generated by program, if the
312 * program name contains dots then replace it with underscores, otherwise return the
313 * original program name.
314 */
315 std::string get_legal_program_name(std::string program_name);
316
317 /**
318 * True if we should generate pure enums for Thrift enums, instead of wrapper classes.
319 */
320 bool gen_pure_enums_;
321
322 /**
323 * True if we should generate templatized reader/writer methods.
324 */
325 bool gen_templates_;
326
327 /**
328 * True iff we should generate process function pointers for only templatized
329 * reader/writer methods.
330 */
331 bool gen_templates_only_;
332
333 /**
334 * True if we should generate move constructors & assignment operators.
335 */
336 bool gen_moveable_;
337
338 /**
339 * True if we should generate ostream definitions
340 */
341 bool gen_no_ostream_operators_;
342
343 /**
344 * True iff we should use a path prefix in our #include statements for other
345 * thrift-generated header files.
346 */
347 bool use_include_prefix_;
348
349 /**
350 * True if we should generate "Continuation OBject"-style classes as well.
351 */
352 bool gen_cob_style_;
353
354 /**
355 * True if we should omit calls to completion__() in CobClient class.
356 */
357 bool gen_no_client_completion_;
358
359 /**
360 * True if we should omit generating the default opeartors ==, != and <.
361 */
362 bool gen_no_default_operators_;
363
364 /**
365 * True if we should generate skeleton.
366 */
367 bool gen_no_skeleton_;
368
369 /**
370 * True if thrift has member(s)
371 */
372 bool has_members_;
373
374 /**
375 * Strings for namespace, computed once up front then used directly
376 */
377
378 std::string ns_open_;
379 std::string ns_close_;
380
381 /**
382 * File streams, stored here to avoid passing them as parameters to every
383 * function.
384 */
385
386 ofstream_with_content_based_conditional_update f_types_;
387 ofstream_with_content_based_conditional_update f_types_impl_;
388 ofstream_with_content_based_conditional_update f_types_tcc_;
389 ofstream_with_content_based_conditional_update f_header_;
390 ofstream_with_content_based_conditional_update f_service_;
391 ofstream_with_content_based_conditional_update f_service_tcc_;
392
393 // The ProcessorGenerator is used to generate parts of the code,
394 // so it needs access to many of our protected members and methods.
395 //
396 // TODO: The code really should be cleaned up so that helper methods for
397 // writing to the output files are separate from the generator classes
398 // themselves.
399 friend class ProcessorGenerator;
400 };
401
402 /**
403 * Prepares for file generation by opening up the necessary file output
404 * streams.
405 */
init_generator()406 void t_cpp_generator::init_generator() {
407 // Make output directory
408 MKDIR(get_out_dir().c_str());
409
410 program_name_ = get_legal_program_name(program_name_);
411
412 // Make output file
413 string f_types_name = get_out_dir() + program_name_ + "_types.h";
414 f_types_.open(f_types_name);
415
416 string f_types_impl_name = get_out_dir() + program_name_ + "_types.cpp";
417 f_types_impl_.open(f_types_impl_name.c_str());
418
419 if (gen_templates_) {
420 // If we don't open the stream, it appears to just discard data,
421 // which is fine.
422 string f_types_tcc_name = get_out_dir() + program_name_ + "_types.tcc";
423 f_types_tcc_.open(f_types_tcc_name.c_str());
424 }
425
426 // Print header
427 f_types_ << autogen_comment();
428 f_types_impl_ << autogen_comment();
429 f_types_tcc_ << autogen_comment();
430
431 // Start ifndef
432 f_types_ << "#ifndef " << program_name_ << "_TYPES_H" << endl << "#define " << program_name_
433 << "_TYPES_H" << endl << endl;
434 f_types_tcc_ << "#ifndef " << program_name_ << "_TYPES_TCC" << endl << "#define " << program_name_
435 << "_TYPES_TCC" << endl << endl;
436
437 // Include base types
438 f_types_ << "#include <iosfwd>" << endl
439 << endl
440 << "#include <thrift/Thrift.h>" << endl
441 << "#include <thrift/TApplicationException.h>" << endl
442 << "#include <thrift/TBase.h>" << endl
443 << "#include <thrift/protocol/TProtocol.h>" << endl
444 << "#include <thrift/transport/TTransport.h>" << endl
445 << endl;
446 // Include C++xx compatibility header
447 f_types_ << "#include <functional>" << endl;
448 f_types_ << "#include <memory>" << endl;
449
450 // Include other Thrift includes
451 const vector<t_program*>& includes = program_->get_includes();
452 for (auto include : includes) {
453 f_types_ << "#include \"" << get_include_prefix(*include) << include->get_name()
454 << "_types.h\"" << endl;
455
456 // XXX(simpkins): If gen_templates_ is enabled, we currently assume all
457 // included files were also generated with templates enabled.
458 f_types_tcc_ << "#include \"" << get_include_prefix(*include) << include->get_name()
459 << "_types.tcc\"" << endl;
460 }
461 f_types_ << endl;
462
463 // Include custom headers
464 const vector<string>& cpp_includes = program_->get_cpp_includes();
465 for (const auto & cpp_include : cpp_includes) {
466 if (cpp_include[0] == '<') {
467 f_types_ << "#include " << cpp_include << endl;
468 } else {
469 f_types_ << "#include \"" << cpp_include << "\"" << endl;
470 }
471 }
472 f_types_ << endl;
473
474 // Include the types file
475 f_types_impl_ << "#include \"" << get_include_prefix(*get_program()) << program_name_
476 << "_types.h\"" << endl << endl;
477 f_types_tcc_ << "#include \"" << get_include_prefix(*get_program()) << program_name_
478 << "_types.h\"" << endl << endl;
479
480 // The swap() code needs <algorithm> for std::swap()
481 f_types_impl_ << "#include <algorithm>" << endl;
482 // for operator<<
483 f_types_impl_ << "#include <ostream>" << endl << endl;
484 f_types_impl_ << "#include <thrift/TToString.h>" << endl << endl;
485
486 // Open namespace
487 ns_open_ = namespace_open(program_->get_namespace("cpp"));
488 ns_close_ = namespace_close(program_->get_namespace("cpp"));
489
490 f_types_ << ns_open_ << endl << endl;
491
492 f_types_impl_ << ns_open_ << endl << endl;
493
494 f_types_tcc_ << ns_open_ << endl << endl;
495 }
496
497 /**
498 * Closes the output files.
499 */
close_generator()500 void t_cpp_generator::close_generator() {
501 // Close namespace
502 f_types_ << ns_close_ << endl << endl;
503 f_types_impl_ << ns_close_ << endl;
504 f_types_tcc_ << ns_close_ << endl << endl;
505
506 // Include the types.tcc file from the types header file,
507 // so clients don't have to explicitly include the tcc file.
508 // TODO(simpkins): Make this a separate option.
509 if (gen_templates_) {
510 f_types_ << "#include \"" << get_include_prefix(*get_program()) << program_name_
511 << "_types.tcc\"" << endl << endl;
512 }
513
514 // Close ifndef
515 f_types_ << "#endif" << endl;
516 f_types_tcc_ << "#endif" << endl;
517
518 // Close output file
519 f_types_.close();
520 f_types_impl_.close();
521 f_types_tcc_.close();
522
523 string f_types_impl_name = get_out_dir() + program_name_ + "_types.cpp";
524
525 if (!has_members_) {
526 remove(f_types_impl_name.c_str());
527 }
528 }
529
530 /**
531 * Generates a typedef. This is just a simple 1-liner in C++
532 *
533 * @param ttypedef The type definition
534 */
generate_typedef(t_typedef * ttypedef)535 void t_cpp_generator::generate_typedef(t_typedef* ttypedef) {
536 generate_java_doc(f_types_, ttypedef);
537 f_types_ << indent() << "typedef " << type_name(ttypedef->get_type(), true) << " "
538 << ttypedef->get_symbolic() << ";" << endl << endl;
539 }
540
generate_enum_constant_list(std::ostream & f,const vector<t_enum_value * > & constants,const char * prefix,const char * suffix,bool include_values)541 void t_cpp_generator::generate_enum_constant_list(std::ostream& f,
542 const vector<t_enum_value*>& constants,
543 const char* prefix,
544 const char* suffix,
545 bool include_values) {
546 f << " {" << endl;
547 indent_up();
548
549 vector<t_enum_value*>::const_iterator c_iter;
550 bool first = true;
551 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
552 if (first) {
553 first = false;
554 } else {
555 f << "," << endl;
556 }
557 generate_java_doc(f, *c_iter);
558 indent(f) << prefix << (*c_iter)->get_name() << suffix;
559 if (include_values) {
560 f << " = " << (*c_iter)->get_value();
561 }
562 }
563
564 f << endl;
565 indent_down();
566 indent(f) << "};" << endl;
567 }
568
569 /**
570 * Generates code for an enumerated type. In C++, this is essentially the same
571 * as the thrift definition itself, using the enum keyword in C++.
572 *
573 * @param tenum The enumeration
574 */
generate_enum(t_enum * tenum)575 void t_cpp_generator::generate_enum(t_enum* tenum) {
576 vector<t_enum_value*> constants = tenum->get_constants();
577
578 std::string enum_name = tenum->get_name();
579 if (!gen_pure_enums_) {
580 enum_name = "type";
581 generate_java_doc(f_types_, tenum);
582 f_types_ << indent() << "struct " << tenum->get_name() << " {" << endl;
583 indent_up();
584 }
585 f_types_ << indent() << "enum " << enum_name;
586
587 generate_enum_constant_list(f_types_, constants, "", "", true);
588
589 if (!gen_pure_enums_) {
590 indent_down();
591 f_types_ << "};" << endl;
592 }
593
594 f_types_ << endl;
595
596 /**
597 Generate a character array of enum names for debugging purposes.
598 */
599 std::string prefix = "";
600 if (!gen_pure_enums_) {
601 prefix = tenum->get_name() + "::";
602 }
603
604 f_types_impl_ << indent() << "int _k" << tenum->get_name() << "Values[] =";
605 generate_enum_constant_list(f_types_impl_, constants, prefix.c_str(), "", false);
606
607 f_types_impl_ << indent() << "const char* _k" << tenum->get_name() << "Names[] =";
608 generate_enum_constant_list(f_types_impl_, constants, "\"", "\"", false);
609
610 f_types_ << indent() << "extern const std::map<int, const char*> _" << tenum->get_name()
611 << "_VALUES_TO_NAMES;" << endl << endl;
612
613 f_types_impl_ << indent() << "const std::map<int, const char*> _" << tenum->get_name()
614 << "_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(" << constants.size() << ", _k"
615 << tenum->get_name() << "Values"
616 << ", _k" << tenum->get_name() << "Names), "
617 << "::apache::thrift::TEnumIterator(-1, nullptr, nullptr));" << endl << endl;
618
619 generate_enum_ostream_operator_decl(f_types_, tenum);
620 generate_enum_ostream_operator(f_types_impl_, tenum);
621
622 generate_enum_to_string_helper_function_decl(f_types_, tenum);
623 generate_enum_to_string_helper_function(f_types_impl_, tenum);
624
625 has_members_ = true;
626 }
627
generate_enum_ostream_operator_decl(std::ostream & out,t_enum * tenum)628 void t_cpp_generator::generate_enum_ostream_operator_decl(std::ostream& out, t_enum* tenum) {
629
630 out << "std::ostream& operator<<(std::ostream& out, const ";
631 if (gen_pure_enums_) {
632 out << tenum->get_name();
633 } else {
634 out << tenum->get_name() << "::type&";
635 }
636 out << " val);" << endl;
637 out << endl;
638 }
639
generate_enum_ostream_operator(std::ostream & out,t_enum * tenum)640 void t_cpp_generator::generate_enum_ostream_operator(std::ostream& out, t_enum* tenum) {
641
642 // If we've been told the consuming application will provide an ostream
643 // operator definition then we only make a declaration:
644
645 if (!has_custom_ostream(tenum)) {
646 out << "std::ostream& operator<<(std::ostream& out, const ";
647 if (gen_pure_enums_) {
648 out << tenum->get_name();
649 } else {
650 out << tenum->get_name() << "::type&";
651 }
652 out << " val) ";
653 scope_up(out);
654
655 out << indent() << "std::map<int, const char*>::const_iterator it = _"
656 << tenum->get_name() << "_VALUES_TO_NAMES.find(val);" << endl;
657 out << indent() << "if (it != _" << tenum->get_name() << "_VALUES_TO_NAMES.end()) {" << endl;
658 indent_up();
659 out << indent() << "out << it->second;" << endl;
660 indent_down();
661 out << indent() << "} else {" << endl;
662 indent_up();
663 out << indent() << "out << static_cast<int>(val);" << endl;
664 indent_down();
665 out << indent() << "}" << endl;
666
667 out << indent() << "return out;" << endl;
668 scope_down(out);
669 out << endl;
670 }
671 }
672
generate_enum_to_string_helper_function_decl(std::ostream & out,t_enum * tenum)673 void t_cpp_generator::generate_enum_to_string_helper_function_decl(std::ostream& out, t_enum* tenum) {
674 out << "std::string to_string(const ";
675 if (gen_pure_enums_) {
676 out << tenum->get_name();
677 } else {
678 out << tenum->get_name() << "::type&";
679 }
680 out << " val);" << endl;
681 out << endl;
682 }
683
generate_enum_to_string_helper_function(std::ostream & out,t_enum * tenum)684 void t_cpp_generator::generate_enum_to_string_helper_function(std::ostream& out, t_enum* tenum) {
685 if (!has_custom_ostream(tenum)) {
686 out << "std::string to_string(const ";
687 if (gen_pure_enums_) {
688 out << tenum->get_name();
689 } else {
690 out << tenum->get_name() << "::type&";
691 }
692 out << " val) " ;
693 scope_up(out);
694
695 out << indent() << "std::map<int, const char*>::const_iterator it = _"
696 << tenum->get_name() << "_VALUES_TO_NAMES.find(val);" << endl;
697 out << indent() << "if (it != _" << tenum->get_name() << "_VALUES_TO_NAMES.end()) {" << endl;
698 indent_up();
699 out << indent() << "return std::string(it->second);" << endl;
700 indent_down();
701 out << indent() << "} else {" << endl;
702 indent_up();
703 out << indent() << "return std::to_string(static_cast<int>(val));" << endl;
704 indent_down();
705 out << indent() << "}" << endl;
706
707 scope_down(out);
708 out << endl;
709 }
710 }
711
712 /**
713 * Generates a class that holds all the constants.
714 */
generate_consts(std::vector<t_const * > consts)715 void t_cpp_generator::generate_consts(std::vector<t_const*> consts) {
716 string f_consts_name = get_out_dir() + program_name_ + "_constants.h";
717 ofstream_with_content_based_conditional_update f_consts;
718 if (consts.size() > 0) {
719 f_consts.open(f_consts_name);
720
721 string f_consts_impl_name = get_out_dir() + program_name_ + "_constants.cpp";
722 ofstream_with_content_based_conditional_update f_consts_impl;
723 f_consts_impl.open(f_consts_impl_name);
724
725 // Print header
726 f_consts << autogen_comment();
727 f_consts_impl << autogen_comment();
728
729 // Start ifndef
730 f_consts << "#ifndef " << program_name_ << "_CONSTANTS_H" << endl << "#define " << program_name_
731 << "_CONSTANTS_H" << endl << endl << "#include \"" << get_include_prefix(*get_program())
732 << program_name_ << "_types.h\"" << endl << endl << ns_open_ << endl << endl;
733
734 f_consts_impl << "#include \"" << get_include_prefix(*get_program()) << program_name_
735 << "_constants.h\"" << endl << endl << ns_open_ << endl << endl;
736
737 f_consts << "class " << program_name_ << "Constants {" << endl << " public:" << endl << " "
738 << program_name_ << "Constants();" << endl << endl;
739 indent_up();
740 vector<t_const*>::iterator c_iter;
741 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
742 string name = (*c_iter)->get_name();
743 t_type* type = (*c_iter)->get_type();
744 f_consts << indent() << type_name(type) << " " << name << ";" << endl;
745 }
746 indent_down();
747 f_consts << "};" << endl;
748
749 f_consts_impl << "const " << program_name_ << "Constants g_" << program_name_ << "_constants;"
750 << endl << endl << program_name_ << "Constants::" << program_name_
751 << "Constants() {" << endl;
752 indent_up();
753 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
754 print_const_value(f_consts_impl,
755 (*c_iter)->get_name(),
756 (*c_iter)->get_type(),
757 (*c_iter)->get_value());
758 }
759 indent_down();
760 indent(f_consts_impl) << "}" << endl;
761
762 f_consts << endl << "extern const " << program_name_ << "Constants g_" << program_name_
763 << "_constants;" << endl << endl << ns_close_ << endl << endl << "#endif" << endl;
764 f_consts.close();
765
766 f_consts_impl << endl << ns_close_ << endl << endl;
767 f_consts_impl.close();
768 }
769 }
770
771 /**
772 * Prints the value of a constant with the given type. Note that type checking
773 * is NOT performed in this function as it is always run beforehand using the
774 * validate_types method in main.cc
775 */
print_const_value(ostream & out,string name,t_type * type,t_const_value * value)776 void t_cpp_generator::print_const_value(ostream& out,
777 string name,
778 t_type* type,
779 t_const_value* value) {
780 type = get_true_type(type);
781 if (type->is_base_type()) {
782 string v2 = render_const_value(out, name, type, value);
783 indent(out) << name << " = " << v2 << ";" << endl << endl;
784 } else if (type->is_enum()) {
785 indent(out) << name
786 << " = static_cast<" << type_name(type) << '>'
787 << '(' << value->get_integer() << ");" << endl << endl;
788 } else if (type->is_struct() || type->is_xception()) {
789 const vector<t_field*>& fields = ((t_struct*)type)->get_members();
790 vector<t_field*>::const_iterator f_iter;
791 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
792 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
793 bool is_nonrequired_field = false;
794 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
795 t_type* field_type = nullptr;
796 is_nonrequired_field = false;
797 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
798 if ((*f_iter)->get_name() == v_iter->first->get_string()) {
799 field_type = (*f_iter)->get_type();
800 is_nonrequired_field = (*f_iter)->get_req() != t_field::T_REQUIRED;
801 }
802 }
803 if (field_type == nullptr) {
804 throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
805 }
806 string item_val = render_const_value(out, name, field_type, v_iter->second);
807 indent(out) << name << "." << v_iter->first->get_string() << " = " << item_val << ";" << endl;
808 if (is_nonrequired_field) {
809 indent(out) << name << ".__isset." << v_iter->first->get_string() << " = true;" << endl;
810 }
811 }
812 out << endl;
813 } else if (type->is_map()) {
814 t_type* ktype = ((t_map*)type)->get_key_type();
815 t_type* vtype = ((t_map*)type)->get_val_type();
816 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
817 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
818 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
819 string key = render_const_value(out, name, ktype, v_iter->first);
820 string item_val = render_const_value(out, name, vtype, v_iter->second);
821 indent(out) << name << ".insert(std::make_pair(" << key << ", " << item_val << "));" << endl;
822 }
823 out << endl;
824 } else if (type->is_list()) {
825 t_type* etype = ((t_list*)type)->get_elem_type();
826 const vector<t_const_value*>& val = value->get_list();
827 vector<t_const_value*>::const_iterator v_iter;
828 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
829 string item_val = render_const_value(out, name, etype, *v_iter);
830 indent(out) << name << ".push_back(" << item_val << ");" << endl;
831 }
832 out << endl;
833 } else if (type->is_set()) {
834 t_type* etype = ((t_set*)type)->get_elem_type();
835 const vector<t_const_value*>& val = value->get_list();
836 vector<t_const_value*>::const_iterator v_iter;
837 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
838 string item_val = render_const_value(out, name, etype, *v_iter);
839 indent(out) << name << ".insert(" << item_val << ");" << endl;
840 }
841 out << endl;
842 } else {
843 throw "INVALID TYPE IN print_const_value: " + type->get_name();
844 }
845 }
846
847 /**
848 *
849 */
render_const_value(ostream & out,string name,t_type * type,t_const_value * value)850 string t_cpp_generator::render_const_value(ostream& out,
851 string name,
852 t_type* type,
853 t_const_value* value) {
854 (void)name;
855 std::ostringstream render;
856
857 if (type->is_base_type()) {
858 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
859 switch (tbase) {
860 case t_base_type::TYPE_STRING:
861 render << '"' << get_escaped_string(value) << '"';
862 break;
863 case t_base_type::TYPE_BOOL:
864 render << ((value->get_integer() > 0) ? "true" : "false");
865 break;
866 case t_base_type::TYPE_I8:
867 case t_base_type::TYPE_I16:
868 case t_base_type::TYPE_I32:
869 render << value->get_integer();
870 break;
871 case t_base_type::TYPE_I64:
872 render << value->get_integer() << "LL";
873 break;
874 case t_base_type::TYPE_DOUBLE:
875 if (value->get_type() == t_const_value::CV_INTEGER) {
876 render << "static_cast<double>(" << value->get_integer() << ")";
877 } else {
878 render << emit_double_as_string(value->get_double());
879 }
880 break;
881 default:
882 throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
883 }
884 } else if (type->is_enum()) {
885 render << "static_cast<" << type_name(type) << '>'
886 << '(' << value->get_integer() << ')';
887 } else {
888 string t = tmp("tmp");
889 indent(out) << type_name(type) << " " << t << ";" << endl;
890 print_const_value(out, t, type, value);
891 render << t;
892 }
893
894 return render.str();
895 }
896
generate_forward_declaration(t_struct * tstruct)897 void t_cpp_generator::generate_forward_declaration(t_struct* tstruct) {
898 // Forward declare struct def
899 f_types_ << indent() << "class " << tstruct->get_name() << ";" << endl << endl;
900 }
901
902 /**
903 * Generates a struct definition for a thrift data type. This is a class
904 * with data members and a read/write() function, plus a mirroring isset
905 * inner class.
906 *
907 * @param tstruct The struct definition
908 */
generate_cpp_struct(t_struct * tstruct,bool is_exception)909 void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception) {
910 generate_struct_declaration(f_types_, tstruct, is_exception, false, true, true, true, true);
911 generate_struct_definition(f_types_impl_, f_types_impl_, tstruct, true, true);
912
913 std::ostream& out = (gen_templates_ ? f_types_tcc_ : f_types_impl_);
914 generate_struct_reader(out, tstruct);
915 generate_struct_writer(out, tstruct);
916 generate_struct_swap(f_types_impl_, tstruct);
917 generate_copy_constructor(f_types_impl_, tstruct, is_exception);
918 if (gen_moveable_) {
919 generate_move_constructor(f_types_impl_, tstruct, is_exception);
920 }
921 generate_assignment_operator(f_types_impl_, tstruct);
922 if (gen_moveable_) {
923 generate_move_assignment_operator(f_types_impl_, tstruct);
924 }
925
926 if (!has_custom_ostream(tstruct)) {
927 generate_struct_print_method(f_types_impl_, tstruct);
928 }
929
930 if (is_exception) {
931 generate_exception_what_method(f_types_impl_, tstruct);
932 }
933
934 has_members_ = true;
935 }
936
generate_copy_constructor(ostream & out,t_struct * tstruct,bool is_exception)937 void t_cpp_generator::generate_copy_constructor(ostream& out,
938 t_struct* tstruct,
939 bool is_exception) {
940 generate_constructor_helper(out, tstruct, is_exception, /*is_move=*/false);
941 }
942
generate_move_constructor(ostream & out,t_struct * tstruct,bool is_exception)943 void t_cpp_generator::generate_move_constructor(ostream& out,
944 t_struct* tstruct,
945 bool is_exception) {
946 generate_constructor_helper(out, tstruct, is_exception, /*is_move=*/true);
947 }
948
949 namespace {
950 // Helper to convert a variable to rvalue, if move is enabled
maybeMove(std::string const & other,bool move)951 std::string maybeMove(std::string const& other, bool move) {
952 if (move) {
953 return "std::move(" + other + ")";
954 }
955 return other;
956 }
957 }
958
generate_constructor_helper(ostream & out,t_struct * tstruct,bool is_exception,bool is_move)959 void t_cpp_generator::generate_constructor_helper(ostream& out,
960 t_struct* tstruct,
961 bool is_exception,
962 bool is_move) {
963
964 std::string tmp_name = tmp("other");
965
966 indent(out) << tstruct->get_name() << "::" << tstruct->get_name();
967
968 if (is_move) {
969 out << "(" << tstruct->get_name() << "&& ";
970 } else {
971 out << "(const " << tstruct->get_name() << "& ";
972 }
973 out << tmp_name << ") ";
974 if(is_move || is_struct_storage_not_throwing(tstruct))
975 out << "noexcept ";
976 if (is_exception)
977 out << ": TException() ";
978 out << "{" << endl;
979 indent_up();
980
981 const vector<t_field*>& members = tstruct->get_members();
982
983 // eliminate compiler unused warning
984 if (members.empty())
985 indent(out) << "(void) " << tmp_name << ";" << endl;
986
987 vector<t_field*>::const_iterator f_iter;
988 bool has_nonrequired_fields = false;
989 for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) {
990 if ((*f_iter)->get_req() != t_field::T_REQUIRED)
991 has_nonrequired_fields = true;
992 indent(out) << (*f_iter)->get_name() << " = "
993 << maybeMove(
994 tmp_name + "." + (*f_iter)->get_name(),
995 is_move && is_complex_type((*f_iter)->get_type()))
996 << ";" << endl;
997 }
998
999 if (has_nonrequired_fields) {
1000 indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", false) << ";" << endl;
1001 }
1002
1003 indent_down();
1004 indent(out) << "}" << endl;
1005 }
1006
generate_assignment_operator(ostream & out,t_struct * tstruct)1007 void t_cpp_generator::generate_assignment_operator(ostream& out, t_struct* tstruct) {
1008 generate_assignment_helper(out, tstruct, /*is_move=*/false);
1009 }
1010
generate_move_assignment_operator(ostream & out,t_struct * tstruct)1011 void t_cpp_generator::generate_move_assignment_operator(ostream& out, t_struct* tstruct) {
1012 generate_assignment_helper(out, tstruct, /*is_move=*/true);
1013 }
1014
generate_assignment_helper(ostream & out,t_struct * tstruct,bool is_move)1015 void t_cpp_generator::generate_assignment_helper(ostream& out, t_struct* tstruct, bool is_move) {
1016 std::string tmp_name = tmp("other");
1017
1018 indent(out) << tstruct->get_name() << "& " << tstruct->get_name() << "::operator=(";
1019
1020 if (is_move) {
1021 out << tstruct->get_name() << "&& ";
1022 } else {
1023 out << "const " << tstruct->get_name() << "& ";
1024 }
1025 out << tmp_name << ") ";
1026 if(is_move || is_struct_storage_not_throwing(tstruct))
1027 out << "noexcept ";
1028 out << "{" << endl;
1029 indent_up();
1030
1031 const vector<t_field*>& members = tstruct->get_members();
1032
1033 // eliminate compiler unused warning
1034 if (members.empty())
1035 indent(out) << "(void) " << tmp_name << ";" << endl;
1036
1037 vector<t_field*>::const_iterator f_iter;
1038 bool has_nonrequired_fields = false;
1039 for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) {
1040 if ((*f_iter)->get_req() != t_field::T_REQUIRED)
1041 has_nonrequired_fields = true;
1042 indent(out) << (*f_iter)->get_name() << " = "
1043 << maybeMove(
1044 tmp_name + "." + (*f_iter)->get_name(),
1045 is_move && is_complex_type((*f_iter)->get_type()))
1046 << ";" << endl;
1047 }
1048 if (has_nonrequired_fields) {
1049 indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", false) << ";" << endl;
1050 }
1051
1052 indent(out) << "return *this;" << endl;
1053 indent_down();
1054 indent(out) << "}" << endl;
1055 }
1056
1057 /**
1058 * Writes the struct declaration into the header file
1059 *
1060 * @param out Output stream
1061 * @param tstruct The struct
1062 */
generate_struct_declaration(ostream & out,t_struct * tstruct,bool is_exception,bool pointers,bool read,bool write,bool swap,bool is_user_struct)1063 void t_cpp_generator::generate_struct_declaration(ostream& out,
1064 t_struct* tstruct,
1065 bool is_exception,
1066 bool pointers,
1067 bool read,
1068 bool write,
1069 bool swap,
1070 bool is_user_struct) {
1071 string extends = "";
1072 if (is_exception) {
1073 extends = " : public ::apache::thrift::TException";
1074 } else {
1075 if (is_user_struct && !gen_templates_) {
1076 extends = " : public virtual ::apache::thrift::TBase";
1077 }
1078 }
1079
1080 // Get members
1081 vector<t_field*>::const_iterator m_iter;
1082 const vector<t_field*>& members = tstruct->get_members();
1083
1084 // Write the isset structure declaration outside the class. This makes
1085 // the generated code amenable to processing by SWIG.
1086 // We only declare the struct if it gets used in the class.
1087
1088 // Isset struct has boolean fields, but only for non-required fields.
1089 bool has_nonrequired_fields = false;
1090 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
1091 if ((*m_iter)->get_req() != t_field::T_REQUIRED)
1092 has_nonrequired_fields = true;
1093 }
1094
1095 if (has_nonrequired_fields && (!pointers || read)) {
1096
1097 out << indent() << "typedef struct _" << tstruct->get_name() << "__isset {" << endl;
1098 indent_up();
1099
1100 indent(out) << "_" << tstruct->get_name() << "__isset() ";
1101 bool first = true;
1102 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
1103 if ((*m_iter)->get_req() == t_field::T_REQUIRED) {
1104 continue;
1105 }
1106 string isSet = ((*m_iter)->get_value() != nullptr) ? "true" : "false";
1107 if (first) {
1108 first = false;
1109 out << ": " << (*m_iter)->get_name() << "(" << isSet << ")";
1110 } else {
1111 out << ", " << (*m_iter)->get_name() << "(" << isSet << ")";
1112 }
1113 }
1114 out << " {}" << endl;
1115
1116 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
1117 if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
1118 indent(out) << "bool " << (*m_iter)->get_name() << " :1;" << endl;
1119 }
1120 }
1121
1122 indent_down();
1123 indent(out) << "} _" << tstruct->get_name() << "__isset;" << endl;
1124 }
1125
1126 out << endl;
1127
1128 generate_java_doc(out, tstruct);
1129
1130 // Open struct def
1131 out << indent() << "class " << tstruct->get_name() << extends << " {" << endl << indent()
1132 << " public:" << endl << endl;
1133 indent_up();
1134
1135 if (!pointers) {
1136 bool ok_noexcept = is_struct_storage_not_throwing(tstruct);
1137 // Copy constructor
1138 indent(out) << tstruct->get_name() << "(const " << tstruct->get_name() << "&)"
1139 << (ok_noexcept? " noexcept" : "") << ';' << endl;
1140
1141 // Move constructor
1142 if (gen_moveable_) {
1143 indent(out) << tstruct->get_name() << "(" << tstruct->get_name() << "&&) noexcept;"
1144 << endl;
1145 }
1146
1147 // Assignment Operator
1148 indent(out) << tstruct->get_name() << "& operator=(const " << tstruct->get_name() << "&)"
1149 << (ok_noexcept? " noexcept" : "") << ';' << endl;
1150
1151 // Move assignment operator
1152 if (gen_moveable_) {
1153 indent(out) << tstruct->get_name() << "& operator=(" << tstruct->get_name() << "&&) noexcept;"
1154 << endl;
1155 }
1156
1157 bool has_default_value = false;
1158 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
1159 t_type* t = get_true_type((*m_iter)->get_type());
1160 if (is_reference(*m_iter) || t->is_string()) {
1161 t_const_value* cv = (*m_iter)->get_value();
1162 if (cv != nullptr) {
1163 has_default_value = true;
1164 break;
1165 }
1166 }
1167 }
1168
1169 // Default constructor
1170 std::string clsname_ctor = tstruct->get_name() + "()";
1171 indent(out) << clsname_ctor << (has_default_value ? "" : " noexcept");
1172
1173 bool init_ctor = false;
1174 std::string args_indent(
1175 indent().size() + clsname_ctor.size() + (has_default_value ? 3 : -1), ' ');
1176
1177 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
1178 t_type* t = get_true_type((*m_iter)->get_type());
1179 if (t->is_base_type() || t->is_enum() || is_reference(*m_iter)) {
1180 string dval;
1181 t_const_value* cv = (*m_iter)->get_value();
1182 if (cv != nullptr) {
1183 dval += render_const_value(out, (*m_iter)->get_name(), t, cv);
1184 } else if (t->is_enum()) {
1185 dval += "static_cast<" + type_name(t) + ">(0)";
1186 } else {
1187 dval += (t->is_string() || is_reference(*m_iter)) ? "" : "0";
1188 }
1189 if (!init_ctor) {
1190 init_ctor = true;
1191 if(has_default_value) {
1192 out << " : ";
1193 } else {
1194 out << '\n' << args_indent << ": ";
1195 args_indent.append(" ");
1196 }
1197 } else {
1198 out << ",\n" << args_indent;
1199 }
1200 out << (*m_iter)->get_name() << "(" << dval << ")";
1201 }
1202 }
1203 out << " {" << endl;
1204 indent_up();
1205 // TODO(dreiss): When everything else in Thrift is perfect,
1206 // do more of these in the initializer list.
1207 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
1208 t_type* t = get_true_type((*m_iter)->get_type());
1209 if (!t->is_base_type() && !t->is_enum() && !is_reference(*m_iter)) {
1210 t_const_value* cv = (*m_iter)->get_value();
1211 if (cv != nullptr) {
1212 print_const_value(out, (*m_iter)->get_name(), t, cv);
1213 }
1214 }
1215 }
1216 scope_down(out);
1217 }
1218
1219 if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) {
1220 out << endl << indent() << "virtual ~" << tstruct->get_name() << "() noexcept;" << endl;
1221 }
1222
1223 // Declare all fields
1224 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
1225 generate_java_doc(out, *m_iter);
1226 indent(out) << declare_field(*m_iter,
1227 false,
1228 (pointers && !(*m_iter)->get_type()->is_xception()),
1229 !read) << endl;
1230 }
1231
1232 // Add the __isset data member if we need it, using the definition from above
1233 if (has_nonrequired_fields && (!pointers || read)) {
1234 out << endl << indent() << "_" << tstruct->get_name() << "__isset __isset;" << endl;
1235 }
1236
1237 // Create a setter function for each field
1238 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
1239 if (pointers) {
1240 continue;
1241 }
1242 if (is_reference((*m_iter))) {
1243 out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "(::std::shared_ptr<"
1244 << type_name((*m_iter)->get_type(), false, false) << ">";
1245 out << " val);" << endl;
1246 } else {
1247 out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "("
1248 << type_name((*m_iter)->get_type(), false, true);
1249 out << " val);" << endl;
1250 }
1251 }
1252 out << endl;
1253
1254 if (!pointers) {
1255 // Should we generate default operators?
1256 if (!gen_no_default_operators_) {
1257 // Generate an equality testing operator. Make it inline since the compiler
1258 // will do a better job than we would when deciding whether to inline it.
1259 out << indent() << "bool operator == (const " << tstruct->get_name() << " & "
1260 << (members.size() > 0 ? "rhs" : "/* rhs */") << ") const" << endl;
1261 scope_up(out);
1262 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
1263 // Most existing Thrift code does not use isset or optional/required,
1264 // so we treat "default" fields as required.
1265 if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
1266 out << indent() << "if (!(" << (*m_iter)->get_name() << " == rhs."
1267 << (*m_iter)->get_name() << "))" << endl << indent() << " return false;" << endl;
1268 } else {
1269 out << indent() << "if (__isset." << (*m_iter)->get_name() << " != rhs.__isset."
1270 << (*m_iter)->get_name() << ")" << endl << indent() << " return false;" << endl
1271 << indent() << "else if (__isset." << (*m_iter)->get_name() << " && !("
1272 << (*m_iter)->get_name() << " == rhs." << (*m_iter)->get_name() << "))" << endl
1273 << indent() << " return false;" << endl;
1274 }
1275 }
1276 indent(out) << "return true;" << endl;
1277 scope_down(out);
1278 out << indent() << "bool operator != (const " << tstruct->get_name() << " &rhs) const {"
1279 << endl << indent() << " return !(*this == rhs);" << endl << indent() << "}" << endl
1280 << endl;
1281
1282 // Generate the declaration of a less-than operator. This must be
1283 // implemented by the application developer if they wish to use it. (They
1284 // will get a link error if they try to use it without an implementation.)
1285 out << indent() << "bool operator < (const " << tstruct->get_name() << " & ) const;" << endl
1286 << endl;
1287 }
1288 }
1289
1290 if (read) {
1291 if (gen_templates_) {
1292 out << indent() << "template <class Protocol_>" << endl << indent()
1293 << "uint32_t read(Protocol_* iprot);" << endl;
1294 } else {
1295 out << indent() << "uint32_t read("
1296 << "::apache::thrift::protocol::TProtocol* iprot)";
1297 if(!is_exception && !extends.empty())
1298 out << " override";
1299 out << ';' << endl;
1300 }
1301 }
1302 if (write) {
1303 if (gen_templates_) {
1304 out << indent() << "template <class Protocol_>" << endl << indent()
1305 << "uint32_t write(Protocol_* oprot) const;" << endl;
1306 } else {
1307 out << indent() << "uint32_t write("
1308 << "::apache::thrift::protocol::TProtocol* oprot) const";
1309 if(!is_exception && !extends.empty())
1310 out << " override";
1311 out << ';' << endl;
1312 }
1313 }
1314 out << endl;
1315
1316 if (is_user_struct && !has_custom_ostream(tstruct)) {
1317 out << indent() << "virtual ";
1318 generate_struct_print_method_decl(out, nullptr);
1319 out << ";" << endl;
1320 }
1321
1322 // std::exception::what()
1323 if (is_exception) {
1324 out << indent() << "mutable std::string thriftTExceptionMessageHolder_;" << endl;
1325 out << indent();
1326 generate_exception_what_method_decl(out, tstruct, false);
1327 out << ";" << endl;
1328 }
1329
1330 indent_down();
1331 indent(out) << "};" << endl << endl;
1332
1333 if (swap) {
1334 // Generate a namespace-scope swap() function
1335 if (tstruct->get_name() == "a" || tstruct->get_name() == "b") {
1336 out << indent() << "void swap(" << tstruct->get_name() << " &a1, " << tstruct->get_name()
1337 << " &a2);" << endl << endl;
1338 } else {
1339 out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name()
1340 << " &b);" << endl << endl;
1341 }
1342 }
1343
1344 if (is_user_struct) {
1345 generate_struct_ostream_operator_decl(out, tstruct);
1346 }
1347 }
1348
generate_struct_definition(ostream & out,ostream & force_cpp_out,t_struct * tstruct,bool setters,bool is_user_struct)1349 void t_cpp_generator::generate_struct_definition(ostream& out,
1350 ostream& force_cpp_out,
1351 t_struct* tstruct,
1352 bool setters,
1353 bool is_user_struct) {
1354 // Get members
1355 vector<t_field*>::const_iterator m_iter;
1356 const vector<t_field*>& members = tstruct->get_members();
1357
1358 // Destructor
1359 if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) {
1360 force_cpp_out << endl << indent() << tstruct->get_name() << "::~" << tstruct->get_name()
1361 << "() noexcept {" << endl;
1362 indent_up();
1363
1364 indent_down();
1365 force_cpp_out << indent() << "}" << endl << endl;
1366 }
1367
1368 // Create a setter function for each field
1369 if (setters) {
1370 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
1371 if (is_reference((*m_iter))) {
1372 out << endl << indent() << "void " << tstruct->get_name() << "::__set_"
1373 << (*m_iter)->get_name() << "(::std::shared_ptr<"
1374 << type_name((*m_iter)->get_type(), false, false) << ">";
1375 out << " val) {" << endl;
1376 } else {
1377 out << endl << indent() << "void " << tstruct->get_name() << "::__set_"
1378 << (*m_iter)->get_name() << "(" << type_name((*m_iter)->get_type(), false, true);
1379 out << " val) {" << endl;
1380 }
1381 indent_up();
1382 out << indent() << "this->" << (*m_iter)->get_name() << " = val;" << endl;
1383 indent_down();
1384
1385 // assume all fields are required except optional fields.
1386 // for optional fields change __isset.name to true
1387 bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL;
1388 if (is_optional) {
1389 out << indent() << indent() << "__isset." << (*m_iter)->get_name() << " = true;" << endl;
1390 }
1391 out << indent() << "}" << endl;
1392 }
1393 }
1394 if (is_user_struct) {
1395 generate_struct_ostream_operator(out, tstruct);
1396 }
1397 out << endl;
1398 }
1399
1400 /**
1401 * Makes a helper function to gen a struct reader.
1402 *
1403 * @param out Stream to write to
1404 * @param tstruct The struct
1405 */
generate_struct_reader(ostream & out,t_struct * tstruct,bool pointers)1406 void t_cpp_generator::generate_struct_reader(ostream& out, t_struct* tstruct, bool pointers) {
1407 if (gen_templates_) {
1408 out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t "
1409 << tstruct->get_name() << "::read(Protocol_* iprot) {" << endl;
1410 } else {
1411 indent(out) << "uint32_t " << tstruct->get_name()
1412 << "::read(::apache::thrift::protocol::TProtocol* iprot) {" << endl;
1413 }
1414 indent_up();
1415
1416 const vector<t_field*>& fields = tstruct->get_members();
1417 vector<t_field*>::const_iterator f_iter;
1418
1419 // Declare stack tmp variables
1420 out << endl
1421 << indent() << "::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);" << endl
1422 << indent() << "uint32_t xfer = 0;" << endl
1423 << indent() << "std::string fname;" << endl
1424 << indent() << "::apache::thrift::protocol::TType ftype;" << endl
1425 << indent() << "int16_t fid;" << endl
1426 << endl
1427 << indent() << "xfer += iprot->readStructBegin(fname);" << endl
1428 << endl
1429 << indent() << "using ::apache::thrift::protocol::TProtocolException;" << endl
1430 << endl;
1431
1432 // Required variables aren't in __isset, so we need tmp vars to check them.
1433 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1434 if ((*f_iter)->get_req() == t_field::T_REQUIRED)
1435 indent(out) << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl;
1436 }
1437 out << endl;
1438
1439 // Loop over reading in fields
1440 indent(out) << "while (true)" << endl;
1441 scope_up(out);
1442
1443 // Read beginning field marker
1444 indent(out) << "xfer += iprot->readFieldBegin(fname, ftype, fid);" << endl;
1445
1446 // Check for field STOP marker
1447 out << indent() << "if (ftype == ::apache::thrift::protocol::T_STOP) {" << endl << indent()
1448 << " break;" << endl << indent() << "}" << endl;
1449
1450 if (fields.empty()) {
1451 out << indent() << "xfer += iprot->skip(ftype);" << endl;
1452 } else {
1453 // Switch statement on the field we are reading
1454 indent(out) << "switch (fid)" << endl;
1455
1456 scope_up(out);
1457
1458 // Generate deserialization code for known cases
1459 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1460 indent(out) << "case " << (*f_iter)->get_key() << ":" << endl;
1461 indent_up();
1462 indent(out) << "if (ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
1463 indent_up();
1464
1465 const char* isset_prefix = ((*f_iter)->get_req() != t_field::T_REQUIRED) ? "this->__isset."
1466 : "isset_";
1467
1468 #if 0
1469 // This code throws an exception if the same field is encountered twice.
1470 // We've decided to leave it out for performance reasons.
1471 // TODO(dreiss): Generate this code and "if" it out to make it easier
1472 // for people recompiling thrift to include it.
1473 out <<
1474 indent() << "if (" << isset_prefix << (*f_iter)->get_name() << ")" << endl <<
1475 indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl;
1476 #endif
1477
1478 if (pointers && !(*f_iter)->get_type()->is_xception()) {
1479 generate_deserialize_field(out, *f_iter, "(*(this->", "))");
1480 } else {
1481 generate_deserialize_field(out, *f_iter, "this->");
1482 }
1483 out << indent() << isset_prefix << (*f_iter)->get_name() << " = true;" << endl;
1484 indent_down();
1485 out << indent() << "} else {" << endl << indent() << " xfer += iprot->skip(ftype);" << endl
1486 <<
1487 // TODO(dreiss): Make this an option when thrift structs
1488 // have a common base class.
1489 // indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl <<
1490 indent() << "}" << endl << indent() << "break;" << endl;
1491 indent_down();
1492 }
1493
1494 // In the default case we skip the field
1495 out << indent() << "default:" << endl << indent() << " xfer += iprot->skip(ftype);" << endl
1496 << indent() << " break;" << endl;
1497
1498 scope_down(out);
1499 } //!fields.empty()
1500 // Read field end marker
1501 indent(out) << "xfer += iprot->readFieldEnd();" << endl;
1502
1503 scope_down(out);
1504
1505 out << endl << indent() << "xfer += iprot->readStructEnd();" << endl;
1506
1507 // Throw if any required fields are missing.
1508 // We do this after reading the struct end so that
1509 // there might possibly be a chance of continuing.
1510 out << endl;
1511 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1512 if ((*f_iter)->get_req() == t_field::T_REQUIRED)
1513 out << indent() << "if (!isset_" << (*f_iter)->get_name() << ')' << endl << indent()
1514 << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl;
1515 }
1516
1517 indent(out) << "return xfer;" << endl;
1518
1519 indent_down();
1520 indent(out) << "}" << endl << endl;
1521 }
1522
1523 /**
1524 * Generates the write function.
1525 *
1526 * @param out Stream to write to
1527 * @param tstruct The struct
1528 */
generate_struct_writer(ostream & out,t_struct * tstruct,bool pointers)1529 void t_cpp_generator::generate_struct_writer(ostream& out, t_struct* tstruct, bool pointers) {
1530 string name = tstruct->get_name();
1531 const vector<t_field*>& fields = tstruct->get_sorted_members();
1532 vector<t_field*>::const_iterator f_iter;
1533
1534 if (gen_templates_) {
1535 out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t "
1536 << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl;
1537 } else {
1538 indent(out) << "uint32_t " << tstruct->get_name()
1539 << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl;
1540 }
1541 indent_up();
1542
1543 out << indent() << "uint32_t xfer = 0;" << endl;
1544
1545 indent(out) << "::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);" << endl;
1546 indent(out) << "xfer += oprot->writeStructBegin(\"" << name << "\");" << endl;
1547
1548 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1549 bool check_if_set = (*f_iter)->get_req() == t_field::T_OPTIONAL
1550 || (*f_iter)->get_type()->is_xception();
1551 if (check_if_set) {
1552 out << endl << indent() << "if (this->__isset." << (*f_iter)->get_name() << ") {" << endl;
1553 indent_up();
1554 } else {
1555 out << endl;
1556 }
1557
1558 // Write field header
1559 out << indent() << "xfer += oprot->writeFieldBegin("
1560 << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", "
1561 << (*f_iter)->get_key() << ");" << endl;
1562 // Write field contents
1563 if (pointers && !(*f_iter)->get_type()->is_xception()) {
1564 generate_serialize_field(out, *f_iter, "(*(this->", "))");
1565 } else {
1566 generate_serialize_field(out, *f_iter, "this->");
1567 }
1568 // Write field closer
1569 indent(out) << "xfer += oprot->writeFieldEnd();" << endl;
1570 if (check_if_set) {
1571 indent_down();
1572 indent(out) << '}';
1573 }
1574 }
1575
1576 out << endl;
1577
1578 // Write the struct map
1579 out << indent() << "xfer += oprot->writeFieldStop();" << endl << indent()
1580 << "xfer += oprot->writeStructEnd();" << endl << indent()
1581 << "return xfer;" << endl;
1582
1583 indent_down();
1584 indent(out) << "}" << endl << endl;
1585 }
1586
1587 /**
1588 * Struct writer for result of a function, which can have only one of its
1589 * fields set and does a conditional if else look up into the __isset field
1590 * of the struct.
1591 *
1592 * @param out Output stream
1593 * @param tstruct The result struct
1594 */
generate_struct_result_writer(ostream & out,t_struct * tstruct,bool pointers)1595 void t_cpp_generator::generate_struct_result_writer(ostream& out,
1596 t_struct* tstruct,
1597 bool pointers) {
1598 string name = tstruct->get_name();
1599 const vector<t_field*>& fields = tstruct->get_sorted_members();
1600 vector<t_field*>::const_iterator f_iter;
1601
1602 if (gen_templates_) {
1603 out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t "
1604 << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl;
1605 } else {
1606 indent(out) << "uint32_t " << tstruct->get_name()
1607 << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl;
1608 }
1609 indent_up();
1610
1611 out << endl << indent() << "uint32_t xfer = 0;" << endl << endl;
1612
1613 indent(out) << "xfer += oprot->writeStructBegin(\"" << name << "\");" << endl;
1614
1615 bool first = true;
1616 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1617 if (first) {
1618 first = false;
1619 out << endl << indent() << "if ";
1620 } else {
1621 out << " else if ";
1622 }
1623
1624 out << "(this->__isset." << (*f_iter)->get_name() << ") {" << endl;
1625
1626 indent_up();
1627
1628 // Write field header
1629 out << indent() << "xfer += oprot->writeFieldBegin("
1630 << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", "
1631 << (*f_iter)->get_key() << ");" << endl;
1632 // Write field contents
1633 if (pointers) {
1634 generate_serialize_field(out, *f_iter, "(*(this->", "))");
1635 } else {
1636 generate_serialize_field(out, *f_iter, "this->");
1637 }
1638 // Write field closer
1639 indent(out) << "xfer += oprot->writeFieldEnd();" << endl;
1640
1641 indent_down();
1642 indent(out) << "}";
1643 }
1644
1645 // Write the struct map
1646 out << endl << indent() << "xfer += oprot->writeFieldStop();" << endl << indent()
1647 << "xfer += oprot->writeStructEnd();" << endl << indent() << "return xfer;" << endl;
1648
1649 indent_down();
1650 indent(out) << "}" << endl << endl;
1651 }
1652
1653 /**
1654 * Generates the swap function.
1655 *
1656 * @param out Stream to write to
1657 * @param tstruct The struct
1658 */
generate_struct_swap(ostream & out,t_struct * tstruct)1659 void t_cpp_generator::generate_struct_swap(ostream& out, t_struct* tstruct) {
1660 if (tstruct->get_name() == "a" || tstruct->get_name() == "b") {
1661 out << indent() << "void swap(" << tstruct->get_name() << " &a1, " << tstruct->get_name()
1662 << " &a2) {" << endl;
1663 } else {
1664 out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name()
1665 << " &b) {" << endl;
1666 }
1667
1668 indent_up();
1669
1670 // Let argument-dependent name lookup find the correct swap() function to
1671 // use based on the argument types. If none is found in the arguments'
1672 // namespaces, fall back to ::std::swap().
1673 out << indent() << "using ::std::swap;" << endl;
1674
1675 bool has_nonrequired_fields = false;
1676 const vector<t_field*>& fields = tstruct->get_members();
1677 for (auto tfield : fields) {
1678 if (tfield->get_req() != t_field::T_REQUIRED) {
1679 has_nonrequired_fields = true;
1680 }
1681
1682 if (tstruct->get_name() == "a" || tstruct->get_name() == "b") {
1683 out << indent() << "swap(a1." << tfield->get_name() << ", a2." << tfield->get_name() << ");"
1684 << endl;
1685 } else {
1686 out << indent() << "swap(a." << tfield->get_name() << ", b." << tfield->get_name() << ");"
1687 << endl;
1688 }
1689 }
1690
1691 if (has_nonrequired_fields) {
1692 if (tstruct->get_name() == "a" || tstruct->get_name() == "b") {
1693 out << indent() << "swap(a1.__isset, a2.__isset);" << endl;
1694 } else {
1695 out << indent() << "swap(a.__isset, b.__isset);" << endl;
1696 }
1697 }
1698
1699 // handle empty structs
1700 if (fields.size() == 0) {
1701 if (tstruct->get_name() == "a" || tstruct->get_name() == "b") {
1702 out << indent() << "(void) a1;" << endl;
1703 out << indent() << "(void) a2;" << endl;
1704 } else {
1705 out << indent() << "(void) a;" << endl;
1706 out << indent() << "(void) b;" << endl;
1707 }
1708 }
1709
1710 scope_down(out);
1711 out << endl;
1712 }
1713
generate_struct_ostream_operator_decl(std::ostream & out,t_struct * tstruct)1714 void t_cpp_generator::generate_struct_ostream_operator_decl(std::ostream& out, t_struct* tstruct) {
1715 out << "std::ostream& operator<<(std::ostream& out, const "
1716 << tstruct->get_name()
1717 << "& obj);" << endl;
1718 out << endl;
1719 }
1720
generate_struct_ostream_operator(std::ostream & out,t_struct * tstruct)1721 void t_cpp_generator::generate_struct_ostream_operator(std::ostream& out, t_struct* tstruct) {
1722 if (!has_custom_ostream(tstruct)) {
1723 // thrift defines this behavior
1724 out << "std::ostream& operator<<(std::ostream& out, const "
1725 << tstruct->get_name()
1726 << "& obj)" << endl;
1727 scope_up(out);
1728 out << indent() << "obj.printTo(out);" << endl
1729 << indent() << "return out;" << endl;
1730 scope_down(out);
1731 out << endl;
1732 }
1733 }
1734
generate_struct_print_method_decl(std::ostream & out,t_struct * tstruct)1735 void t_cpp_generator::generate_struct_print_method_decl(std::ostream& out, t_struct* tstruct) {
1736 out << "void ";
1737 if (tstruct) {
1738 out << tstruct->get_name() << "::";
1739 }
1740 out << "printTo(std::ostream& out) const";
1741 }
1742
generate_exception_what_method_decl(std::ostream & out,t_struct * tstruct,bool external)1743 void t_cpp_generator::generate_exception_what_method_decl(std::ostream& out,
1744 t_struct* tstruct,
1745 bool external) {
1746 out << "const char* ";
1747 if (external) {
1748 out << tstruct->get_name() << "::";
1749 }
1750 out << "what() const noexcept";
1751 if(!external)
1752 out << " override";
1753 }
1754
1755 namespace struct_ostream_operator_generator {
generate_required_field_value(std::ostream & out,const t_field * field)1756 void generate_required_field_value(std::ostream& out, const t_field* field) {
1757 out << " << to_string(" << field->get_name() << ")";
1758 }
1759
generate_optional_field_value(std::ostream & out,const t_field * field)1760 void generate_optional_field_value(std::ostream& out, const t_field* field) {
1761 out << "; (__isset." << field->get_name() << " ? (out";
1762 generate_required_field_value(out, field);
1763 out << ") : (out << \"<null>\"))";
1764 }
1765
generate_field_value(std::ostream & out,const t_field * field)1766 void generate_field_value(std::ostream& out, const t_field* field) {
1767 if (field->get_req() == t_field::T_OPTIONAL)
1768 generate_optional_field_value(out, field);
1769 else
1770 generate_required_field_value(out, field);
1771 }
1772
generate_field_name(std::ostream & out,const t_field * field)1773 void generate_field_name(std::ostream& out, const t_field* field) {
1774 out << "\"" << field->get_name() << "=\"";
1775 }
1776
generate_field(std::ostream & out,const t_field * field)1777 void generate_field(std::ostream& out, const t_field* field) {
1778 generate_field_name(out, field);
1779 generate_field_value(out, field);
1780 }
1781
generate_fields(std::ostream & out,const vector<t_field * > & fields,const std::string & indent)1782 void generate_fields(std::ostream& out,
1783 const vector<t_field*>& fields,
1784 const std::string& indent) {
1785 const vector<t_field*>::const_iterator beg = fields.begin();
1786 const vector<t_field*>::const_iterator end = fields.end();
1787
1788 for (vector<t_field*>::const_iterator it = beg; it != end; ++it) {
1789 out << indent << "out << ";
1790
1791 if (it != beg) {
1792 out << "\", \" << ";
1793 }
1794
1795 generate_field(out, *it);
1796 out << ";" << endl;
1797 }
1798 }
1799 }
1800
1801 /**
1802 * Generates operator<<
1803 */
generate_struct_print_method(std::ostream & out,t_struct * tstruct)1804 void t_cpp_generator::generate_struct_print_method(std::ostream& out, t_struct* tstruct) {
1805 out << indent();
1806 generate_struct_print_method_decl(out, tstruct);
1807 out << " {" << endl;
1808
1809 indent_up();
1810
1811 out << indent() << "using ::apache::thrift::to_string;" << endl;
1812 out << indent() << "out << \"" << tstruct->get_name() << "(\";" << endl;
1813 struct_ostream_operator_generator::generate_fields(out, tstruct->get_members(), indent());
1814 out << indent() << "out << \")\";" << endl;
1815
1816 indent_down();
1817 out << "}" << endl << endl;
1818 }
1819
1820 /**
1821 * Generates what() method for exceptions
1822 */
generate_exception_what_method(std::ostream & out,t_struct * tstruct)1823 void t_cpp_generator::generate_exception_what_method(std::ostream& out, t_struct* tstruct) {
1824 out << indent();
1825 generate_exception_what_method_decl(out, tstruct, true);
1826 out << " {" << endl;
1827
1828 indent_up();
1829 out << indent() << "try {" << endl;
1830
1831 indent_up();
1832 out << indent() << "std::stringstream ss;" << endl;
1833 out << indent() << "ss << \"TException - service has thrown: \" << *this;" << endl;
1834 out << indent() << "this->thriftTExceptionMessageHolder_ = ss.str();" << endl;
1835 out << indent() << "return this->thriftTExceptionMessageHolder_.c_str();" << endl;
1836 indent_down();
1837
1838 out << indent() << "} catch (const std::exception&) {" << endl;
1839
1840 indent_up();
1841 out << indent() << "return \"TException - service has thrown: " << tstruct->get_name() << "\";"
1842 << endl;
1843 indent_down();
1844
1845 out << indent() << "}" << endl;
1846
1847 indent_down();
1848 out << "}" << endl << endl;
1849 }
1850
1851 /**
1852 * Generates a thrift service. In C++, this comprises an entirely separate
1853 * header and source file. The header file defines the methods and includes
1854 * the data types defined in the main header file, and the implementation
1855 * file contains implementations of the basic printer and default interfaces.
1856 *
1857 * @param tservice The service definition
1858 */
generate_service(t_service * tservice)1859 void t_cpp_generator::generate_service(t_service* tservice) {
1860 string svcname = tservice->get_name();
1861
1862 // Make output files
1863 string f_header_name = get_out_dir() + svcname + ".h";
1864 f_header_.open(f_header_name.c_str());
1865
1866 // Print header file includes
1867 f_header_ << autogen_comment();
1868 f_header_ << "#ifndef " << svcname << "_H" << endl << "#define " << svcname << "_H" << endl
1869 << endl;
1870 if (gen_cob_style_) {
1871 f_header_ << "#include <thrift/transport/TBufferTransports.h>" << endl // TMemoryBuffer
1872 << "#include <functional>" << endl
1873 << "namespace apache { namespace thrift { namespace async {" << endl
1874 << "class TAsyncChannel;" << endl << "}}}" << endl;
1875 }
1876 f_header_ << "#include <thrift/TDispatchProcessor.h>" << endl;
1877 if (gen_cob_style_) {
1878 f_header_ << "#include <thrift/async/TAsyncDispatchProcessor.h>" << endl;
1879 }
1880 f_header_ << "#include <thrift/async/TConcurrentClientSyncInfo.h>" << endl;
1881 f_header_ << "#include <memory>" << endl;
1882 f_header_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ << "_types.h\""
1883 << endl;
1884
1885 t_service* extends_service = tservice->get_extends();
1886 if (extends_service != nullptr) {
1887 f_header_ << "#include \"" << get_include_prefix(*(extends_service->get_program()))
1888 << extends_service->get_name() << ".h\"" << endl;
1889 }
1890
1891 f_header_ << endl << ns_open_ << endl << endl;
1892
1893 f_header_ << "#ifdef _MSC_VER\n"
1894 " #pragma warning( push )\n"
1895 " #pragma warning (disable : 4250 ) //inheriting methods via dominance \n"
1896 "#endif\n\n";
1897
1898 // Service implementation file includes
1899 string f_service_name = get_out_dir() + svcname + ".cpp";
1900 f_service_.open(f_service_name.c_str());
1901 f_service_ << autogen_comment();
1902 f_service_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl;
1903 if (gen_cob_style_) {
1904 f_service_ << "#include \"thrift/async/TAsyncChannel.h\"" << endl;
1905 }
1906 if (gen_templates_) {
1907 f_service_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".tcc\""
1908 << endl;
1909
1910 string f_service_tcc_name = get_out_dir() + svcname + ".tcc";
1911 f_service_tcc_.open(f_service_tcc_name.c_str());
1912 f_service_tcc_ << autogen_comment();
1913 f_service_tcc_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\""
1914 << endl;
1915
1916 f_service_tcc_ << "#ifndef " << svcname << "_TCC" << endl << "#define " << svcname << "_TCC"
1917 << endl << endl;
1918
1919 if (gen_cob_style_) {
1920 f_service_tcc_ << "#include \"thrift/async/TAsyncChannel.h\"" << endl;
1921 }
1922 }
1923
1924 f_service_ << endl << ns_open_ << endl << endl;
1925 f_service_tcc_ << endl << ns_open_ << endl << endl;
1926
1927 // Generate all the components
1928 generate_service_interface(tservice, "");
1929 generate_service_interface_factory(tservice, "");
1930 generate_service_null(tservice, "");
1931 generate_service_helpers(tservice);
1932 generate_service_client(tservice, "");
1933 generate_service_processor(tservice, "");
1934 generate_service_multiface(tservice);
1935 generate_service_client(tservice, "Concurrent");
1936
1937 // Generate skeleton
1938 if (!gen_no_skeleton_) {
1939 generate_service_skeleton(tservice);
1940 }
1941
1942 // Generate all the cob components
1943 if (gen_cob_style_) {
1944 generate_service_interface(tservice, "CobCl");
1945 generate_service_interface(tservice, "CobSv");
1946 generate_service_interface_factory(tservice, "CobSv");
1947 generate_service_null(tservice, "CobSv");
1948 generate_service_client(tservice, "Cob");
1949 generate_service_processor(tservice, "Cob");
1950
1951 if (!gen_no_skeleton_) {
1952 generate_service_async_skeleton(tservice);
1953 }
1954
1955 }
1956
1957 f_header_ << "#ifdef _MSC_VER\n"
1958 " #pragma warning( pop )\n"
1959 "#endif\n\n";
1960
1961 // Close the namespace
1962 f_service_ << ns_close_ << endl << endl;
1963 f_service_tcc_ << ns_close_ << endl << endl;
1964 f_header_ << ns_close_ << endl << endl;
1965
1966 // TODO(simpkins): Make this a separate option
1967 if (gen_templates_) {
1968 f_header_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".tcc\"" << endl
1969 << "#include \"" << get_include_prefix(*get_program()) << program_name_
1970 << "_types.tcc\"" << endl << endl;
1971 }
1972
1973 f_header_ << "#endif" << endl;
1974 f_service_tcc_ << "#endif" << endl;
1975
1976 // Close the files
1977 f_service_tcc_.close();
1978 f_service_.close();
1979 f_header_.close();
1980 }
1981
1982 /**
1983 * Generates helper functions for a service. Basically, this generates types
1984 * for all the arguments and results to functions.
1985 *
1986 * @param tservice The service to generate a header definition for
1987 */
generate_service_helpers(t_service * tservice)1988 void t_cpp_generator::generate_service_helpers(t_service* tservice) {
1989 vector<t_function*> functions = tservice->get_functions();
1990 vector<t_function*>::iterator f_iter;
1991 std::ostream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
1992
1993 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1994 t_struct* ts = (*f_iter)->get_arglist();
1995 string name_orig = ts->get_name();
1996
1997 // TODO(dreiss): Why is this stuff not in generate_function_helpers?
1998 ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_args");
1999 generate_struct_declaration(f_header_, ts, false);
2000 generate_struct_definition(out, f_service_, ts, false);
2001 generate_struct_reader(out, ts);
2002 generate_struct_writer(out, ts);
2003 ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_pargs");
2004 generate_struct_declaration(f_header_, ts, false, true, false, true);
2005 generate_struct_definition(out, f_service_, ts, false);
2006 generate_struct_writer(out, ts, true);
2007 ts->set_name(name_orig);
2008
2009 generate_function_helpers(tservice, *f_iter);
2010 }
2011 }
2012
2013 /**
2014 * Generates a service interface definition.
2015 *
2016 * @param tservice The service to generate a header definition for
2017 */
generate_service_interface(t_service * tservice,string style)2018 void t_cpp_generator::generate_service_interface(t_service* tservice, string style) {
2019
2020 string service_if_name = service_name_ + style + "If";
2021 if (style == "CobCl") {
2022 // Forward declare the client.
2023 string client_name = service_name_ + "CobClient";
2024 if (gen_templates_) {
2025 client_name += "T";
2026 service_if_name += "T";
2027 indent(f_header_) << "template <class Protocol_>" << endl;
2028 }
2029 indent(f_header_) << "class " << client_name << ";" << endl << endl;
2030 }
2031
2032 string extends = "";
2033 if (tservice->get_extends() != nullptr) {
2034 extends = " : virtual public " + type_name(tservice->get_extends()) + style + "If";
2035 if (style == "CobCl" && gen_templates_) {
2036 // TODO(simpkins): If gen_templates_ is enabled, we currently assume all
2037 // parent services were also generated with templates enabled.
2038 extends += "T<Protocol_>";
2039 }
2040 }
2041
2042 if (style == "CobCl" && gen_templates_) {
2043 f_header_ << "template <class Protocol_>" << endl;
2044 }
2045
2046 generate_java_doc(f_header_, tservice);
2047
2048 f_header_ << "class " << service_if_name << extends << " {" << endl << " public:" << endl;
2049 indent_up();
2050 f_header_ << indent() << "virtual ~" << service_if_name << "() {}" << endl;
2051
2052 vector<t_function*> functions = tservice->get_functions();
2053 vector<t_function*>::iterator f_iter;
2054 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
2055 if ((*f_iter)->has_doc())
2056 f_header_ << endl;
2057 generate_java_doc(f_header_, *f_iter);
2058 f_header_ << indent() << "virtual " << function_signature(*f_iter, style) << " = 0;" << endl;
2059 }
2060 indent_down();
2061 f_header_ << "};" << endl << endl;
2062
2063 if (style == "CobCl" && gen_templates_) {
2064 // generate a backwards-compatible typedef for clients that do not
2065 // know about the new template-style code
2066 f_header_ << "typedef " << service_if_name << "< ::apache::thrift::protocol::TProtocol> "
2067 << service_name_ << style << "If;" << endl << endl;
2068 }
2069 }
2070
2071 /**
2072 * Generates a service interface factory.
2073 *
2074 * @param tservice The service to generate an interface factory for.
2075 */
generate_service_interface_factory(t_service * tservice,string style)2076 void t_cpp_generator::generate_service_interface_factory(t_service* tservice, string style) {
2077 string service_if_name = service_name_ + style + "If";
2078
2079 // Figure out the name of the upper-most parent class.
2080 // Getting everything to work out properly with inheritance is annoying.
2081 // Here's what we're doing for now:
2082 //
2083 // - All handlers implement getHandler(), but subclasses use covariant return
2084 // types to return their specific service interface class type. We have to
2085 // use raw pointers because of this; shared_ptr<> can't be used for
2086 // covariant return types.
2087 //
2088 // - Since we're not using shared_ptr<>, we also provide a releaseHandler()
2089 // function that must be called to release a pointer to a handler obtained
2090 // via getHandler().
2091 //
2092 // releaseHandler() always accepts a pointer to the upper-most parent class
2093 // type. This is necessary since the parent versions of releaseHandler()
2094 // may accept any of the parent types, not just the most specific subclass
2095 // type. Implementations can use dynamic_cast to cast the pointer to the
2096 // subclass type if desired.
2097 t_service* base_service = tservice;
2098 while (base_service->get_extends() != nullptr) {
2099 base_service = base_service->get_extends();
2100 }
2101 string base_if_name = type_name(base_service) + style + "If";
2102
2103 // Generate the abstract factory class
2104 string factory_name = service_if_name + "Factory";
2105 string extends;
2106 if (tservice->get_extends() != nullptr) {
2107 extends = " : virtual public " + type_name(tservice->get_extends()) + style + "IfFactory";
2108 }
2109
2110 f_header_ << "class " << factory_name << extends << " {" << endl << " public:" << endl;
2111 indent_up();
2112 f_header_ << indent() << "typedef " << service_if_name << " Handler;" << endl << endl << indent()
2113 << "virtual ~" << factory_name << "() {}" << endl << endl << indent() << "virtual "
2114 << service_if_name << "* getHandler("
2115 << "const ::apache::thrift::TConnectionInfo& connInfo)"
2116 << (extends.empty() ? "" : " override") << " = 0;" << endl << indent()
2117 << "virtual void releaseHandler(" << base_if_name << "* /* handler */)"
2118 << (extends.empty() ? "" : " override") << " = 0;" << endl << indent();
2119
2120 indent_down();
2121 f_header_ << "};" << endl << endl;
2122
2123 // Generate the singleton factory class
2124 string singleton_factory_name = service_if_name + "SingletonFactory";
2125 f_header_ << "class " << singleton_factory_name << " : virtual public " << factory_name << " {"
2126 << endl << " public:" << endl;
2127 indent_up();
2128 f_header_ << indent() << singleton_factory_name << "(const ::std::shared_ptr<" << service_if_name
2129 << ">& iface) : iface_(iface) {}" << endl << indent() << "virtual ~"
2130 << singleton_factory_name << "() {}" << endl << endl << indent() << "virtual "
2131 << service_if_name << "* getHandler("
2132 << "const ::apache::thrift::TConnectionInfo&) override {" << endl << indent()
2133 << " return iface_.get();" << endl << indent() << "}" << endl << indent()
2134 << "virtual void releaseHandler(" << base_if_name << "* /* handler */) override {}" << endl;
2135
2136 f_header_ << endl << " protected:" << endl << indent() << "::std::shared_ptr<" << service_if_name
2137 << "> iface_;" << endl;
2138
2139 indent_down();
2140 f_header_ << "};" << endl << endl;
2141 }
2142
2143 /**
2144 * Generates a null implementation of the service.
2145 *
2146 * @param tservice The service to generate a header definition for
2147 */
generate_service_null(t_service * tservice,string style)2148 void t_cpp_generator::generate_service_null(t_service* tservice, string style) {
2149 string extends = "";
2150 if (tservice->get_extends() != nullptr) {
2151 extends = " , virtual public " + type_name(tservice->get_extends()) + style + "Null";
2152 }
2153 f_header_ << "class " << service_name_ << style << "Null : virtual public " << service_name_
2154 << style << "If" << extends << " {" << endl << " public:" << endl;
2155 indent_up();
2156 f_header_ << indent() << "virtual ~" << service_name_ << style << "Null() {}" << endl;
2157 vector<t_function*> functions = tservice->get_functions();
2158 vector<t_function*>::iterator f_iter;
2159 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
2160 f_header_ << indent() << function_signature(*f_iter, style, "", false)
2161 << " override {" << endl;
2162 indent_up();
2163
2164 t_type* returntype = (*f_iter)->get_returntype();
2165 t_field returnfield(returntype, "_return");
2166
2167 if (style == "") {
2168 if (returntype->is_void() || is_complex_type(returntype)) {
2169 f_header_ << indent() << "return;" << endl;
2170 } else {
2171 f_header_ << indent() << declare_field(&returnfield, true) << endl << indent()
2172 << "return _return;" << endl;
2173 }
2174 } else if (style == "CobSv") {
2175 if (returntype->is_void()) {
2176 f_header_ << indent() << "return cob();" << endl;
2177 } else {
2178 t_field returnfield(returntype, "_return");
2179 f_header_ << indent() << declare_field(&returnfield, true) << endl << indent()
2180 << "return cob(_return);" << endl;
2181 }
2182
2183 } else {
2184 throw "UNKNOWN STYLE";
2185 }
2186
2187 indent_down();
2188 f_header_ << indent() << "}" << endl;
2189 }
2190 indent_down();
2191 f_header_ << "};" << endl << endl;
2192 }
2193
generate_function_call(ostream & out,t_function * tfunction,string target,string iface,string arg_prefix)2194 void t_cpp_generator::generate_function_call(ostream& out,
2195 t_function* tfunction,
2196 string target,
2197 string iface,
2198 string arg_prefix) {
2199 bool first = true;
2200 t_type* ret_type = get_true_type(tfunction->get_returntype());
2201 out << indent();
2202 if (!tfunction->is_oneway() && !ret_type->is_void()) {
2203 if (is_complex_type(ret_type)) {
2204 first = false;
2205 out << iface << "->" << tfunction->get_name() << "(" << target;
2206 } else {
2207 out << target << " = " << iface << "->" << tfunction->get_name() << "(";
2208 }
2209 } else {
2210 out << iface << "->" << tfunction->get_name() << "(";
2211 }
2212 const std::vector<t_field*>& fields = tfunction->get_arglist()->get_members();
2213 vector<t_field*>::const_iterator f_iter;
2214 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
2215 if (first) {
2216 first = false;
2217 } else {
2218 out << ", ";
2219 }
2220 out << arg_prefix << (*f_iter)->get_name();
2221 }
2222 out << ");" << endl;
2223 }
2224
generate_service_async_skeleton(t_service * tservice)2225 void t_cpp_generator::generate_service_async_skeleton(t_service* tservice) {
2226 string svcname = tservice->get_name();
2227
2228 // Service implementation file includes
2229 string f_skeleton_name = get_out_dir() + svcname + "_async_server.skeleton.cpp";
2230
2231 string ns = namespace_prefix(tservice->get_program()->get_namespace("cpp"));
2232
2233 ofstream_with_content_based_conditional_update f_skeleton;
2234 f_skeleton.open(f_skeleton_name.c_str());
2235 f_skeleton << "// This autogenerated skeleton file illustrates one way to adapt a synchronous"
2236 << endl << "// interface into an asynchronous interface. You should copy it to another"
2237 << endl
2238 << "// filename to avoid overwriting it and rewrite as asynchronous any functions"
2239 << endl << "// that would otherwise introduce unwanted latency." << endl << endl
2240 << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl
2241 << "#include <thrift/protocol/TBinaryProtocol.h>" << endl
2242 << "#include <thrift/async/TAsyncProtocolProcessor.h>" << endl
2243 << "#include <thrift/async/TEvhttpServer.h>" << endl
2244 << "#include <event.h>" << endl
2245 << "#include <evhttp.h>" << endl << endl
2246 << "using namespace ::apache::thrift;" << endl
2247 << "using namespace ::apache::thrift::protocol;" << endl
2248 << "using namespace ::apache::thrift::transport;" << endl
2249 << "using namespace ::apache::thrift::async;" << endl << endl;
2250
2251 // the following code would not compile:
2252 // using namespace ;
2253 // using namespace ::;
2254 if ((!ns.empty()) && (ns.compare(" ::") != 0)) {
2255 f_skeleton << "using namespace " << string(ns, 0, ns.size() - 2) << ";" << endl << endl;
2256 }
2257
2258 f_skeleton << "class " << svcname << "Handler : virtual public " << svcname << "If {" << endl
2259 << " public:" << endl;
2260 indent_up();
2261 f_skeleton << indent() << svcname << "Handler() {" << endl << indent()
2262 << " // Your initialization goes here" << endl << indent() << "}" << endl << endl;
2263
2264 vector<t_function*> functions = tservice->get_functions();
2265 vector<t_function*>::iterator f_iter;
2266 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
2267 generate_java_doc(f_skeleton, *f_iter);
2268 f_skeleton << indent() << function_signature(*f_iter, "") << " {" << endl << indent()
2269 << " // Your implementation goes here" << endl << indent() << " printf(\""
2270 << (*f_iter)->get_name() << "\\n\");" << endl << indent() << "}" << endl << endl;
2271 }
2272
2273 indent_down();
2274 f_skeleton << "};" << endl << endl;
2275
2276 f_skeleton << "class " << svcname << "AsyncHandler : "
2277 << "public " << svcname << "CobSvIf {" << endl << " public:" << endl;
2278 indent_up();
2279 f_skeleton << indent() << svcname << "AsyncHandler() {" << endl << indent()
2280 << " syncHandler_ = std::unique_ptr<" << svcname << "Handler>(new " << svcname
2281 << "Handler);" << endl << indent() << " // Your initialization goes here" << endl
2282 << indent() << "}" << endl;
2283 f_skeleton << indent() << "virtual ~" << service_name_ << "AsyncHandler();" << endl;
2284
2285 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
2286 f_skeleton << endl << indent() << function_signature(*f_iter, "CobSv", "", true) << " {"
2287 << endl;
2288 indent_up();
2289
2290 t_type* returntype = (*f_iter)->get_returntype();
2291 t_field returnfield(returntype, "_return");
2292
2293 string target = returntype->is_void() ? "" : "_return";
2294 if (!returntype->is_void()) {
2295 f_skeleton << indent() << declare_field(&returnfield, true) << endl;
2296 }
2297 generate_function_call(f_skeleton, *f_iter, target, "syncHandler_", "");
2298 f_skeleton << indent() << "return cob(" << target << ");" << endl;
2299
2300 scope_down(f_skeleton);
2301 }
2302 f_skeleton << endl << " protected:" << endl << indent() << "std::unique_ptr<" << svcname
2303 << "Handler> syncHandler_;" << endl;
2304 indent_down();
2305 f_skeleton << "};" << endl << endl;
2306
2307 f_skeleton << indent() << "int main(int argc, char **argv) {" << endl;
2308 indent_up();
2309 f_skeleton
2310 << indent() << "int port = 9090;" << endl << indent() << "::std::shared_ptr<" << svcname
2311 << "AsyncHandler> handler(new " << svcname << "AsyncHandler());" << endl << indent()
2312 << "::std::shared_ptr<" << svcname << "AsyncProcessor> processor(new " << svcname << "AsyncProcessor(handler));" << endl
2313 << indent() << "::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());"
2314 << endl
2315 << indent() << "::std::shared_ptr<TAsyncProtocolProcessor> protocolProcessor(new TAsyncProtocolProcessor(processor, protocolFactory));"
2316 << endl << endl << indent()
2317 << "TEvhttpServer server(protocolProcessor, port);"
2318 << endl << indent() << "server.serve();" << endl << indent() << "return 0;" << endl;
2319 indent_down();
2320 f_skeleton << "}" << endl << endl;
2321 }
2322
2323 /**
2324 * Generates a multiface, which is a single server that just takes a set
2325 * of objects implementing the interface and calls them all, returning the
2326 * value of the last one to be called.
2327 *
2328 * @param tservice The service to generate a multiserver for.
2329 */
generate_service_multiface(t_service * tservice)2330 void t_cpp_generator::generate_service_multiface(t_service* tservice) {
2331 // Generate the dispatch methods
2332 vector<t_function*> functions = tservice->get_functions();
2333 vector<t_function*>::iterator f_iter;
2334
2335 string extends = "";
2336 string extends_multiface = "";
2337 if (tservice->get_extends() != nullptr) {
2338 extends = type_name(tservice->get_extends());
2339 extends_multiface = ", public " + extends + "Multiface";
2340 }
2341
2342 string list_type = string("std::vector<std::shared_ptr<") + service_name_ + "If> >";
2343
2344 // Generate the header portion
2345 f_header_ << "class " << service_name_ << "Multiface : "
2346 << "virtual public " << service_name_ << "If" << extends_multiface << " {" << endl
2347 << " public:" << endl;
2348 indent_up();
2349 f_header_ << indent() << service_name_ << "Multiface(" << list_type
2350 << "& ifaces) : ifaces_(ifaces) {" << endl;
2351 if (!extends.empty()) {
2352 f_header_ << indent()
2353 << " std::vector<std::shared_ptr<" + service_name_ + "If> >::iterator iter;"
2354 << endl << indent() << " for (iter = ifaces.begin(); iter != ifaces.end(); ++iter) {"
2355 << endl << indent() << " " << extends << "Multiface::add(*iter);" << endl
2356 << indent() << " }" << endl;
2357 }
2358 f_header_ << indent() << "}" << endl << indent() << "virtual ~" << service_name_
2359 << "Multiface() {}" << endl;
2360 indent_down();
2361
2362 // Protected data members
2363 f_header_ << " protected:" << endl;
2364 indent_up();
2365 f_header_ << indent() << list_type << " ifaces_;" << endl << indent() << service_name_
2366 << "Multiface() {}" << endl << indent() << "void add(::std::shared_ptr<"
2367 << service_name_ << "If> iface) {" << endl;
2368 if (!extends.empty()) {
2369 f_header_ << indent() << " " << extends << "Multiface::add(iface);" << endl;
2370 }
2371 f_header_ << indent() << " ifaces_.push_back(iface);" << endl << indent() << "}" << endl;
2372 indent_down();
2373
2374 f_header_ << indent() << " public:" << endl;
2375 indent_up();
2376
2377 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
2378 generate_java_doc(f_header_, *f_iter);
2379 t_struct* arglist = (*f_iter)->get_arglist();
2380 const vector<t_field*>& args = arglist->get_members();
2381 vector<t_field*>::const_iterator a_iter;
2382
2383 string call = string("ifaces_[i]->") + (*f_iter)->get_name() + "(";
2384 bool first = true;
2385 if (is_complex_type((*f_iter)->get_returntype())) {
2386 call += "_return";
2387 first = false;
2388 }
2389 for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) {
2390 if (first) {
2391 first = false;
2392 } else {
2393 call += ", ";
2394 }
2395 call += (*a_iter)->get_name();
2396 }
2397 call += ")";
2398
2399 f_header_ << indent() << function_signature(*f_iter, "") << " override {" << endl;
2400 indent_up();
2401 f_header_ << indent() << "size_t sz = ifaces_.size();" << endl << indent() << "size_t i = 0;"
2402 << endl << indent() << "for (; i < (sz - 1); ++i) {" << endl;
2403 indent_up();
2404 f_header_ << indent() << call << ";" << endl;
2405 indent_down();
2406 f_header_ << indent() << "}" << endl;
2407
2408 if (!(*f_iter)->get_returntype()->is_void()) {
2409 if (is_complex_type((*f_iter)->get_returntype())) {
2410 f_header_ << indent() << call << ";" << endl << indent() << "return;" << endl;
2411 } else {
2412 f_header_ << indent() << "return " << call << ";" << endl;
2413 }
2414 } else {
2415 f_header_ << indent() << call << ";" << endl;
2416 }
2417
2418 indent_down();
2419 f_header_ << indent() << "}" << endl << endl;
2420 }
2421
2422 indent_down();
2423 f_header_ << indent() << "};" << endl << endl;
2424 }
2425
2426 /**
2427 * Generates a service client definition.
2428 *
2429 * @param tservice The service to generate a server for.
2430 */
generate_service_client(t_service * tservice,string style)2431 void t_cpp_generator::generate_service_client(t_service* tservice, string style) {
2432 string ifstyle;
2433 if (style == "Cob") {
2434 ifstyle = "CobCl";
2435 }
2436
2437 std::ostream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
2438 string template_header, template_suffix, short_suffix, protocol_type, _this;
2439 string const prot_factory_type = "::apache::thrift::protocol::TProtocolFactory";
2440 if (gen_templates_) {
2441 template_header = "template <class Protocol_>\n";
2442 short_suffix = "T";
2443 template_suffix = "T<Protocol_>";
2444 protocol_type = "Protocol_";
2445 _this = "this->";
2446 } else {
2447 protocol_type = "::apache::thrift::protocol::TProtocol";
2448 }
2449 string prot_ptr = "std::shared_ptr< " + protocol_type + ">";
2450 string client_suffix = "Client" + template_suffix;
2451 string if_suffix = "If";
2452 if (style == "Cob") {
2453 if_suffix += template_suffix;
2454 }
2455
2456 string extends = "";
2457 string extends_client = "";
2458 if (tservice->get_extends() != nullptr) {
2459 // TODO(simpkins): If gen_templates_ is enabled, we currently assume all
2460 // parent services were also generated with templates enabled.
2461 extends = type_name(tservice->get_extends());
2462 extends_client = ", public " + extends + style + client_suffix;
2463 }
2464
2465 // Generate the header portion
2466 if (style == "Concurrent") {
2467 f_header_ << "// The \'concurrent\' client is a thread safe client that correctly handles\n"
2468 "// out of order responses. It is slower than the regular client, so should\n"
2469 "// only be used when you need to share a connection among multiple threads\n";
2470 }
2471 f_header_ << template_header << "class " << service_name_ << style << "Client" << short_suffix
2472 << " : "
2473 << "virtual public " << service_name_ << ifstyle << if_suffix << extends_client << " {"
2474 << endl << " public:" << endl;
2475
2476 indent_up();
2477 if (style != "Cob") {
2478 f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr
2479 << " prot";
2480 if (style == "Concurrent") {
2481 f_header_ << ", std::shared_ptr< ::apache::thrift::async::TConcurrentClientSyncInfo> sync";
2482 }
2483 f_header_ << ") ";
2484
2485 if (extends.empty()) {
2486 if (style == "Concurrent") {
2487 f_header_ << ": sync_(sync)" << endl;
2488 }
2489 f_header_ << "{" << endl;
2490 f_header_ << indent() << " setProtocol" << short_suffix << "(prot);" << endl << indent()
2491 << "}" << endl;
2492 } else {
2493 f_header_ << ":" << endl;
2494 f_header_ << indent() << " " << extends << style << client_suffix << "(prot, prot";
2495 if (style == "Concurrent") {
2496 f_header_ << ", sync";
2497 }
2498 f_header_ << ") {}" << endl;
2499 }
2500
2501 f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr
2502 << " iprot, " << prot_ptr << " oprot";
2503 if (style == "Concurrent") {
2504 f_header_ << ", std::shared_ptr< ::apache::thrift::async::TConcurrentClientSyncInfo> sync";
2505 }
2506 f_header_ << ") ";
2507
2508 if (extends.empty()) {
2509 if (style == "Concurrent") {
2510 f_header_ << ": sync_(sync)" << endl;
2511 }
2512 f_header_ << "{" << endl;
2513 f_header_ << indent() << " setProtocol" << short_suffix << "(iprot,oprot);" << endl
2514 << indent() << "}" << endl;
2515 } else {
2516 f_header_ << ":" << indent() << " " << extends << style << client_suffix
2517 << "(iprot, oprot";
2518 if (style == "Concurrent") {
2519 f_header_ << ", sync";
2520 }
2521 f_header_ << ") {}" << endl;
2522 }
2523
2524 // create the setProtocol methods
2525 if (extends.empty()) {
2526 f_header_ << " private:" << endl;
2527 // 1: one parameter
2528 f_header_ << indent() << "void setProtocol" << short_suffix << "(" << prot_ptr << " prot) {"
2529 << endl;
2530 f_header_ << indent() << "setProtocol" << short_suffix << "(prot,prot);" << endl;
2531 f_header_ << indent() << "}" << endl;
2532 // 2: two parameter
2533 f_header_ << indent() << "void setProtocol" << short_suffix << "(" << prot_ptr << " iprot, "
2534 << prot_ptr << " oprot) {" << endl;
2535
2536 f_header_ << indent() << " piprot_=iprot;" << endl << indent() << " poprot_=oprot;" << endl
2537 << indent() << " iprot_ = iprot.get();" << endl << indent()
2538 << " oprot_ = oprot.get();" << endl;
2539
2540 f_header_ << indent() << "}" << endl;
2541 f_header_ << " public:" << endl;
2542 }
2543
2544 // Generate getters for the protocols.
2545 // Note that these are not currently templated for simplicity.
2546 // TODO(simpkins): should they be templated?
2547 f_header_ << indent()
2548 << "std::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {"
2549 << endl << indent() << " return " << _this << "piprot_;" << endl << indent() << "}"
2550 << endl;
2551
2552 f_header_ << indent()
2553 << "std::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {"
2554 << endl << indent() << " return " << _this << "poprot_;" << endl << indent() << "}"
2555 << endl;
2556
2557 } else /* if (style == "Cob") */ {
2558 f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "("
2559 << "std::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel, "
2560 << "::apache::thrift::protocol::TProtocolFactory* protocolFactory) :" << endl;
2561 if (extends.empty()) {
2562 f_header_ << indent() << " channel_(channel)," << endl << indent()
2563 << " itrans_(new ::apache::thrift::transport::TMemoryBuffer())," << endl
2564 << indent() << " otrans_(new ::apache::thrift::transport::TMemoryBuffer()),"
2565 << endl;
2566 if (gen_templates_) {
2567 // TProtocolFactory classes return generic TProtocol pointers.
2568 // We have to dynamic cast to the Protocol_ type we are expecting.
2569 f_header_ << indent() << " piprot_(::std::dynamic_pointer_cast<Protocol_>("
2570 << "protocolFactory->getProtocol(itrans_)))," << endl << indent()
2571 << " poprot_(::std::dynamic_pointer_cast<Protocol_>("
2572 << "protocolFactory->getProtocol(otrans_))) {" << endl;
2573 // Throw a TException if either dynamic cast failed.
2574 f_header_ << indent() << " if (!piprot_ || !poprot_) {" << endl << indent()
2575 << " throw ::apache::thrift::TException(\""
2576 << "TProtocolFactory returned unexpected protocol type in " << service_name_
2577 << style << "Client" << short_suffix << " constructor\");" << endl << indent()
2578 << " }" << endl;
2579 } else {
2580 f_header_ << indent() << " piprot_(protocolFactory->getProtocol(itrans_))," << endl
2581 << indent() << " poprot_(protocolFactory->getProtocol(otrans_)) {" << endl;
2582 }
2583 f_header_ << indent() << " iprot_ = piprot_.get();" << endl << indent()
2584 << " oprot_ = poprot_.get();" << endl << indent() << "}" << endl;
2585 } else {
2586 f_header_ << indent() << " " << extends << style << client_suffix
2587 << "(channel, protocolFactory) {}" << endl;
2588 }
2589 }
2590
2591 if (style == "Cob") {
2592 generate_java_doc(f_header_, tservice);
2593
2594 f_header_ << indent()
2595 << "::std::shared_ptr< ::apache::thrift::async::TAsyncChannel> getChannel() {" << endl
2596 << indent() << " return " << _this << "channel_;" << endl << indent() << "}" << endl;
2597 if (!gen_no_client_completion_) {
2598 f_header_ << indent() << "virtual void completed__(bool /* success */) {}" << endl;
2599 }
2600 }
2601
2602 vector<t_function*> functions = tservice->get_functions();
2603 vector<t_function*>::const_iterator f_iter;
2604 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
2605 generate_java_doc(f_header_, *f_iter);
2606 indent(f_header_) << function_signature(*f_iter, ifstyle)
2607 << " override;" << endl;
2608 // TODO(dreiss): Use private inheritance to avoid generating thise in cob-style.
2609 if (style == "Concurrent" && !(*f_iter)->is_oneway()) {
2610 // concurrent clients need to move the seqid from the send function to the
2611 // recv function. Oneway methods don't have a recv function, so we don't need to
2612 // move the seqid for them. Attempting to do so would result in a seqid leak.
2613 t_function send_function(g_type_i32, /*returning seqid*/
2614 string("send_") + (*f_iter)->get_name(),
2615 (*f_iter)->get_arglist());
2616 indent(f_header_) << function_signature(&send_function, "") << ";" << endl;
2617 } else {
2618 t_function send_function(g_type_void,
2619 string("send_") + (*f_iter)->get_name(),
2620 (*f_iter)->get_arglist());
2621 indent(f_header_) << function_signature(&send_function, "") << ";" << endl;
2622 }
2623 if (!(*f_iter)->is_oneway()) {
2624 if (style == "Concurrent") {
2625 t_field seqIdArg(g_type_i32, "seqid");
2626 t_struct seqIdArgStruct(program_);
2627 seqIdArgStruct.append(&seqIdArg);
2628 t_function recv_function((*f_iter)->get_returntype(),
2629 string("recv_") + (*f_iter)->get_name(),
2630 &seqIdArgStruct);
2631 indent(f_header_) << function_signature(&recv_function, "") << ";" << endl;
2632 } else {
2633 t_struct noargs(program_);
2634 t_function recv_function((*f_iter)->get_returntype(),
2635 string("recv_") + (*f_iter)->get_name(),
2636 &noargs);
2637 indent(f_header_) << function_signature(&recv_function, "") << ";" << endl;
2638 }
2639 }
2640 }
2641 indent_down();
2642
2643 if (extends.empty()) {
2644 f_header_ << " protected:" << endl;
2645 indent_up();
2646
2647 if (style == "Cob") {
2648 f_header_ << indent()
2649 << "::std::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel_;" << endl
2650 << indent()
2651 << "::std::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> itrans_;" << endl
2652 << indent()
2653 << "::std::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> otrans_;"
2654 << endl;
2655 }
2656 f_header_ <<
2657 indent() << prot_ptr << " piprot_;" << endl <<
2658 indent() << prot_ptr << " poprot_;" << endl <<
2659 indent() << protocol_type << "* iprot_;" << endl <<
2660 indent() << protocol_type << "* oprot_;" << endl;
2661
2662 if (style == "Concurrent") {
2663 f_header_ <<
2664 indent() << "std::shared_ptr< ::apache::thrift::async::TConcurrentClientSyncInfo> sync_;"<<endl;
2665 }
2666 indent_down();
2667 }
2668
2669 f_header_ << "};" << endl << endl;
2670
2671 if (gen_templates_) {
2672 // Output a backwards compatibility typedef using
2673 // TProtocol as the template parameter.
2674 f_header_ << "typedef " << service_name_ << style
2675 << "ClientT< ::apache::thrift::protocol::TProtocol> " << service_name_ << style
2676 << "Client;" << endl << endl;
2677 }
2678
2679 string scope = service_name_ + style + client_suffix + "::";
2680
2681 // Generate client method implementations
2682 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
2683 string seqIdCapture;
2684 string seqIdUse;
2685 string seqIdCommaUse;
2686 if (style == "Concurrent" && !(*f_iter)->is_oneway()) {
2687 seqIdCapture = "int32_t seqid = ";
2688 seqIdUse = "seqid";
2689 seqIdCommaUse = ", seqid";
2690 }
2691
2692 string funname = (*f_iter)->get_name();
2693
2694 // Open function
2695 if (gen_templates_) {
2696 indent(out) << template_header;
2697 }
2698 indent(out) << function_signature(*f_iter, ifstyle, scope) << endl;
2699 scope_up(out);
2700 indent(out) << seqIdCapture << "send_" << funname << "(";
2701
2702 // Get the struct of function call params
2703 t_struct* arg_struct = (*f_iter)->get_arglist();
2704
2705 // Declare the function arguments
2706 const vector<t_field*>& fields = arg_struct->get_members();
2707 vector<t_field*>::const_iterator fld_iter;
2708 bool first = true;
2709 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
2710 if (first) {
2711 first = false;
2712 } else {
2713 out << ", ";
2714 }
2715 out << (*fld_iter)->get_name();
2716 }
2717 out << ");" << endl;
2718
2719 if (style != "Cob") {
2720 if (!(*f_iter)->is_oneway()) {
2721 out << indent();
2722 if (!(*f_iter)->get_returntype()->is_void()) {
2723 if (is_complex_type((*f_iter)->get_returntype())) {
2724 out << "recv_" << funname << "(_return" << seqIdCommaUse << ");" << endl;
2725 } else {
2726 out << "return recv_" << funname << "(" << seqIdUse << ");" << endl;
2727 }
2728 } else {
2729 out << "recv_" << funname << "(" << seqIdUse << ");" << endl;
2730 }
2731 }
2732 } else {
2733 if (!(*f_iter)->is_oneway()) {
2734 out << indent() << _this << "channel_->sendAndRecvMessage("
2735 << "::std::bind(cob, this), " << _this << "otrans_.get(), " << _this << "itrans_.get());"
2736 << endl;
2737 } else {
2738 out << indent() << _this << "channel_->sendMessage("
2739 << "::std::bind(cob, this), " << _this << "otrans_.get());" << endl;
2740 }
2741 }
2742 scope_down(out);
2743 out << endl;
2744
2745 // if (style != "Cob") // TODO(dreiss): Libify the client and don't generate this for cob-style
2746 if (true) {
2747 t_type* send_func_return_type = g_type_void;
2748 if (style == "Concurrent" && !(*f_iter)->is_oneway()) {
2749 send_func_return_type = g_type_i32;
2750 }
2751 // Function for sending
2752 t_function send_function(send_func_return_type,
2753 string("send_") + (*f_iter)->get_name(),
2754 (*f_iter)->get_arglist());
2755
2756 // Open the send function
2757 if (gen_templates_) {
2758 indent(out) << template_header;
2759 }
2760 indent(out) << function_signature(&send_function, "", scope) << endl;
2761 scope_up(out);
2762
2763 // Function arguments and results
2764 string argsname = tservice->get_name() + "_" + (*f_iter)->get_name() + "_pargs";
2765 string resultname = tservice->get_name() + "_" + (*f_iter)->get_name() + "_presult";
2766
2767 string cseqidVal = "0";
2768 if (style == "Concurrent") {
2769 if (!(*f_iter)->is_oneway()) {
2770 cseqidVal = "this->sync_->generateSeqId()";
2771 }
2772 }
2773 // Serialize the request
2774 out <<
2775 indent() << "int32_t cseqid = " << cseqidVal << ";" << endl;
2776 if(style == "Concurrent") {
2777 out <<
2778 indent() << "::apache::thrift::async::TConcurrentSendSentry sentry(this->sync_.get());" << endl;
2779 }
2780 if (style == "Cob") {
2781 out <<
2782 indent() << _this << "otrans_->resetBuffer();" << endl;
2783 }
2784 out <<
2785 indent() << _this << "oprot_->writeMessageBegin(\"" <<
2786 (*f_iter)->get_name() <<
2787 "\", ::apache::thrift::protocol::" << ((*f_iter)->is_oneway() ? "T_ONEWAY" : "T_CALL") <<
2788 ", cseqid);" << endl << endl <<
2789 indent() << argsname << " args;" << endl;
2790
2791 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
2792 out << indent() << "args." << (*fld_iter)->get_name() << " = &" << (*fld_iter)->get_name()
2793 << ";" << endl;
2794 }
2795
2796 out << indent() << "args.write(" << _this << "oprot_);" << endl << endl << indent() << _this
2797 << "oprot_->writeMessageEnd();" << endl << indent() << _this
2798 << "oprot_->getTransport()->writeEnd();" << endl << indent() << _this
2799 << "oprot_->getTransport()->flush();" << endl;
2800
2801 if (style == "Concurrent") {
2802 out << endl << indent() << "sentry.commit();" << endl;
2803
2804 if (!(*f_iter)->is_oneway()) {
2805 out << indent() << "return cseqid;" << endl;
2806 }
2807 }
2808 scope_down(out);
2809 out << endl;
2810
2811 // Generate recv function only if not an oneway function
2812 if (!(*f_iter)->is_oneway()) {
2813 t_struct noargs(program_);
2814
2815 t_field seqIdArg(g_type_i32, "seqid");
2816 t_struct seqIdArgStruct(program_);
2817 seqIdArgStruct.append(&seqIdArg);
2818
2819 t_struct* recv_function_args = &noargs;
2820 if (style == "Concurrent") {
2821 recv_function_args = &seqIdArgStruct;
2822 }
2823
2824 t_function recv_function((*f_iter)->get_returntype(),
2825 string("recv_") + (*f_iter)->get_name(),
2826 recv_function_args);
2827 // Open the recv function
2828 if (gen_templates_) {
2829 indent(out) << template_header;
2830 }
2831 indent(out) << function_signature(&recv_function, "", scope) << endl;
2832 scope_up(out);
2833
2834 out << endl <<
2835 indent() << "int32_t rseqid = 0;" << endl <<
2836 indent() << "std::string fname;" << endl <<
2837 indent() << "::apache::thrift::protocol::TMessageType mtype;" << endl;
2838 if(style == "Concurrent") {
2839 out <<
2840 endl <<
2841 indent() << "// the read mutex gets dropped and reacquired as part of waitForWork()" << endl <<
2842 indent() << "// The destructor of this sentry wakes up other clients" << endl <<
2843 indent() << "::apache::thrift::async::TConcurrentRecvSentry sentry(this->sync_.get(), seqid);" << endl;
2844 }
2845 if (style == "Cob" && !gen_no_client_completion_) {
2846 out << indent() << "bool completed = false;" << endl << endl << indent() << "try {";
2847 indent_up();
2848 }
2849 out << endl;
2850 if (style == "Concurrent") {
2851 out <<
2852 indent() << "while(true) {" << endl <<
2853 indent() << " if(!this->sync_->getPending(fname, mtype, rseqid)) {" << endl;
2854 indent_up();
2855 indent_up();
2856 }
2857 out <<
2858 indent() << _this << "iprot_->readMessageBegin(fname, mtype, rseqid);" << endl;
2859 if (style == "Concurrent") {
2860 scope_down(out);
2861 out << indent() << "if(seqid == rseqid) {" << endl;
2862 indent_up();
2863 }
2864 out <<
2865 indent() << "if (mtype == ::apache::thrift::protocol::T_EXCEPTION) {" << endl <<
2866 indent() << " ::apache::thrift::TApplicationException x;" << endl <<
2867 indent() << " x.read(" << _this << "iprot_);" << endl <<
2868 indent() << " " << _this << "iprot_->readMessageEnd();" << endl <<
2869 indent() << " " << _this << "iprot_->getTransport()->readEnd();" << endl;
2870 if (style == "Cob" && !gen_no_client_completion_) {
2871 out << indent() << " completed = true;" << endl << indent() << " completed__(true);"
2872 << endl;
2873 }
2874 if (style == "Concurrent") {
2875 out << indent() << " sentry.commit();" << endl;
2876 }
2877 out <<
2878 indent() << " throw x;" << endl <<
2879 indent() << "}" << endl <<
2880 indent() << "if (mtype != ::apache::thrift::protocol::T_REPLY) {" << endl <<
2881 indent() << " " << _this << "iprot_->skip(" << "::apache::thrift::protocol::T_STRUCT);" << endl <<
2882 indent() << " " << _this << "iprot_->readMessageEnd();" << endl <<
2883 indent() << " " << _this << "iprot_->getTransport()->readEnd();" << endl;
2884 if (style == "Cob" && !gen_no_client_completion_) {
2885 out << indent() << " completed = true;" << endl << indent() << " completed__(false);"
2886 << endl;
2887 }
2888 out <<
2889 indent() << "}" << endl <<
2890 indent() << "if (fname.compare(\"" << (*f_iter)->get_name() << "\") != 0) {" << endl <<
2891 indent() << " " << _this << "iprot_->skip(" << "::apache::thrift::protocol::T_STRUCT);" << endl <<
2892 indent() << " " << _this << "iprot_->readMessageEnd();" << endl <<
2893 indent() << " " << _this << "iprot_->getTransport()->readEnd();" << endl;
2894 if (style == "Cob" && !gen_no_client_completion_) {
2895 out << indent() << " completed = true;" << endl << indent() << " completed__(false);"
2896 << endl;
2897 }
2898 if (style == "Concurrent") {
2899 out << endl <<
2900 indent() << " // in a bad state, don't commit" << endl <<
2901 indent() << " using ::apache::thrift::protocol::TProtocolException;" << endl <<
2902 indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl;
2903 }
2904 out << indent() << "}" << endl;
2905
2906 if (!(*f_iter)->get_returntype()->is_void()
2907 && !is_complex_type((*f_iter)->get_returntype())) {
2908 t_field returnfield((*f_iter)->get_returntype(), "_return");
2909 out << indent() << declare_field(&returnfield) << endl;
2910 }
2911
2912 out << indent() << resultname << " result;" << endl;
2913
2914 if (!(*f_iter)->get_returntype()->is_void()) {
2915 out << indent() << "result.success = &_return;" << endl;
2916 }
2917
2918 out << indent() << "result.read(" << _this << "iprot_);" << endl << indent() << _this
2919 << "iprot_->readMessageEnd();" << endl << indent() << _this
2920 << "iprot_->getTransport()->readEnd();" << endl << endl;
2921
2922 // Careful, only look for _result if not a void function
2923 if (!(*f_iter)->get_returntype()->is_void()) {
2924 if (is_complex_type((*f_iter)->get_returntype())) {
2925 out <<
2926 indent() << "if (result.__isset.success) {" << endl;
2927 out <<
2928 indent() << " // _return pointer has now been filled" << endl;
2929 if (style == "Cob" && !gen_no_client_completion_) {
2930 out << indent() << " completed = true;" << endl << indent() << " completed__(true);"
2931 << endl;
2932 }
2933 if (style == "Concurrent") {
2934 out << indent() << " sentry.commit();" << endl;
2935 }
2936 out <<
2937 indent() << " return;" << endl <<
2938 indent() << "}" << endl;
2939 } else {
2940 out << indent() << "if (result.__isset.success) {" << endl;
2941 if (style == "Cob" && !gen_no_client_completion_) {
2942 out << indent() << " completed = true;" << endl << indent() << " completed__(true);"
2943 << endl;
2944 }
2945 if (style == "Concurrent") {
2946 out << indent() << " sentry.commit();" << endl;
2947 }
2948 out << indent() << " return _return;" << endl << indent() << "}" << endl;
2949 }
2950 }
2951
2952 t_struct* xs = (*f_iter)->get_xceptions();
2953 const std::vector<t_field*>& xceptions = xs->get_members();
2954 vector<t_field*>::const_iterator x_iter;
2955 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
2956 out << indent() << "if (result.__isset." << (*x_iter)->get_name() << ") {" << endl;
2957 if (style == "Cob" && !gen_no_client_completion_) {
2958 out << indent() << " completed = true;" << endl << indent() << " completed__(true);"
2959 << endl;
2960 }
2961 if (style == "Concurrent") {
2962 out << indent() << " sentry.commit();" << endl;
2963 }
2964 out << indent() << " throw result." << (*x_iter)->get_name() << ";" << endl << indent()
2965 << "}" << endl;
2966 }
2967
2968 // We only get here if we are a void function
2969 if ((*f_iter)->get_returntype()->is_void()) {
2970 if (style == "Cob" && !gen_no_client_completion_) {
2971 out << indent() << "completed = true;" << endl << indent() << "completed__(true);"
2972 << endl;
2973 }
2974 if (style == "Concurrent") {
2975 out << indent() << "sentry.commit();" << endl;
2976 }
2977 indent(out) << "return;" << endl;
2978 } else {
2979 if (style == "Cob" && !gen_no_client_completion_) {
2980 out << indent() << "completed = true;" << endl << indent() << "completed__(true);"
2981 << endl;
2982 }
2983 if (style == "Concurrent") {
2984 out << indent() << "// in a bad state, don't commit" << endl;
2985 }
2986 out << indent() << "throw "
2987 "::apache::thrift::TApplicationException(::apache::thrift::"
2988 "TApplicationException::MISSING_RESULT, \"" << (*f_iter)->get_name()
2989 << " failed: unknown result\");" << endl;
2990 }
2991 if (style == "Concurrent") {
2992 indent_down();
2993 indent_down();
2994 out <<
2995 indent() << " }" << endl <<
2996 indent() << " // seqid != rseqid" << endl <<
2997 indent() << " this->sync_->updatePending(fname, mtype, rseqid);" << endl <<
2998 endl <<
2999 indent() << " // this will temporarily unlock the readMutex, and let other clients get work done" << endl <<
3000 indent() << " this->sync_->waitForWork(seqid);" << endl <<
3001 indent() << "} // end while(true)" << endl;
3002 }
3003 if (style == "Cob" && !gen_no_client_completion_) {
3004 indent_down();
3005 out << indent() << "} catch (...) {" << endl << indent() << " if (!completed) {" << endl
3006 << indent() << " completed__(false);" << endl << indent() << " }" << endl
3007 << indent() << " throw;" << endl << indent() << "}" << endl;
3008 }
3009 // Close function
3010 scope_down(out);
3011 out << endl;
3012 }
3013 }
3014 }
3015 }
3016
3017 class ProcessorGenerator {
3018 public:
3019 ProcessorGenerator(t_cpp_generator* generator, t_service* service, const string& style);
3020
run()3021 void run() {
3022 generate_class_definition();
3023
3024 // Generate the dispatchCall() function
3025 generate_dispatch_call(false);
3026 if (generator_->gen_templates_) {
3027 generate_dispatch_call(true);
3028 }
3029
3030 // Generate all of the process subfunctions
3031 generate_process_functions();
3032
3033 generate_factory();
3034 }
3035
3036 void generate_class_definition();
3037 void generate_dispatch_call(bool template_protocol);
3038 void generate_process_functions();
3039 void generate_factory();
3040
3041 protected:
type_name(t_type * ttype,bool in_typedef=false,bool arg=false)3042 std::string type_name(t_type* ttype, bool in_typedef = false, bool arg = false) {
3043 return generator_->type_name(ttype, in_typedef, arg);
3044 }
3045
indent()3046 std::string indent() { return generator_->indent(); }
indent(std::ostream & os)3047 std::ostream& indent(std::ostream& os) { return generator_->indent(os); }
3048
indent_up()3049 void indent_up() { generator_->indent_up(); }
indent_down()3050 void indent_down() { generator_->indent_down(); }
3051
3052 t_cpp_generator* generator_;
3053 t_service* service_;
3054 std::ostream& f_header_;
3055 std::ostream& f_out_;
3056 string service_name_;
3057 string style_;
3058 string pstyle_;
3059 string class_name_;
3060 string if_name_;
3061 string factory_class_name_;
3062 string finish_cob_;
3063 string finish_cob_decl_;
3064 string ret_type_;
3065 string call_context_;
3066 string cob_arg_;
3067 string call_context_arg_;
3068 string call_context_decl_;
3069 string template_header_;
3070 string template_suffix_;
3071 string typename_str_;
3072 string class_suffix_;
3073 string extends_;
3074 };
3075
ProcessorGenerator(t_cpp_generator * generator,t_service * service,const string & style)3076 ProcessorGenerator::ProcessorGenerator(t_cpp_generator* generator,
3077 t_service* service,
3078 const string& style)
3079 : generator_(generator),
3080 service_(service),
3081 f_header_(generator->f_header_),
3082 f_out_(generator->gen_templates_ ? generator->f_service_tcc_ : generator->f_service_),
3083 service_name_(generator->service_name_),
3084 style_(style) {
3085 if (style_ == "Cob") {
3086 pstyle_ = "Async";
3087 class_name_ = service_name_ + pstyle_ + "Processor";
3088 if_name_ = service_name_ + "CobSvIf";
3089
3090 finish_cob_ = "::std::function<void(bool ok)> cob, ";
3091 finish_cob_decl_ = "::std::function<void(bool ok)>, ";
3092 cob_arg_ = "cob, ";
3093 ret_type_ = "void ";
3094 } else {
3095 class_name_ = service_name_ + "Processor";
3096 if_name_ = service_name_ + "If";
3097
3098 ret_type_ = "bool ";
3099 // TODO(edhall) callContext should eventually be added to TAsyncProcessor
3100 call_context_ = ", void* callContext";
3101 call_context_arg_ = ", callContext";
3102 call_context_decl_ = ", void*";
3103 }
3104
3105 factory_class_name_ = class_name_ + "Factory";
3106
3107 if (generator->gen_templates_) {
3108 template_header_ = "template <class Protocol_>\n";
3109 template_suffix_ = "<Protocol_>";
3110 typename_str_ = "typename ";
3111 class_name_ += "T";
3112 factory_class_name_ += "T";
3113 }
3114
3115 if (service_->get_extends() != nullptr) {
3116 extends_ = type_name(service_->get_extends()) + pstyle_ + "Processor";
3117 if (generator_->gen_templates_) {
3118 // TODO(simpkins): If gen_templates_ is enabled, we currently assume all
3119 // parent services were also generated with templates enabled.
3120 extends_ += "T<Protocol_>";
3121 }
3122 }
3123 }
3124
generate_class_definition()3125 void ProcessorGenerator::generate_class_definition() {
3126 // Generate the dispatch methods
3127 vector<t_function*> functions = service_->get_functions();
3128 vector<t_function*>::iterator f_iter;
3129
3130 string parent_class;
3131 if (service_->get_extends() != nullptr) {
3132 parent_class = extends_;
3133 } else {
3134 if (style_ == "Cob") {
3135 parent_class = "::apache::thrift::async::TAsyncDispatchProcessor";
3136 } else {
3137 parent_class = "::apache::thrift::TDispatchProcessor";
3138 }
3139
3140 if (generator_->gen_templates_) {
3141 parent_class += "T<Protocol_>";
3142 }
3143 }
3144
3145 // Generate the header portion
3146 f_header_ << template_header_ << "class " << class_name_ << " : public " << parent_class << " {"
3147 << endl;
3148
3149 // Protected data members
3150 f_header_ << " protected:" << endl;
3151 indent_up();
3152 f_header_ << indent() << "::std::shared_ptr<" << if_name_ << "> iface_;" << endl;
3153 f_header_ << indent() << "virtual " << ret_type_ << "dispatchCall(" << finish_cob_
3154 << "::apache::thrift::protocol::TProtocol* iprot, "
3155 << "::apache::thrift::protocol::TProtocol* oprot, "
3156 << "const std::string& fname, int32_t seqid" << call_context_
3157 << ") override;" << endl;
3158 if (generator_->gen_templates_) {
3159 f_header_ << indent() << "virtual " << ret_type_ << "dispatchCallTemplated(" << finish_cob_
3160 << "Protocol_* iprot, Protocol_* oprot, "
3161 << "const std::string& fname, int32_t seqid" << call_context_ << ");" << endl;
3162 }
3163 indent_down();
3164
3165 // Process function declarations
3166 f_header_ << " private:" << endl;
3167 indent_up();
3168
3169 // Declare processMap_
3170 f_header_ << indent() << "typedef void (" << class_name_ << "::*"
3171 << "ProcessFunction)(" << finish_cob_decl_ << "int32_t, "
3172 << "::apache::thrift::protocol::TProtocol*, "
3173 << "::apache::thrift::protocol::TProtocol*" << call_context_decl_ << ");" << endl;
3174 if (generator_->gen_templates_) {
3175 f_header_ << indent() << "typedef void (" << class_name_ << "::*"
3176 << "SpecializedProcessFunction)(" << finish_cob_decl_ << "int32_t, "
3177 << "Protocol_*, Protocol_*" << call_context_decl_ << ");" << endl << indent()
3178 << "struct ProcessFunctions {" << endl << indent() << " ProcessFunction generic;"
3179 << endl << indent() << " SpecializedProcessFunction specialized;" << endl << indent()
3180 << " ProcessFunctions(ProcessFunction g, "
3181 << "SpecializedProcessFunction s) :" << endl << indent() << " generic(g)," << endl
3182 << indent() << " specialized(s) {}" << endl << indent()
3183 << " ProcessFunctions() : generic(nullptr), specialized(nullptr) "
3184 << "{}" << endl << indent() << "};" << endl << indent()
3185 << "typedef std::map<std::string, ProcessFunctions> "
3186 << "ProcessMap;" << endl;
3187 } else {
3188 f_header_ << indent() << "typedef std::map<std::string, ProcessFunction> "
3189 << "ProcessMap;" << endl;
3190 }
3191 f_header_ << indent() << "ProcessMap processMap_;" << endl;
3192
3193 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
3194 indent(f_header_) << "void process_" << (*f_iter)->get_name() << "(" << finish_cob_
3195 << "int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, "
3196 "::apache::thrift::protocol::TProtocol* oprot" << call_context_ << ");"
3197 << endl;
3198 if (generator_->gen_templates_) {
3199 indent(f_header_) << "void process_" << (*f_iter)->get_name() << "(" << finish_cob_
3200 << "int32_t seqid, Protocol_* iprot, Protocol_* oprot" << call_context_
3201 << ");" << endl;
3202 }
3203 if (style_ == "Cob") {
3204 // XXX Factor this out, even if it is a pain.
3205 string ret_arg = ((*f_iter)->get_returntype()->is_void()
3206 ? ""
3207 : ", const " + type_name((*f_iter)->get_returntype()) + "& _return");
3208 f_header_ << indent() << "void return_" << (*f_iter)->get_name()
3209 << "(::std::function<void(bool ok)> cob, int32_t seqid, "
3210 << "::apache::thrift::protocol::TProtocol* oprot, "
3211 << "void* ctx" << ret_arg << ");" << endl;
3212 if (generator_->gen_templates_) {
3213 f_header_ << indent() << "void return_" << (*f_iter)->get_name()
3214 << "(::std::function<void(bool ok)> cob, int32_t seqid, "
3215 << "Protocol_* oprot, void* ctx" << ret_arg << ");" << endl;
3216 }
3217 // XXX Don't declare throw if it doesn't exist
3218 f_header_ << indent() << "void throw_" << (*f_iter)->get_name()
3219 << "(::std::function<void(bool ok)> cob, int32_t seqid, "
3220 << "::apache::thrift::protocol::TProtocol* oprot, void* ctx, "
3221 << "::apache::thrift::TDelayedException* _throw);" << endl;
3222 if (generator_->gen_templates_) {
3223 f_header_ << indent() << "void throw_" << (*f_iter)->get_name()
3224 << "(::std::function<void(bool ok)> cob, int32_t seqid, "
3225 << "Protocol_* oprot, void* ctx, "
3226 << "::apache::thrift::TDelayedException* _throw);" << endl;
3227 }
3228 }
3229 }
3230
3231 f_header_ << " public:" << endl << indent() << class_name_ << "(::std::shared_ptr<" << if_name_
3232 << "> iface) :" << endl;
3233 if (!extends_.empty()) {
3234 f_header_ << indent() << " " << extends_ << "(iface)," << endl;
3235 }
3236 f_header_ << indent() << " iface_(iface) {" << endl;
3237 indent_up();
3238
3239 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
3240 f_header_ << indent() << "processMap_[\"" << (*f_iter)->get_name() << "\"] = ";
3241 if (generator_->gen_templates_) {
3242 f_header_ << "ProcessFunctions(" << endl;
3243 if (generator_->gen_templates_only_) {
3244 indent(f_header_) << " nullptr," << endl;
3245 } else {
3246 indent(f_header_) << " &" << class_name_ << "::process_" << (*f_iter)->get_name() << ","
3247 << endl;
3248 }
3249 indent(f_header_) << " &" << class_name_ << "::process_" << (*f_iter)->get_name() << ")";
3250 } else {
3251 f_header_ << "&" << class_name_ << "::process_" << (*f_iter)->get_name();
3252 }
3253 f_header_ << ";" << endl;
3254 }
3255
3256 indent_down();
3257 f_header_ << indent() << "}" << endl << endl << indent() << "virtual ~" << class_name_ << "() {}"
3258 << endl;
3259 indent_down();
3260 f_header_ << "};" << endl << endl;
3261
3262 if (generator_->gen_templates_) {
3263 // Generate a backwards compatible typedef, for callers who don't know
3264 // about the new template-style code.
3265 //
3266 // We can't use TProtocol as the template parameter, since ProcessorT
3267 // provides overloaded versions of most methods, one of which accepts
3268 // TProtocol pointers, and one which accepts Protocol_ pointers. This
3269 // results in a compile error if instantiated with Protocol_ == TProtocol.
3270 // Therefore, we define TDummyProtocol solely so we can use it as the
3271 // template parameter here.
3272 f_header_ << "typedef " << class_name_ << "< ::apache::thrift::protocol::TDummyProtocol > "
3273 << service_name_ << pstyle_ << "Processor;" << endl << endl;
3274 }
3275 }
3276
generate_dispatch_call(bool template_protocol)3277 void ProcessorGenerator::generate_dispatch_call(bool template_protocol) {
3278 string protocol = "::apache::thrift::protocol::TProtocol";
3279 string function_suffix;
3280 if (template_protocol) {
3281 protocol = "Protocol_";
3282 // We call the generic version dispatchCall(), and the specialized
3283 // version dispatchCallTemplated(). We can't call them both
3284 // dispatchCall(), since this will cause the compiler to issue a warning if
3285 // a service that doesn't use templates inherits from a service that does
3286 // use templates: the compiler complains that the subclass only implements
3287 // the generic version of dispatchCall(), and hides the templated version.
3288 // Using different names for the two functions prevents this.
3289 function_suffix = "Templated";
3290 }
3291
3292 f_out_ << template_header_ << ret_type_ << class_name_ << template_suffix_ << "::dispatchCall"
3293 << function_suffix << "(" << finish_cob_ << protocol << "* iprot, " << protocol
3294 << "* oprot, "
3295 << "const std::string& fname, int32_t seqid" << call_context_ << ") {" << endl;
3296 indent_up();
3297
3298 // HOT: member function pointer map
3299 f_out_ << indent() << typename_str_ << "ProcessMap::iterator pfn;" << endl << indent()
3300 << "pfn = processMap_.find(fname);" << endl << indent()
3301 << "if (pfn == processMap_.end()) {" << endl;
3302 if (extends_.empty()) {
3303 f_out_ << indent() << " iprot->skip(::apache::thrift::protocol::T_STRUCT);" << endl << indent()
3304 << " iprot->readMessageEnd();" << endl << indent()
3305 << " iprot->getTransport()->readEnd();" << endl << indent()
3306 << " ::apache::thrift::TApplicationException "
3307 "x(::apache::thrift::TApplicationException::UNKNOWN_METHOD, \"Invalid method name: "
3308 "'\"+fname+\"'\");" << endl << indent()
3309 << " oprot->writeMessageBegin(fname, ::apache::thrift::protocol::T_EXCEPTION, seqid);"
3310 << endl << indent() << " x.write(oprot);" << endl << indent()
3311 << " oprot->writeMessageEnd();" << endl << indent()
3312 << " oprot->getTransport()->writeEnd();" << endl << indent()
3313 << " oprot->getTransport()->flush();" << endl << indent()
3314 << (style_ == "Cob" ? " return cob(true);" : " return true;") << endl;
3315 } else {
3316 f_out_ << indent() << " return " << extends_ << "::dispatchCall("
3317 << (style_ == "Cob" ? "cob, " : "") << "iprot, oprot, fname, seqid" << call_context_arg_
3318 << ");" << endl;
3319 }
3320 f_out_ << indent() << "}" << endl;
3321 if (template_protocol) {
3322 f_out_ << indent() << "(this->*(pfn->second.specialized))";
3323 } else {
3324 if (generator_->gen_templates_only_) {
3325 // TODO: This is a null pointer, so nothing good will come from calling
3326 // it. Throw an exception instead.
3327 f_out_ << indent() << "(this->*(pfn->second.generic))";
3328 } else if (generator_->gen_templates_) {
3329 f_out_ << indent() << "(this->*(pfn->second.generic))";
3330 } else {
3331 f_out_ << indent() << "(this->*(pfn->second))";
3332 }
3333 }
3334 f_out_ << "(" << cob_arg_ << "seqid, iprot, oprot" << call_context_arg_ << ");" << endl;
3335
3336 // TODO(dreiss): return pfn ret?
3337 if (style_ == "Cob") {
3338 f_out_ << indent() << "return;" << endl;
3339 } else {
3340 f_out_ << indent() << "return true;" << endl;
3341 }
3342
3343 indent_down();
3344 f_out_ << "}" << endl << endl;
3345 }
3346
generate_process_functions()3347 void ProcessorGenerator::generate_process_functions() {
3348 vector<t_function*> functions = service_->get_functions();
3349 vector<t_function*>::iterator f_iter;
3350 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
3351 if (generator_->gen_templates_) {
3352 generator_->generate_process_function(service_, *f_iter, style_, false);
3353 generator_->generate_process_function(service_, *f_iter, style_, true);
3354 } else {
3355 generator_->generate_process_function(service_, *f_iter, style_, false);
3356 }
3357 }
3358 }
3359
generate_factory()3360 void ProcessorGenerator::generate_factory() {
3361 string if_factory_name = if_name_ + "Factory";
3362
3363 // Generate the factory class definition
3364 f_header_ << template_header_ << "class " << factory_class_name_ << " : public ::apache::thrift::"
3365 << (style_ == "Cob" ? "async::TAsyncProcessorFactory" : "TProcessorFactory") << " {"
3366 << endl << " public:" << endl;
3367 indent_up();
3368
3369 f_header_ << indent() << factory_class_name_ << "(const ::std::shared_ptr< " << if_factory_name
3370 << " >& handlerFactory) noexcept :" << endl << indent()
3371 << " handlerFactory_(handlerFactory) {}" << endl << endl << indent()
3372 << "::std::shared_ptr< ::apache::thrift::"
3373 << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > "
3374 << "getProcessor(const ::apache::thrift::TConnectionInfo& connInfo) override;"
3375 << endl;
3376
3377 f_header_ << endl << " protected:" << endl << indent() << "::std::shared_ptr< "
3378 << if_factory_name << " > handlerFactory_;" << endl;
3379
3380 indent_down();
3381 f_header_ << "};" << endl << endl;
3382
3383 // If we are generating templates, output a typedef for the plain
3384 // factory name.
3385 if (generator_->gen_templates_) {
3386 f_header_ << "typedef " << factory_class_name_
3387 << "< ::apache::thrift::protocol::TDummyProtocol > " << service_name_ << pstyle_
3388 << "ProcessorFactory;" << endl << endl;
3389 }
3390
3391 // Generate the getProcessor() method
3392 f_out_ << template_header_ << indent() << "::std::shared_ptr< ::apache::thrift::"
3393 << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > "
3394 << factory_class_name_ << template_suffix_ << "::getProcessor("
3395 << "const ::apache::thrift::TConnectionInfo& connInfo) {" << endl;
3396 indent_up();
3397
3398 f_out_ << indent() << "::apache::thrift::ReleaseHandler< " << if_factory_name
3399 << " > cleanup(handlerFactory_);" << endl << indent() << "::std::shared_ptr< "
3400 << if_name_ << " > handler("
3401 << "handlerFactory_->getHandler(connInfo), cleanup);" << endl << indent()
3402 << "::std::shared_ptr< ::apache::thrift::"
3403 << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > "
3404 << "processor(new " << class_name_ << template_suffix_ << "(handler));" << endl << indent()
3405 << "return processor;" << endl;
3406
3407 indent_down();
3408 f_out_ << indent() << "}" << endl << endl;
3409 }
3410
3411 /**
3412 * Generates a service processor definition.
3413 *
3414 * @param tservice The service to generate a processor for.
3415 */
generate_service_processor(t_service * tservice,string style)3416 void t_cpp_generator::generate_service_processor(t_service* tservice, string style) {
3417 ProcessorGenerator generator(this, tservice, style);
3418 generator.run();
3419 }
3420
3421 /**
3422 * Generates a struct and helpers for a function.
3423 *
3424 * @param tfunction The function
3425 */
generate_function_helpers(t_service * tservice,t_function * tfunction)3426 void t_cpp_generator::generate_function_helpers(t_service* tservice, t_function* tfunction) {
3427 if (tfunction->is_oneway()) {
3428 return;
3429 }
3430
3431 std::ostream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
3432
3433 t_struct result(program_, tservice->get_name() + "_" + tfunction->get_name() + "_result");
3434 t_field success(tfunction->get_returntype(), "success", 0);
3435 if (!tfunction->get_returntype()->is_void()) {
3436 result.append(&success);
3437 }
3438
3439 t_struct* xs = tfunction->get_xceptions();
3440 const vector<t_field*>& fields = xs->get_members();
3441 vector<t_field*>::const_iterator f_iter;
3442 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
3443 result.append(*f_iter);
3444 }
3445
3446 generate_struct_declaration(f_header_, &result, false);
3447 generate_struct_definition(out, f_service_, &result, false);
3448 generate_struct_reader(out, &result);
3449 generate_struct_result_writer(out, &result);
3450
3451 result.set_name(tservice->get_name() + "_" + tfunction->get_name() + "_presult");
3452 generate_struct_declaration(f_header_, &result, false, true, true, gen_cob_style_);
3453 generate_struct_definition(out, f_service_, &result, false);
3454 generate_struct_reader(out, &result, true);
3455 if (gen_cob_style_) {
3456 generate_struct_writer(out, &result, true);
3457 }
3458 }
3459
3460 /**
3461 * Generates a process function definition.
3462 *
3463 * @param tfunction The function to write a dispatcher for
3464 */
generate_process_function(t_service * tservice,t_function * tfunction,string style,bool specialized)3465 void t_cpp_generator::generate_process_function(t_service* tservice,
3466 t_function* tfunction,
3467 string style,
3468 bool specialized) {
3469 t_struct* arg_struct = tfunction->get_arglist();
3470 const std::vector<t_field*>& fields = arg_struct->get_members();
3471 vector<t_field*>::const_iterator f_iter;
3472
3473 t_struct* xs = tfunction->get_xceptions();
3474 const std::vector<t_field*>& xceptions = xs->get_members();
3475 vector<t_field*>::const_iterator x_iter;
3476 string service_func_name = "\"" + tservice->get_name() + "." + tfunction->get_name() + "\"";
3477
3478 std::ostream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
3479
3480 string prot_type = (specialized ? "Protocol_" : "::apache::thrift::protocol::TProtocol");
3481 string class_suffix;
3482 if (gen_templates_) {
3483 class_suffix = "T<Protocol_>";
3484 }
3485
3486 // I tried to do this as one function. I really did. But it was too hard.
3487 if (style != "Cob") {
3488 // Open function
3489 if (gen_templates_) {
3490 out << indent() << "template <class Protocol_>" << endl;
3491 }
3492 const bool unnamed_oprot_seqid = tfunction->is_oneway() && !(gen_templates_ && !specialized);
3493 out << "void " << tservice->get_name() << "Processor" << class_suffix << "::"
3494 << "process_" << tfunction->get_name() << "("
3495 << "int32_t" << (unnamed_oprot_seqid ? ", " : " seqid, ") << prot_type << "* iprot, "
3496 << prot_type << "*" << (unnamed_oprot_seqid ? ", " : " oprot, ") << "void* callContext)"
3497 << endl;
3498 scope_up(out);
3499
3500 string argsname = tservice->get_name() + "_" + tfunction->get_name() + "_args";
3501 string resultname = tservice->get_name() + "_" + tfunction->get_name() + "_result";
3502
3503 if (tfunction->is_oneway() && !unnamed_oprot_seqid) {
3504 out << indent() << "(void) seqid;" << endl << indent() << "(void) oprot;" << endl;
3505 }
3506
3507 out << indent() << "void* ctx = nullptr;" << endl << indent()
3508 << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3509 << " ctx = this->eventHandler_->getContext(" << service_func_name << ", callContext);"
3510 << endl << indent() << "}" << endl << indent()
3511 << "::apache::thrift::TProcessorContextFreer freer("
3512 << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl
3513 << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3514 << " this->eventHandler_->preRead(ctx, " << service_func_name << ");" << endl << indent()
3515 << "}" << endl << endl << indent() << argsname << " args;" << endl << indent()
3516 << "args.read(iprot);" << endl << indent() << "iprot->readMessageEnd();" << endl << indent()
3517 << "uint32_t bytes = iprot->getTransport()->readEnd();" << endl << endl << indent()
3518 << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3519 << " this->eventHandler_->postRead(ctx, " << service_func_name << ", bytes);" << endl
3520 << indent() << "}" << endl << endl;
3521
3522 // Declare result
3523 if (!tfunction->is_oneway()) {
3524 out << indent() << resultname << " result;" << endl;
3525 }
3526
3527 // Try block for functions with exceptions
3528 out << indent() << "try {" << endl;
3529 indent_up();
3530
3531 // Generate the function call
3532 bool first = true;
3533 out << indent();
3534 if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
3535 if (is_complex_type(tfunction->get_returntype())) {
3536 first = false;
3537 out << "iface_->" << tfunction->get_name() << "(result.success";
3538 } else {
3539 out << "result.success = iface_->" << tfunction->get_name() << "(";
3540 }
3541 } else {
3542 out << "iface_->" << tfunction->get_name() << "(";
3543 }
3544 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
3545 if (first) {
3546 first = false;
3547 } else {
3548 out << ", ";
3549 }
3550 out << "args." << (*f_iter)->get_name();
3551 }
3552 out << ");" << endl;
3553
3554 // Set isset on success field
3555 if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
3556 out << indent() << "result.__isset.success = true;" << endl;
3557 }
3558
3559 indent_down();
3560 out << indent() << "}";
3561
3562 if (!tfunction->is_oneway()) {
3563 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
3564 out << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name()
3565 << ") {" << endl;
3566 if (!tfunction->is_oneway()) {
3567 indent_up();
3568 out << indent() << "result." << (*x_iter)->get_name()
3569 << " = std::move(" << (*x_iter)->get_name() << ");" << endl
3570 << indent() << "result.__isset." << (*x_iter)->get_name() << " = true;" << endl;
3571 indent_down();
3572 out << indent() << "}";
3573 } else {
3574 out << "}";
3575 }
3576 }
3577 }
3578
3579 if (!tfunction->is_oneway()) {
3580 out << " catch (const std::exception& e) {" << endl;
3581 } else {
3582 out << " catch (const std::exception&) {" << endl;
3583 }
3584
3585 indent_up();
3586 out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3587 << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl
3588 << indent() << "}" << endl;
3589
3590 if (!tfunction->is_oneway()) {
3591 out << endl << indent() << "::apache::thrift::TApplicationException x(e.what());" << endl
3592 << indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name()
3593 << "\", ::apache::thrift::protocol::T_EXCEPTION, seqid);" << endl << indent()
3594 << "x.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl
3595 << indent() << "oprot->getTransport()->writeEnd();" << endl << indent()
3596 << "oprot->getTransport()->flush();" << endl;
3597 }
3598 out << indent() << "return;" << endl;
3599 indent_down();
3600 out << indent() << "}" << endl << endl;
3601
3602 // Shortcut out here for oneway functions
3603 if (tfunction->is_oneway()) {
3604 out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3605 << " this->eventHandler_->asyncComplete(ctx, " << service_func_name << ");" << endl
3606 << indent() << "}" << endl << endl << indent() << "return;" << endl;
3607 indent_down();
3608 out << "}" << endl << endl;
3609 return;
3610 }
3611
3612 // Serialize the result into a struct
3613 out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3614 << " this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << endl << indent()
3615 << "}" << endl << endl << indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name()
3616 << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << endl << indent()
3617 << "result.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl
3618 << indent() << "bytes = oprot->getTransport()->writeEnd();" << endl << indent()
3619 << "oprot->getTransport()->flush();" << endl << endl << indent()
3620 << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3621 << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl
3622 << indent() << "}" << endl;
3623
3624 // Close function
3625 scope_down(out);
3626 out << endl;
3627 }
3628
3629 // Cob style.
3630 else {
3631 // Processor entry point.
3632 // TODO(edhall) update for callContext when TEventServer is ready
3633 if (gen_templates_) {
3634 out << indent() << "template <class Protocol_>" << endl;
3635 }
3636 out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::process_"
3637 << tfunction->get_name() << "(::std::function<void(bool ok)> cob, int32_t seqid, "
3638 << prot_type << "* iprot, " << prot_type << "* oprot)" << endl;
3639 scope_up(out);
3640
3641 // TODO(simpkins): we could try to consoldate this
3642 // with the non-cob code above
3643 if (gen_templates_ && !specialized) {
3644 // If these are instances of Protocol_, instead of any old TProtocol,
3645 // use the specialized process function instead.
3646 out << indent() << "Protocol_* _iprot = dynamic_cast<Protocol_*>(iprot);" << endl << indent()
3647 << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" << endl << indent()
3648 << "if (_iprot && _oprot) {" << endl << indent() << " return process_"
3649 << tfunction->get_name() << "(cob, seqid, _iprot, _oprot);" << endl << indent() << "}"
3650 << endl << indent() << "T_GENERIC_PROTOCOL(this, iprot, _iprot);" << endl << indent()
3651 << "T_GENERIC_PROTOCOL(this, oprot, _oprot);" << endl << endl;
3652 }
3653
3654 if (tfunction->is_oneway()) {
3655 out << indent() << "(void) seqid;" << endl << indent() << "(void) oprot;" << endl;
3656 }
3657
3658 out << indent() << tservice->get_name() + "_" + tfunction->get_name() << "_args args;" << endl
3659 << indent() << "void* ctx = nullptr;" << endl << indent()
3660 << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3661 << " ctx = this->eventHandler_->getContext(" << service_func_name << ", nullptr);" << endl
3662 << indent() << "}" << endl << indent() << "::apache::thrift::TProcessorContextFreer freer("
3663 << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl
3664 << indent() << "try {" << endl;
3665 indent_up();
3666 out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3667 << " this->eventHandler_->preRead(ctx, " << service_func_name << ");" << endl << indent()
3668 << "}" << endl << indent() << "args.read(iprot);" << endl << indent()
3669 << "iprot->readMessageEnd();" << endl << indent()
3670 << "uint32_t bytes = iprot->getTransport()->readEnd();" << endl << indent()
3671 << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3672 << " this->eventHandler_->postRead(ctx, " << service_func_name << ", bytes);" << endl
3673 << indent() << "}" << endl;
3674 scope_down(out);
3675
3676 // TODO(dreiss): Handle TExceptions? Expose to server?
3677 out << indent() << "catch (const std::exception&) {" << endl << indent()
3678 << " if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3679 << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl
3680 << indent() << " }" << endl << indent() << " return cob(false);" << endl << indent()
3681 << "}" << endl;
3682
3683 if (tfunction->is_oneway()) {
3684 out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3685 << " this->eventHandler_->asyncComplete(ctx, " << service_func_name << ");" << endl
3686 << indent() << "}" << endl;
3687 }
3688 // TODO(dreiss): Figure out a strategy for exceptions in async handlers.
3689 out << indent() << "freer.unregister();" << endl;
3690 if (tfunction->is_oneway()) {
3691 // No return. Just hand off our cob.
3692 // TODO(dreiss): Call the cob immediately?
3693 out << indent() << "iface_->" << tfunction->get_name() << "("
3694 << "::std::bind(cob, true)" << endl;
3695 indent_up();
3696 indent_up();
3697 } else {
3698 string ret_arg, ret_placeholder;
3699 if (!tfunction->get_returntype()->is_void()) {
3700 ret_arg = ", const " + type_name(tfunction->get_returntype()) + "& _return";
3701 ret_placeholder = ", ::std::placeholders::_1";
3702 }
3703
3704 // When gen_templates_ is true, the return_ and throw_ functions are
3705 // overloaded. We have to declare pointers to them so that the compiler
3706 // can resolve the correct overloaded version.
3707 out << indent() << "void (" << tservice->get_name() << "AsyncProcessor" << class_suffix
3708 << "::*return_fn)(::std::function<void(bool ok)> "
3709 << "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx" << ret_arg
3710 << ") =" << endl;
3711 out << indent() << " &" << tservice->get_name() << "AsyncProcessor" << class_suffix
3712 << "::return_" << tfunction->get_name() << ";" << endl;
3713 if (!xceptions.empty()) {
3714 out << indent() << "void (" << tservice->get_name() << "AsyncProcessor" << class_suffix
3715 << "::*throw_fn)(::std::function<void(bool ok)> "
3716 << "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx, "
3717 << "::apache::thrift::TDelayedException* _throw) =" << endl;
3718 out << indent() << " &" << tservice->get_name() << "AsyncProcessor" << class_suffix
3719 << "::throw_" << tfunction->get_name() << ";" << endl;
3720 }
3721
3722 out << indent() << "iface_->" << tfunction->get_name() << "(" << endl;
3723 indent_up();
3724 indent_up();
3725 out << indent() << "::std::bind(return_fn, this, cob, seqid, oprot, ctx" << ret_placeholder
3726 << ")";
3727 if (!xceptions.empty()) {
3728 out << ',' << endl << indent() << "::std::bind(throw_fn, this, cob, seqid, oprot, "
3729 << "ctx, ::std::placeholders::_1)";
3730 }
3731 }
3732
3733 // XXX Whitespace cleanup.
3734 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
3735 out << ',' << endl << indent() << "args." << (*f_iter)->get_name();
3736 }
3737 out << ");" << endl;
3738 indent_down();
3739 indent_down();
3740 scope_down(out);
3741 out << endl;
3742
3743 // Normal return.
3744 if (!tfunction->is_oneway()) {
3745 string ret_arg_decl, ret_arg_name;
3746 if (!tfunction->get_returntype()->is_void()) {
3747 ret_arg_decl = ", const " + type_name(tfunction->get_returntype()) + "& _return";
3748 ret_arg_name = ", _return";
3749 }
3750 if (gen_templates_) {
3751 out << indent() << "template <class Protocol_>" << endl;
3752 }
3753 out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::return_"
3754 << tfunction->get_name() << "(::std::function<void(bool ok)> cob, int32_t seqid, "
3755 << prot_type << "* oprot, void* ctx" << ret_arg_decl << ')' << endl;
3756 scope_up(out);
3757
3758 if (gen_templates_ && !specialized) {
3759 // If oprot is a Protocol_ instance,
3760 // use the specialized return function instead.
3761 out << indent() << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" << endl
3762 << indent() << "if (_oprot) {" << endl << indent() << " return return_"
3763 << tfunction->get_name() << "(cob, seqid, _oprot, ctx" << ret_arg_name << ");" << endl
3764 << indent() << "}" << endl << indent() << "T_GENERIC_PROTOCOL(this, oprot, _oprot);"
3765 << endl << endl;
3766 }
3767
3768 out << indent() << tservice->get_name() << "_" << tfunction->get_name() << "_presult result;"
3769 << endl;
3770 if (!tfunction->get_returntype()->is_void()) {
3771 // The const_cast here is unfortunate, but it would be a pain to avoid,
3772 // and we only do a write with this struct, which is const-safe.
3773 out << indent() << "result.success = const_cast<" << type_name(tfunction->get_returntype())
3774 << "*>(&_return);" << endl << indent() << "result.__isset.success = true;" << endl;
3775 }
3776 // Serialize the result into a struct
3777 out << endl << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3778 << " ctx = this->eventHandler_->getContext(" << service_func_name << ", nullptr);" << endl
3779 << indent() << "}" << endl << indent()
3780 << "::apache::thrift::TProcessorContextFreer freer("
3781 << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl
3782 << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3783 << " this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << endl
3784 << indent() << "}" << endl << endl << indent() << "oprot->writeMessageBegin(\""
3785 << tfunction->get_name() << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << endl
3786 << indent() << "result.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();"
3787 << endl << indent() << "uint32_t bytes = oprot->getTransport()->writeEnd();" << endl
3788 << indent() << "oprot->getTransport()->flush();" << endl << indent()
3789 << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3790 << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl
3791 << indent() << "}" << endl << indent() << "return cob(true);" << endl;
3792 scope_down(out);
3793 out << endl;
3794 }
3795
3796 // Exception return.
3797 if (!tfunction->is_oneway() && !xceptions.empty()) {
3798 if (gen_templates_) {
3799 out << indent() << "template <class Protocol_>" << endl;
3800 }
3801 out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::throw_"
3802 << tfunction->get_name() << "(::std::function<void(bool ok)> cob, int32_t seqid, "
3803 << prot_type << "* oprot, void* ctx, "
3804 << "::apache::thrift::TDelayedException* _throw)" << endl;
3805 scope_up(out);
3806
3807 if (gen_templates_ && !specialized) {
3808 // If oprot is a Protocol_ instance,
3809 // use the specialized throw function instead.
3810 out << indent() << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" << endl
3811 << indent() << "if (_oprot) {" << endl << indent() << " return throw_"
3812 << tfunction->get_name() << "(cob, seqid, _oprot, ctx, _throw);" << endl << indent()
3813 << "}" << endl << indent() << "T_GENERIC_PROTOCOL(this, oprot, _oprot);" << endl
3814 << endl;
3815 }
3816
3817 // Get the event handler context
3818 out << endl << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3819 << " ctx = this->eventHandler_->getContext(" << service_func_name << ", nullptr);" << endl
3820 << indent() << "}" << endl << indent()
3821 << "::apache::thrift::TProcessorContextFreer freer("
3822 << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl;
3823
3824 // Throw the TDelayedException, and catch the result
3825 out << indent() << tservice->get_name() << "_" << tfunction->get_name() << "_result result;"
3826 << endl << endl << indent() << "try {" << endl;
3827 indent_up();
3828 out << indent() << "_throw->throw_it();" << endl << indent() << "return cob(false);"
3829 << endl; // Is this possible? TBD.
3830 indent_down();
3831 out << indent() << '}';
3832 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
3833 out << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name()
3834 << ") {" << endl;
3835 indent_up();
3836 out << indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name()
3837 << ";" << endl << indent() << "result.__isset." << (*x_iter)->get_name() << " = true;"
3838 << endl;
3839 scope_down(out);
3840 }
3841
3842 // Handle the case where an undeclared exception is thrown
3843 out << " catch (std::exception& e) {" << endl;
3844 indent_up();
3845 out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3846 << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl
3847 << indent() << "}" << endl << endl << indent()
3848 << "::apache::thrift::TApplicationException x(e.what());" << endl << indent()
3849 << "oprot->writeMessageBegin(\"" << tfunction->get_name()
3850 << "\", ::apache::thrift::protocol::T_EXCEPTION, seqid);" << endl << indent()
3851 << "x.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl
3852 << indent() << "oprot->getTransport()->writeEnd();" << endl << indent()
3853 << "oprot->getTransport()->flush();" << endl <<
3854 // We pass true to the cob here, since we did successfully write a
3855 // response, even though it is an exception response.
3856 // It looks like the argument is currently ignored, anyway.
3857 indent() << "return cob(true);" << endl;
3858 scope_down(out);
3859
3860 // Serialize the result into a struct
3861 out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3862 << " this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << endl
3863 << indent() << "}" << endl << endl << indent() << "oprot->writeMessageBegin(\""
3864 << tfunction->get_name() << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << endl
3865 << indent() << "result.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();"
3866 << endl << indent() << "uint32_t bytes = oprot->getTransport()->writeEnd();" << endl
3867 << indent() << "oprot->getTransport()->flush();" << endl << indent()
3868 << "if (this->eventHandler_.get() != nullptr) {" << endl << indent()
3869 << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl
3870 << indent() << "}" << endl << indent() << "return cob(true);" << endl;
3871 scope_down(out);
3872 out << endl;
3873 } // for each function
3874 } // cob style
3875 }
3876
3877 /**
3878 * Generates a skeleton file of a server
3879 *
3880 * @param tservice The service to generate a server for.
3881 */
generate_service_skeleton(t_service * tservice)3882 void t_cpp_generator::generate_service_skeleton(t_service* tservice) {
3883 string svcname = tservice->get_name();
3884
3885 // Service implementation file includes
3886 string f_skeleton_name = get_out_dir() + svcname + "_server.skeleton.cpp";
3887
3888 string ns = namespace_prefix(tservice->get_program()->get_namespace("cpp"));
3889
3890 ofstream_with_content_based_conditional_update f_skeleton;
3891 f_skeleton.open(f_skeleton_name.c_str());
3892 f_skeleton << "// This autogenerated skeleton file illustrates how to build a server." << endl
3893 << "// You should copy it to another filename to avoid overwriting it." << endl << endl
3894 << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl
3895 << "#include <thrift/protocol/TBinaryProtocol.h>" << endl
3896 << "#include <thrift/server/TSimpleServer.h>" << endl
3897 << "#include <thrift/transport/TServerSocket.h>" << endl
3898 << "#include <thrift/transport/TBufferTransports.h>" << endl << endl
3899 << "using namespace ::apache::thrift;" << endl
3900 << "using namespace ::apache::thrift::protocol;" << endl
3901 << "using namespace ::apache::thrift::transport;" << endl
3902 << "using namespace ::apache::thrift::server;" << endl << endl;
3903
3904 // the following code would not compile:
3905 // using namespace ;
3906 // using namespace ::;
3907 if ((!ns.empty()) && (ns.compare(" ::") != 0)) {
3908 f_skeleton << "using namespace " << string(ns, 0, ns.size() - 2) << ";" << endl << endl;
3909 }
3910
3911 f_skeleton << "class " << svcname << "Handler : virtual public " << svcname << "If {" << endl
3912 << " public:" << endl;
3913 indent_up();
3914 f_skeleton << indent() << svcname << "Handler() {" << endl << indent()
3915 << " // Your initialization goes here" << endl << indent() << "}" << endl << endl;
3916
3917 vector<t_function*> functions = tservice->get_functions();
3918 vector<t_function*>::iterator f_iter;
3919 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
3920 generate_java_doc(f_skeleton, *f_iter);
3921 f_skeleton << indent() << function_signature(*f_iter, "") << " {" << endl << indent()
3922 << " // Your implementation goes here" << endl << indent() << " printf(\""
3923 << (*f_iter)->get_name() << "\\n\");" << endl << indent() << "}" << endl << endl;
3924 }
3925
3926 indent_down();
3927 f_skeleton << "};" << endl << endl;
3928
3929 f_skeleton << indent() << "int main(int argc, char **argv) {" << endl;
3930 indent_up();
3931 f_skeleton
3932 << indent() << "int port = 9090;" << endl << indent() << "::std::shared_ptr<" << svcname
3933 << "Handler> handler(new " << svcname << "Handler());" << endl << indent()
3934 << "::std::shared_ptr<TProcessor> processor(new " << svcname << "Processor(handler));" << endl
3935 << indent() << "::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));"
3936 << endl << indent()
3937 << "::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());" << endl
3938 << indent() << "::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());"
3939 << endl << endl << indent()
3940 << "TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);"
3941 << endl << indent() << "server.serve();" << endl << indent() << "return 0;" << endl;
3942 indent_down();
3943 f_skeleton << "}" << endl << endl;
3944
3945 // Close the files
3946 f_skeleton.close();
3947 }
3948
3949 /**
3950 * Deserializes a field of any type.
3951 */
generate_deserialize_field(ostream & out,t_field * tfield,string prefix,string suffix)3952 void t_cpp_generator::generate_deserialize_field(ostream& out,
3953 t_field* tfield,
3954 string prefix,
3955 string suffix) {
3956 t_type* type = get_true_type(tfield->get_type());
3957
3958 if (type->is_void()) {
3959 throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
3960 }
3961
3962 string name = prefix + tfield->get_name() + suffix;
3963
3964 if (type->is_struct() || type->is_xception()) {
3965 generate_deserialize_struct(out, (t_struct*)type, name, is_reference(tfield));
3966 } else if (type->is_container()) {
3967 generate_deserialize_container(out, type, name);
3968 } else if (type->is_base_type()) {
3969 indent(out) << "xfer += iprot->";
3970 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
3971 switch (tbase) {
3972 case t_base_type::TYPE_VOID:
3973 throw "compiler error: cannot serialize void field in a struct: " + name;
3974 break;
3975 case t_base_type::TYPE_STRING:
3976 if (type->is_binary()) {
3977 out << "readBinary(" << name << ");";
3978 } else {
3979 out << "readString(" << name << ");";
3980 }
3981 break;
3982 case t_base_type::TYPE_BOOL:
3983 out << "readBool(" << name << ");";
3984 break;
3985 case t_base_type::TYPE_I8:
3986 out << "readByte(" << name << ");";
3987 break;
3988 case t_base_type::TYPE_I16:
3989 out << "readI16(" << name << ");";
3990 break;
3991 case t_base_type::TYPE_I32:
3992 out << "readI32(" << name << ");";
3993 break;
3994 case t_base_type::TYPE_I64:
3995 out << "readI64(" << name << ");";
3996 break;
3997 case t_base_type::TYPE_DOUBLE:
3998 out << "readDouble(" << name << ");";
3999 break;
4000 default:
4001 throw "compiler error: no C++ reader for base type " + t_base_type::t_base_name(tbase) + name;
4002 }
4003 out << endl;
4004 } else if (type->is_enum()) {
4005 string t = tmp("ecast");
4006 out << indent() << "int32_t " << t << ";" << endl << indent() << "xfer += iprot->readI32(" << t
4007 << ");" << endl << indent() << name << " = static_cast<"
4008 << type_name(type) << ">(" << t << ");" << endl;
4009 } else {
4010 printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
4011 tfield->get_name().c_str(),
4012 type_name(type).c_str());
4013 }
4014 }
4015
4016 /**
4017 * Generates an unserializer for a variable. This makes two key assumptions,
4018 * first that there is a const char* variable named data that points to the
4019 * buffer for deserialization, and that there is a variable protocol which
4020 * is a reference to a TProtocol serialization object.
4021 */
generate_deserialize_struct(ostream & out,t_struct * tstruct,string prefix,bool pointer)4022 void t_cpp_generator::generate_deserialize_struct(ostream& out,
4023 t_struct* tstruct,
4024 string prefix,
4025 bool pointer) {
4026 if (pointer) {
4027 indent(out) << "if (!" << prefix << ") { " << endl;
4028 indent(out) << " " << prefix << " = ::std::shared_ptr<" << type_name(tstruct) << ">(new "
4029 << type_name(tstruct) << ");" << endl;
4030 indent(out) << "}" << endl;
4031 indent(out) << "xfer += " << prefix << "->read(iprot);" << endl;
4032 indent(out) << "bool wasSet = false;" << endl;
4033 const vector<t_field*>& members = tstruct->get_members();
4034 vector<t_field*>::const_iterator f_iter;
4035 for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) {
4036
4037 indent(out) << "if (" << prefix << "->__isset." << (*f_iter)->get_name()
4038 << ") { wasSet = true; }" << endl;
4039 }
4040 indent(out) << "if (!wasSet) { " << prefix << ".reset(); }" << endl;
4041 } else {
4042 indent(out) << "xfer += " << prefix << ".read(iprot);" << endl;
4043 }
4044 }
4045
generate_deserialize_container(ostream & out,t_type * ttype,string prefix)4046 void t_cpp_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
4047 scope_up(out);
4048
4049 string size = tmp("_size");
4050 string ktype = tmp("_ktype");
4051 string vtype = tmp("_vtype");
4052 string etype = tmp("_etype");
4053
4054 t_container* tcontainer = (t_container*)ttype;
4055 bool use_push = tcontainer->has_cpp_name();
4056
4057 indent(out) << prefix << ".clear();" << endl << indent() << "uint32_t " << size << ";" << endl;
4058
4059 // Declare variables, read header
4060 if (ttype->is_map()) {
4061 out << indent() << "::apache::thrift::protocol::TType " << ktype << ";" << endl << indent()
4062 << "::apache::thrift::protocol::TType " << vtype << ";" << endl << indent()
4063 << "xfer += iprot->readMapBegin(" << ktype << ", " << vtype << ", " << size << ");" << endl;
4064 } else if (ttype->is_set()) {
4065 out << indent() << "::apache::thrift::protocol::TType " << etype << ";" << endl << indent()
4066 << "xfer += iprot->readSetBegin(" << etype << ", " << size << ");" << endl;
4067 } else if (ttype->is_list()) {
4068 out << indent() << "::apache::thrift::protocol::TType " << etype << ";" << endl << indent()
4069 << "xfer += iprot->readListBegin(" << etype << ", " << size << ");" << endl;
4070 if (!use_push) {
4071 indent(out) << prefix << ".resize(" << size << ");" << endl;
4072 }
4073 }
4074
4075 // For loop iterates over elements
4076 string i = tmp("_i");
4077 out << indent() << "uint32_t " << i << ";" << endl << indent() << "for (" << i << " = 0; " << i
4078 << " < " << size << "; ++" << i << ")" << endl;
4079
4080 scope_up(out);
4081
4082 if (ttype->is_map()) {
4083 generate_deserialize_map_element(out, (t_map*)ttype, prefix);
4084 } else if (ttype->is_set()) {
4085 generate_deserialize_set_element(out, (t_set*)ttype, prefix);
4086 } else if (ttype->is_list()) {
4087 generate_deserialize_list_element(out, (t_list*)ttype, prefix, use_push, i);
4088 }
4089
4090 scope_down(out);
4091
4092 // Read container end
4093 if (ttype->is_map()) {
4094 indent(out) << "xfer += iprot->readMapEnd();" << endl;
4095 } else if (ttype->is_set()) {
4096 indent(out) << "xfer += iprot->readSetEnd();" << endl;
4097 } else if (ttype->is_list()) {
4098 indent(out) << "xfer += iprot->readListEnd();" << endl;
4099 }
4100
4101 scope_down(out);
4102 }
4103
4104 /**
4105 * Generates code to deserialize a map
4106 */
generate_deserialize_map_element(ostream & out,t_map * tmap,string prefix)4107 void t_cpp_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
4108 string key = tmp("_key");
4109 string val = tmp("_val");
4110 t_field fkey(tmap->get_key_type(), key);
4111 t_field fval(tmap->get_val_type(), val);
4112
4113 out << indent() << declare_field(&fkey) << endl;
4114
4115 generate_deserialize_field(out, &fkey);
4116 indent(out) << declare_field(&fval, false, false, false, true) << " = " << prefix << "[" << key
4117 << "];" << endl;
4118
4119 generate_deserialize_field(out, &fval);
4120 }
4121
generate_deserialize_set_element(ostream & out,t_set * tset,string prefix)4122 void t_cpp_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
4123 string elem = tmp("_elem");
4124 t_field felem(tset->get_elem_type(), elem);
4125
4126 indent(out) << declare_field(&felem) << endl;
4127
4128 generate_deserialize_field(out, &felem);
4129
4130 indent(out) << prefix << ".insert(" << elem << ");" << endl;
4131 }
4132
generate_deserialize_list_element(ostream & out,t_list * tlist,string prefix,bool use_push,string index)4133 void t_cpp_generator::generate_deserialize_list_element(ostream& out,
4134 t_list* tlist,
4135 string prefix,
4136 bool use_push,
4137 string index) {
4138 if (use_push) {
4139 string elem = tmp("_elem");
4140 t_field felem(tlist->get_elem_type(), elem);
4141 indent(out) << declare_field(&felem) << endl;
4142 generate_deserialize_field(out, &felem);
4143 indent(out) << prefix << ".push_back(" << elem << ");" << endl;
4144 } else {
4145 t_field felem(tlist->get_elem_type(), prefix + "[" + index + "]");
4146 generate_deserialize_field(out, &felem);
4147 }
4148 }
4149
4150 /**
4151 * Serializes a field of any type.
4152 *
4153 * @param tfield The field to serialize
4154 * @param prefix Name to prepend to field name
4155 */
generate_serialize_field(ostream & out,t_field * tfield,string prefix,string suffix)4156 void t_cpp_generator::generate_serialize_field(ostream& out,
4157 t_field* tfield,
4158 string prefix,
4159 string suffix) {
4160 t_type* type = get_true_type(tfield->get_type());
4161
4162 string name = prefix + tfield->get_name() + suffix;
4163
4164 // Do nothing for void types
4165 if (type->is_void()) {
4166 throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
4167 }
4168
4169 if (type->is_struct() || type->is_xception()) {
4170 generate_serialize_struct(out, (t_struct*)type, name, is_reference(tfield));
4171 } else if (type->is_container()) {
4172 generate_serialize_container(out, type, name);
4173 } else if (type->is_base_type() || type->is_enum()) {
4174
4175 indent(out) << "xfer += oprot->";
4176
4177 if (type->is_base_type()) {
4178 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
4179 switch (tbase) {
4180 case t_base_type::TYPE_VOID:
4181 throw "compiler error: cannot serialize void field in a struct: " + name;
4182 break;
4183 case t_base_type::TYPE_STRING:
4184 if (type->is_binary()) {
4185 out << "writeBinary(" << name << ");";
4186 } else {
4187 out << "writeString(" << name << ");";
4188 }
4189 break;
4190 case t_base_type::TYPE_BOOL:
4191 out << "writeBool(" << name << ");";
4192 break;
4193 case t_base_type::TYPE_I8:
4194 out << "writeByte(" << name << ");";
4195 break;
4196 case t_base_type::TYPE_I16:
4197 out << "writeI16(" << name << ");";
4198 break;
4199 case t_base_type::TYPE_I32:
4200 out << "writeI32(" << name << ");";
4201 break;
4202 case t_base_type::TYPE_I64:
4203 out << "writeI64(" << name << ");";
4204 break;
4205 case t_base_type::TYPE_DOUBLE:
4206 out << "writeDouble(" << name << ");";
4207 break;
4208 default:
4209 throw "compiler error: no C++ writer for base type " + t_base_type::t_base_name(tbase)
4210 + name;
4211 }
4212 } else if (type->is_enum()) {
4213 out << "writeI32(static_cast<int32_t>(" << name << "));";
4214 }
4215 out << endl;
4216 } else {
4217 printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n",
4218 name.c_str(),
4219 type_name(type).c_str());
4220 }
4221 }
4222
4223 /**
4224 * Serializes all the members of a struct.
4225 *
4226 * @param tstruct The struct to serialize
4227 * @param prefix String prefix to attach to all fields
4228 */
generate_serialize_struct(ostream & out,t_struct * tstruct,string prefix,bool pointer)4229 void t_cpp_generator::generate_serialize_struct(ostream& out,
4230 t_struct* tstruct,
4231 string prefix,
4232 bool pointer) {
4233 if (pointer) {
4234 indent(out) << "if (" << prefix << ") {" << endl;
4235 indent(out) << " xfer += " << prefix << "->write(oprot); " << endl;
4236 indent(out) << "} else {"
4237 << "oprot->writeStructBegin(\"" << tstruct->get_name() << "\"); " << endl;
4238 indent(out) << " oprot->writeStructEnd();" << endl;
4239 indent(out) << " oprot->writeFieldStop();" << endl;
4240 indent(out) << "}" << endl;
4241 } else {
4242 indent(out) << "xfer += " << prefix << ".write(oprot);" << endl;
4243 }
4244 }
4245
generate_serialize_container(ostream & out,t_type * ttype,string prefix)4246 void t_cpp_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
4247 scope_up(out);
4248
4249 if (ttype->is_map()) {
4250 indent(out) << "xfer += oprot->writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type())
4251 << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
4252 << "static_cast<uint32_t>(" << prefix << ".size()));" << endl;
4253 } else if (ttype->is_set()) {
4254 indent(out) << "xfer += oprot->writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type())
4255 << ", "
4256 << "static_cast<uint32_t>(" << prefix << ".size()));" << endl;
4257 } else if (ttype->is_list()) {
4258 indent(out) << "xfer += oprot->writeListBegin("
4259 << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", "
4260 << "static_cast<uint32_t>(" << prefix << ".size()));" << endl;
4261 }
4262
4263 string iter = tmp("_iter");
4264 out << indent() << type_name(ttype) << "::const_iterator " << iter << ";" << endl << indent()
4265 << "for (" << iter << " = " << prefix << ".begin(); " << iter << " != " << prefix
4266 << ".end(); ++" << iter << ")" << endl;
4267 scope_up(out);
4268 if (ttype->is_map()) {
4269 generate_serialize_map_element(out, (t_map*)ttype, iter);
4270 } else if (ttype->is_set()) {
4271 generate_serialize_set_element(out, (t_set*)ttype, iter);
4272 } else if (ttype->is_list()) {
4273 generate_serialize_list_element(out, (t_list*)ttype, iter);
4274 }
4275 scope_down(out);
4276
4277 if (ttype->is_map()) {
4278 indent(out) << "xfer += oprot->writeMapEnd();" << endl;
4279 } else if (ttype->is_set()) {
4280 indent(out) << "xfer += oprot->writeSetEnd();" << endl;
4281 } else if (ttype->is_list()) {
4282 indent(out) << "xfer += oprot->writeListEnd();" << endl;
4283 }
4284
4285 scope_down(out);
4286 }
4287
4288 /**
4289 * Serializes the members of a map.
4290 *
4291 */
generate_serialize_map_element(ostream & out,t_map * tmap,string iter)4292 void t_cpp_generator::generate_serialize_map_element(ostream& out, t_map* tmap, string iter) {
4293 t_field kfield(tmap->get_key_type(), iter + "->first");
4294 generate_serialize_field(out, &kfield, "");
4295
4296 t_field vfield(tmap->get_val_type(), iter + "->second");
4297 generate_serialize_field(out, &vfield, "");
4298 }
4299
4300 /**
4301 * Serializes the members of a set.
4302 */
generate_serialize_set_element(ostream & out,t_set * tset,string iter)4303 void t_cpp_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
4304 t_field efield(tset->get_elem_type(), "(*" + iter + ")");
4305 generate_serialize_field(out, &efield, "");
4306 }
4307
4308 /**
4309 * Serializes the members of a list.
4310 */
generate_serialize_list_element(ostream & out,t_list * tlist,string iter)4311 void t_cpp_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
4312 t_field efield(tlist->get_elem_type(), "(*" + iter + ")");
4313 generate_serialize_field(out, &efield, "");
4314 }
4315
4316 /**
4317 * Makes a :: prefix for a namespace
4318 *
4319 * @param ns The namespace, w/ periods in it
4320 * @return Namespaces
4321 */
namespace_prefix(string ns)4322 string t_cpp_generator::namespace_prefix(string ns) {
4323 // Always start with "::", to avoid possible name collisions with
4324 // other names in one of the current namespaces.
4325 //
4326 // We also need a leading space, in case the name is used inside of a
4327 // template parameter. "MyTemplate<::foo::Bar>" is not valid C++,
4328 // since "<:" is an alternative token for "[".
4329 string result = " ::";
4330
4331 if (ns.size() == 0) {
4332 return result;
4333 }
4334 string::size_type loc;
4335 while ((loc = ns.find(".")) != string::npos) {
4336 result += ns.substr(0, loc);
4337 result += "::";
4338 ns = ns.substr(loc + 1);
4339 }
4340 if (ns.size() > 0) {
4341 result += ns + "::";
4342 }
4343 return result;
4344 }
4345
4346 /**
4347 * Opens namespace.
4348 *
4349 * @param ns The namespace, w/ periods in it
4350 * @return Namespaces
4351 */
namespace_open(string ns)4352 string t_cpp_generator::namespace_open(string ns) {
4353 if (ns.size() == 0) {
4354 return "";
4355 }
4356 string result = "";
4357 string separator = "";
4358 string::size_type loc;
4359 while ((loc = ns.find(".")) != string::npos) {
4360 result += separator;
4361 result += "namespace ";
4362 result += ns.substr(0, loc);
4363 result += " {";
4364 separator = " ";
4365 ns = ns.substr(loc + 1);
4366 }
4367 if (ns.size() > 0) {
4368 result += separator + "namespace " + ns + " {";
4369 }
4370 return result;
4371 }
4372
4373 /**
4374 * Closes namespace.
4375 *
4376 * @param ns The namespace, w/ periods in it
4377 * @return Namespaces
4378 */
namespace_close(string ns)4379 string t_cpp_generator::namespace_close(string ns) {
4380 if (ns.size() == 0) {
4381 return "";
4382 }
4383 string result = "}";
4384 string::size_type loc;
4385 while ((loc = ns.find(".")) != string::npos) {
4386 result += "}";
4387 ns = ns.substr(loc + 1);
4388 }
4389 result += " // namespace";
4390 return result;
4391 }
4392
4393 /**
4394 * Returns a C++ type name
4395 *
4396 * @param ttype The type
4397 * @return String of the type name, i.e. std::set<type>
4398 */
type_name(t_type * ttype,bool in_typedef,bool arg)4399 string t_cpp_generator::type_name(t_type* ttype, bool in_typedef, bool arg) {
4400 if (ttype->is_base_type()) {
4401 string bname = base_type_name(((t_base_type*)ttype)->get_base());
4402 std::map<string, std::vector<string>>::iterator it = ttype->annotations_.find("cpp.type");
4403 if (it != ttype->annotations_.end() && !it->second.empty()) {
4404 bname = it->second.back();
4405 }
4406
4407 if (!arg) {
4408 return bname;
4409 }
4410
4411 if (((t_base_type*)ttype)->get_base() == t_base_type::TYPE_STRING) {
4412 return "const " + bname + "&";
4413 } else {
4414 return "const " + bname;
4415 }
4416 }
4417
4418 // Check for a custom overloaded C++ name
4419 if (ttype->is_container()) {
4420 string cname;
4421
4422 t_container* tcontainer = (t_container*)ttype;
4423 if (tcontainer->has_cpp_name()) {
4424 cname = tcontainer->get_cpp_name();
4425 } else if (ttype->is_map()) {
4426 t_map* tmap = (t_map*)ttype;
4427 cname = "std::map<" + type_name(tmap->get_key_type(), in_typedef) + ", "
4428 + type_name(tmap->get_val_type(), in_typedef) + "> ";
4429 } else if (ttype->is_set()) {
4430 t_set* tset = (t_set*)ttype;
4431 cname = "std::set<" + type_name(tset->get_elem_type(), in_typedef) + "> ";
4432 } else if (ttype->is_list()) {
4433 t_list* tlist = (t_list*)ttype;
4434 cname = "std::vector<" + type_name(tlist->get_elem_type(), in_typedef) + "> ";
4435 }
4436
4437 if (arg) {
4438 return "const " + cname + "&";
4439 } else {
4440 return cname;
4441 }
4442 }
4443
4444 string class_prefix;
4445 if (in_typedef && (ttype->is_struct() || ttype->is_xception())) {
4446 class_prefix = "class ";
4447 }
4448
4449 // Check if it needs to be namespaced
4450 string pname;
4451 t_program* program = ttype->get_program();
4452 if (program != nullptr && program != program_) {
4453 pname = class_prefix + namespace_prefix(program->get_namespace("cpp")) + ttype->get_name();
4454 } else {
4455 pname = class_prefix + ttype->get_name();
4456 }
4457
4458 if (ttype->is_enum() && !gen_pure_enums_) {
4459 pname += "::type";
4460 }
4461
4462 if (arg) {
4463 if (is_complex_type(ttype)) {
4464 return "const " + pname + "&";
4465 } else {
4466 return "const " + pname;
4467 }
4468 } else {
4469 return pname;
4470 }
4471 }
4472
4473 /**
4474 * Returns the C++ type that corresponds to the thrift type.
4475 *
4476 * @param tbase The base type
4477 * @return Explicit C++ type, i.e. "int32_t"
4478 */
base_type_name(t_base_type::t_base tbase)4479 string t_cpp_generator::base_type_name(t_base_type::t_base tbase) {
4480 switch (tbase) {
4481 case t_base_type::TYPE_VOID:
4482 return "void";
4483 case t_base_type::TYPE_STRING:
4484 return "std::string";
4485 case t_base_type::TYPE_BOOL:
4486 return "bool";
4487 case t_base_type::TYPE_I8:
4488 return "int8_t";
4489 case t_base_type::TYPE_I16:
4490 return "int16_t";
4491 case t_base_type::TYPE_I32:
4492 return "int32_t";
4493 case t_base_type::TYPE_I64:
4494 return "int64_t";
4495 case t_base_type::TYPE_DOUBLE:
4496 return "double";
4497 default:
4498 throw "compiler error: no C++ base type name for base type " + t_base_type::t_base_name(tbase);
4499 }
4500 }
4501
4502 /**
4503 * Declares a field, which may include initialization as necessary.
4504 *
4505 * @param ttype The type
4506 * @return Field declaration, i.e. int x = 0;
4507 */
declare_field(t_field * tfield,bool init,bool pointer,bool constant,bool reference)4508 string t_cpp_generator::declare_field(t_field* tfield,
4509 bool init,
4510 bool pointer,
4511 bool constant,
4512 bool reference) {
4513 // TODO(mcslee): do we ever need to initialize the field?
4514 string result = "";
4515 if (constant) {
4516 result += "const ";
4517 }
4518 result += type_name(tfield->get_type());
4519 if (is_reference(tfield)) {
4520 result = "::std::shared_ptr<" + result + ">";
4521 }
4522 if (pointer) {
4523 result += "*";
4524 }
4525 if (reference) {
4526 result += "&";
4527 }
4528 result += " " + tfield->get_name();
4529 if (init) {
4530 t_type* type = get_true_type(tfield->get_type());
4531
4532 if (type->is_base_type()) {
4533 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
4534 switch (tbase) {
4535 case t_base_type::TYPE_VOID:
4536 case t_base_type::TYPE_STRING:
4537 break;
4538 case t_base_type::TYPE_BOOL:
4539 result += " = false";
4540 break;
4541 case t_base_type::TYPE_I8:
4542 case t_base_type::TYPE_I16:
4543 case t_base_type::TYPE_I32:
4544 case t_base_type::TYPE_I64:
4545 result += " = 0";
4546 break;
4547 case t_base_type::TYPE_DOUBLE:
4548 result += " = 0.0";
4549 break;
4550 default:
4551 throw "compiler error: no C++ initializer for base type " + t_base_type::t_base_name(tbase);
4552 }
4553 } else if (type->is_enum()) {
4554 result += " = static_cast<" + type_name(type) + ">(0)";
4555 }
4556 }
4557 if (!reference) {
4558 result += ";";
4559 }
4560 return result;
4561 }
4562
4563 /**
4564 * Renders a function signature of the form 'type name(args)'
4565 *
4566 * @param tfunction Function definition
4567 * @return String of rendered function definition
4568 */
function_signature(t_function * tfunction,string style,string prefix,bool name_params)4569 string t_cpp_generator::function_signature(t_function* tfunction,
4570 string style,
4571 string prefix,
4572 bool name_params) {
4573 t_type* ttype = tfunction->get_returntype();
4574 t_struct* arglist = tfunction->get_arglist();
4575 bool has_xceptions = !tfunction->get_xceptions()->get_members().empty();
4576
4577 if (style == "") {
4578 if (is_complex_type(ttype)) {
4579 return "void " + prefix + tfunction->get_name() + "(" + type_name(ttype)
4580 + (name_params ? "& _return" : "& /* _return */")
4581 + argument_list(arglist, name_params, true) + ")";
4582 } else {
4583 return type_name(ttype) + " " + prefix + tfunction->get_name() + "("
4584 + argument_list(arglist, name_params) + ")";
4585 }
4586 } else if (style.substr(0, 3) == "Cob") {
4587 string cob_type;
4588 string exn_cob;
4589 if (style == "CobCl") {
4590 cob_type = "(" + service_name_ + "CobClient";
4591 if (gen_templates_) {
4592 cob_type += "T<Protocol_>";
4593 }
4594 cob_type += "* client)";
4595 } else if (style == "CobSv") {
4596 cob_type = (ttype->is_void() ? "()" : ("(" + type_name(ttype) + " const& _return)"));
4597 if (has_xceptions) {
4598 exn_cob
4599 = ", ::std::function<void(::apache::thrift::TDelayedException* _throw)> /* exn_cob */";
4600 }
4601 } else {
4602 throw "UNKNOWN STYLE";
4603 }
4604
4605 return "void " + prefix + tfunction->get_name() + "(::std::function<void" + cob_type + "> cob"
4606 + exn_cob + argument_list(arglist, name_params, true) + ")";
4607 } else {
4608 throw "UNKNOWN STYLE";
4609 }
4610 }
4611
4612 /**
4613 * Renders a field list
4614 *
4615 * @param tstruct The struct definition
4616 * @return Comma sepearated list of all field names in that struct
4617 */
argument_list(t_struct * tstruct,bool name_params,bool start_comma)4618 string t_cpp_generator::argument_list(t_struct* tstruct, bool name_params, bool start_comma) {
4619 string result = "";
4620
4621 const vector<t_field*>& fields = tstruct->get_members();
4622 vector<t_field*>::const_iterator f_iter;
4623 bool first = !start_comma;
4624 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
4625 if (first) {
4626 first = false;
4627 } else {
4628 result += ", ";
4629 }
4630 result += type_name((*f_iter)->get_type(), false, true) + " "
4631 + (name_params ? (*f_iter)->get_name() : "/* " + (*f_iter)->get_name() + " */");
4632 }
4633 return result;
4634 }
4635
4636 /**
4637 * Converts the parse type to a C++ enum string for the given type.
4638 *
4639 * @param type Thrift Type
4640 * @return String of C++ code to definition of that type constant
4641 */
type_to_enum(t_type * type)4642 string t_cpp_generator::type_to_enum(t_type* type) {
4643 type = get_true_type(type);
4644
4645 if (type->is_base_type()) {
4646 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
4647 switch (tbase) {
4648 case t_base_type::TYPE_VOID:
4649 throw "NO T_VOID CONSTRUCT";
4650 case t_base_type::TYPE_STRING:
4651 return "::apache::thrift::protocol::T_STRING";
4652 case t_base_type::TYPE_BOOL:
4653 return "::apache::thrift::protocol::T_BOOL";
4654 case t_base_type::TYPE_I8:
4655 return "::apache::thrift::protocol::T_BYTE";
4656 case t_base_type::TYPE_I16:
4657 return "::apache::thrift::protocol::T_I16";
4658 case t_base_type::TYPE_I32:
4659 return "::apache::thrift::protocol::T_I32";
4660 case t_base_type::TYPE_I64:
4661 return "::apache::thrift::protocol::T_I64";
4662 case t_base_type::TYPE_DOUBLE:
4663 return "::apache::thrift::protocol::T_DOUBLE";
4664 default:
4665 break;
4666 }
4667 } else if (type->is_enum()) {
4668 return "::apache::thrift::protocol::T_I32";
4669 } else if (type->is_struct()) {
4670 return "::apache::thrift::protocol::T_STRUCT";
4671 } else if (type->is_xception()) {
4672 return "::apache::thrift::protocol::T_STRUCT";
4673 } else if (type->is_map()) {
4674 return "::apache::thrift::protocol::T_MAP";
4675 } else if (type->is_set()) {
4676 return "::apache::thrift::protocol::T_SET";
4677 } else if (type->is_list()) {
4678 return "::apache::thrift::protocol::T_LIST";
4679 }
4680
4681 throw "INVALID TYPE IN type_to_enum: " + type->get_name();
4682 }
4683
4684
is_struct_storage_not_throwing(t_struct * tstruct) const4685 bool t_cpp_generator::is_struct_storage_not_throwing(t_struct* tstruct) const {
4686 vector<t_field*> members = tstruct->get_members();
4687
4688 for(size_t i=0; i < members.size(); ++i) {
4689 t_type* type = get_true_type(members[i]->get_type());
4690
4691 if(type->is_enum())
4692 continue;
4693 if(type->is_xception())
4694 return false;
4695 if(type->is_base_type()) switch(((t_base_type*)type)->get_base()) {
4696 case t_base_type::TYPE_BOOL:
4697 case t_base_type::TYPE_I8:
4698 case t_base_type::TYPE_I16:
4699 case t_base_type::TYPE_I32:
4700 case t_base_type::TYPE_I64:
4701 case t_base_type::TYPE_DOUBLE:
4702 continue;
4703 case t_base_type::TYPE_VOID:
4704 case t_base_type::TYPE_STRING:
4705 default:
4706 return false;
4707 }
4708 if(type->is_struct()) {
4709 const vector<t_field*>& more = ((t_struct*)type)->get_members();
4710 for(auto it = more.begin(); it < more.end(); ++it) {
4711 if(std::find(members.begin(), members.end(), *it) == members.end())
4712 members.push_back(*it);
4713 }
4714 continue;
4715 }
4716 return false; // rest-as-throwing(require-alloc)
4717 }
4718 return true;
4719 }
4720
4721
get_include_prefix(const t_program & program) const4722 string t_cpp_generator::get_include_prefix(const t_program& program) const {
4723 string include_prefix = program.get_include_prefix();
4724 if (!use_include_prefix_ || (include_prefix.size() > 0 && include_prefix[0] == '/')) {
4725 // if flag is turned off or this is absolute path, return empty prefix
4726 return "";
4727 }
4728
4729 string::size_type last_slash = string::npos;
4730 if ((last_slash = include_prefix.rfind("/")) != string::npos) {
4731 return include_prefix.substr(0, last_slash)
4732 + (get_program()->is_out_path_absolute() ? "/" : "/" + out_dir_base_ + "/");
4733 }
4734
4735 return "";
4736 }
4737
get_legal_program_name(std::string program_name)4738 string t_cpp_generator::get_legal_program_name(std::string program_name)
4739 {
4740 std::size_t found = 0;
4741
4742 while(true) {
4743 found = program_name.find('.');
4744
4745 if(found != string::npos) {
4746 program_name = program_name.replace(found, 1, "_");
4747 } else {
4748 break;
4749 }
4750 }
4751
4752 return program_name;
4753 }
4754
display_name() const4755 std::string t_cpp_generator::display_name() const {
4756 return "C++";
4757 }
4758
4759
4760 THRIFT_REGISTER_GENERATOR(
4761 cpp,
4762 "C++",
4763 " cob_style: Generate \"Continuation OBject\"-style classes.\n"
4764 " no_client_completion:\n"
4765 " Omit calls to completion__() in CobClient class.\n"
4766 " no_default_operators:\n"
4767 " Omits generation of default operators ==, != and <\n"
4768 " templates: Generate templatized reader/writer methods.\n"
4769 " pure_enums: Generate pure enums instead of wrapper classes.\n"
4770 " include_prefix: Use full include paths in generated files.\n"
4771 " moveable_types: Generate move constructors and assignment operators.\n"
4772 " no_ostream_operators:\n"
4773 " Omit generation of ostream definitions.\n"
4774 " no_skeleton: Omits generation of skeleton.\n")
4775