From bd0db88477f02c67962c638c8dfb5f2bb6619144 Mon Sep 17 00:00:00 2001 From: David Reiss Date: Wed, 27 Feb 2008 01:54:51 +0000 Subject: [PATCH] Infrastructure for loading code generators a bit more dynamically. Add a generic and easy-to-use mechanism for Thrift code generators to register themselves centrally. The central registry is used to obtain documentation for the options accepted by individual generators and get instances of individual generators. It also does a little bit of option parsing that will be useful for all generators. Obviously, this change cannot be tested on its own. I can only say that Thrift still builds and runs correctly. Subsequent changes will apply this infrastructure to specific code generators. Steve Grimm has assured me that this is standard Git practice. In fact, I ran this test after converting the C++ and Java generators: dreiss@dreiss-vmware:dynamic_generators:thrift/test$ mkdir old new dreiss@dreiss-vmware:dynamic_generators:thrift/test$ cd old dreiss@dreiss-vmware:dynamic_generators:thrift/test/old$ ../../compiler/cpp/thrift -cpp -dense -java -javabean ../DebugProtoTest.thrift [WARNING::1] -cpp is deprecated. Use --gen cpp [WARNING::1] -java is deprecated. Use --gen java [WARNING::1] -javabean is deprecated. Use --gen java:beans dreiss@dreiss-vmware:dynamic_generators:thrift/test/old$ cd ../new/ dreiss@dreiss-vmware:dynamic_generators:thrift/test/new$ ../../compiler/cpp/thrift --gen cpp:dense --gen java --gen java:beans ../DebugProtoTest.thrift dreiss@dreiss-vmware:dynamic_generators:thrift/test/new$ cd .. dreiss@dreiss-vmware:dynamic_generators:thrift/test$ diff -ur old/ new/ dreiss@dreiss-vmware:dynamic_generators:thrift/test$ git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665507 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/cpp/src/generate/t_generator.cc | 63 +++++++++++++++++++ compiler/cpp/src/generate/t_generator.h | 79 ++++++++++++++++++++++++ compiler/cpp/src/main.cc | 45 ++++++++++++-- 3 files changed, 183 insertions(+), 4 deletions(-) diff --git a/compiler/cpp/src/generate/t_generator.cc b/compiler/cpp/src/generate/t_generator.cc index 98ed6ee6..ff3ed57e 100644 --- a/compiler/cpp/src/generate/t_generator.cc +++ b/compiler/cpp/src/generate/t_generator.cc @@ -66,3 +66,66 @@ void t_generator::generate_consts(vector consts) { generate_const(*c_iter); } } + + +void t_generator_registry::register_generator(t_generator_factory* factory) { + gen_map_t& the_map = get_generator_map(); + if (the_map.find(factory->get_short_name()) != the_map.end()) { + failure("Duplicate generators for language \"%s\"!\n", factory->get_short_name().c_str()); + } + the_map[factory->get_short_name()] = factory; +} + +t_generator* t_generator_registry::get_generator(t_program* program, + const string& options) { + string::size_type colon = options.find(':'); + string language = options.substr(0, colon); + + map parsed_options; + if (colon != string::npos) { + string::size_type pos = colon+1; + while (pos != string::npos && pos < options.size()) { + string::size_type next_pos = options.find(',', pos); + string option = options.substr(pos, next_pos-pos); + pos = ((next_pos == string::npos) ? next_pos : next_pos+1); + + string::size_type separator = option.find('='); + string key, value; + if (separator == string::npos) { + key = option; + value = ""; + } else { + key = option.substr(0, separator); + value = option.substr(separator+1); + } + + parsed_options[key] = value; + } + } + + gen_map_t& the_map = get_generator_map(); + gen_map_t::iterator iter = the_map.find(language); + + if (iter == the_map.end()) { + return NULL; + } + + return iter->second->get_generator(program, parsed_options, options); +} + +t_generator_registry::gen_map_t& t_generator_registry::get_generator_map() { + // http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 + static gen_map_t* the_map = new gen_map_t(); + return *the_map; +} + +t_generator_factory::t_generator_factory( + const std::string& short_name, + const std::string& long_name, + const std::string& documentation) + : short_name_(short_name) + , long_name_(long_name) + , documentation_(documentation) +{ + t_generator_registry::register_generator(this); +} diff --git a/compiler/cpp/src/generate/t_generator.h b/compiler/cpp/src/generate/t_generator.h index 734e0338..594bed96 100644 --- a/compiler/cpp/src/generate/t_generator.h +++ b/compiler/cpp/src/generate/t_generator.h @@ -191,4 +191,83 @@ class t_generator { int tmp_; }; + +/** + * A factory for producing generator classes of a particular language. + * + * This class is also responsible for: + * - Registering itself with the generator registry. + * - Providing documentation for the generators it produces. + */ +class t_generator_factory { + public: + t_generator_factory(const std::string& short_name, + const std::string& long_name, + const std::string& documentation); + + virtual ~t_generator_factory() {} + + virtual t_generator* get_generator( + // The program to generate. + t_program* program, + // Note: parsed_options will not exist beyond the call to get_generator. + const std::map& parsed_options, + // Note: option_string might not exist beyond the call to get_generator. + const std::string& option_string) + = 0; + + std::string get_short_name() { return short_name_; } + std::string get_long_name() { return long_name_; } + std::string get_documentation() { return documentation_; } + + private: + std::string short_name_; + std::string long_name_; + std::string documentation_; +}; + +template +class t_generator_factory_impl : public t_generator_factory { + public: + t_generator_factory_impl(const std::string& short_name, + const std::string& long_name, + const std::string& documentation) + : t_generator_factory(short_name, long_name, documentation) + {} + + virtual t_generator* get_generator( + t_program* program, + const std::map& parsed_options, + const std::string& option_string) { + return new generator(program, parsed_options, option_string); + } +}; + +class t_generator_registry { + public: + static void register_generator(t_generator_factory* factory); + + static t_generator* get_generator(t_program* program, + const std::string& options); + + typedef std::map gen_map_t; + static gen_map_t& get_generator_map(); + + private: + t_generator_registry(); + t_generator_registry(const t_generator_registry&); +}; + +#define THRIFT_REGISTER_GENERATOR(language, long_name, doc) \ + class t_##language##_generator_factory_impl \ + : public t_generator_factory_impl \ + { \ + public: \ + t_##language##_generator_factory_impl() \ + : t_generator_factory_impl( \ + #language, long_name, doc) \ + {} \ + }; \ + static t_##language##_generator_factory_impl _registerer; + #endif diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc index 8b1848c9..b24a9030 100644 --- a/compiler/cpp/src/main.cc +++ b/compiler/cpp/src/main.cc @@ -635,6 +635,21 @@ void usage() { fprintf(stderr, " -v[erbose] Verbose mode\n"); fprintf(stderr, " -r[ecurse] Also generate included files\n"); fprintf(stderr, " -debug Parse debug trace to stdout\n"); + fprintf(stderr, " --gen STR Generate code with a dynamically-registered generator.\n"); + fprintf(stderr, " STR has the form language[:key1=val1[,key2,[key3=val3]]].\n"); + fprintf(stderr, " Keys and values are options passed to the generator.\n"); + fprintf(stderr, " Many options will not require values.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Available generators (and options):\n"); + + t_generator_registry::gen_map_t gen_map = t_generator_registry::get_generator_map(); + t_generator_registry::gen_map_t::iterator iter; + for (iter = gen_map.begin(); iter != gen_map.end(); ++iter) { + fprintf(stderr, " %s (%s):\n", + iter->second->get_short_name().c_str(), + iter->second->get_long_name().c_str()); + fprintf(stderr, "%s", iter->second->get_documentation().c_str()); + } exit(1); } @@ -839,7 +854,7 @@ void parse(t_program* program, t_program* parent_program) { /** * Generate code */ -void generate(t_program* program) { +void generate(t_program* program, const vector& generator_strings) { // Oooohh, recursive code generation, hot!! if (gen_recurse) { const vector& includes = program->get_includes(); @@ -847,7 +862,7 @@ void generate(t_program* program) { // Propogate output path from parent to child programs includes[i]->set_out_path(program->get_out_path()); - generate(includes[i]); + generate(includes[i], generator_strings); } } @@ -967,6 +982,19 @@ void generate(t_program* program) { if (dump_docs) { dump_docstrings(program); } + + vector::const_iterator iter; + for (iter = generator_strings.begin(); iter != generator_strings.end(); ++iter) { + t_generator* generator = t_generator_registry::get_generator(program, *iter); + + if (generator == NULL) { + pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str()); + } else { + pverbose("Generating \"%s\"\n", iter->c_str()); + generator->generate_program(); + } + } + } catch (string s) { printf("Error: %s\n", s.c_str()); } catch (const char* exc) { @@ -993,6 +1021,8 @@ int main(int argc, char** argv) { usage(); } + vector generator_strings; + // Set the current path to a dummy value to make warning messages clearer. g_curpath = "arguments"; @@ -1017,6 +1047,13 @@ int main(int argc, char** argv) { g_verbose = 1; } else if (strcmp(arg, "-r") == 0 || strcmp(arg, "-recurse") == 0 ) { gen_recurse = true; + } else if (strcmp(arg, "-gen") == 0) { + arg = argv[++i]; + if (arg == NULL) { + fprintf(stderr, "!!! Missing generator specification"); + usage(); + } + generator_strings.push_back(arg); } else if (strcmp(arg, "-dense") == 0) { gen_dense = true; } else if (strcmp(arg, "-cpp") == 0) { @@ -1115,7 +1152,7 @@ int main(int argc, char** argv) { } // You gotta generate something! - if (!gen_cpp && !gen_java && !gen_javabean && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl && !gen_erl && !gen_ocaml && !gen_hs && !gen_cocoa && !gen_st && !gen_csharp) { + if (!gen_cpp && !gen_java && !gen_javabean && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl && !gen_erl && !gen_ocaml && !gen_hs && !gen_cocoa && !gen_st && !gen_csharp && generator_strings.empty()) { fprintf(stderr, "!!! No output language(s) specified\n\n"); usage(); } @@ -1170,7 +1207,7 @@ int main(int argc, char** argv) { yylineno = 1; // Generate it! - generate(program); + generate(program, generator_strings); // Clean up. Who am I kidding... this program probably orphans heap memory // all over the place, but who cares because it is about to exit and it is -- 2.17.1