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
diff --git a/compiler/cpp/src/generate/t_generator.cc b/compiler/cpp/src/generate/t_generator.cc
index 98ed6ee..ff3ed57 100644
--- a/compiler/cpp/src/generate/t_generator.cc
+++ b/compiler/cpp/src/generate/t_generator.cc
@@ -66,3 +66,66 @@
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<string, string> 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 734e033..594bed9 100644
--- a/compiler/cpp/src/generate/t_generator.h
+++ b/compiler/cpp/src/generate/t_generator.h
@@ -191,4 +191,83 @@
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<std::string, std::string>& 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 <typename generator>
+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<std::string, std::string>& 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<std::string, t_generator_factory*> 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<t_##language##_generator> \
+ { \
+ public: \
+ t_##language##_generator_factory_impl() \
+ : t_generator_factory_impl<t_##language##_generator>( \
+ #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 8b1848c..b24a903 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -635,6 +635,21 @@
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 @@
/**
* Generate code
*/
-void generate(t_program* program) {
+void generate(t_program* program, const vector<string>& generator_strings) {
// Oooohh, recursive code generation, hot!!
if (gen_recurse) {
const vector<t_program*>& includes = program->get_includes();
@@ -847,7 +862,7 @@
// 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 @@
if (dump_docs) {
dump_docstrings(program);
}
+
+ vector<string>::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 @@
usage();
}
+ vector<string> generator_strings;
+
// Set the current path to a dummy value to make warning messages clearer.
g_curpath = "arguments";
@@ -1017,6 +1047,13 @@
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 @@
}
// 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 @@
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