THRIFT-2835 Add possibility to distribute generators separately from thrift core, and load them dynamically
Client: Compiler
Patch: Nobuaki Sukegawa, rebased by dtmuller

Also fixed by dtmuller:
* Add plugin namespace for erlang language binding
* Fix unit test test_const_value
* Don't clear type cache with every t_program conversion
* Type "wb" may not be supported by popen on non-Windows platforms
* Fix constness of AST type signatures
diff --git a/compiler/cpp/CMakeLists.txt b/compiler/cpp/CMakeLists.txt
index b7ed6ea..48cadbc 100644
--- a/compiler/cpp/CMakeLists.txt
+++ b/compiler/cpp/CMakeLists.txt
@@ -21,7 +21,7 @@
 if(MSVC)
     configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/windows/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
 else()
-    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
 endif()
 
 find_package(FLEX REQUIRED)
@@ -47,39 +47,17 @@
 add_library(libparse STATIC ${libparse_SOURCES})
 
 # Create the thrift compiler
-set( thrift_SOURCES
-    src/main.cc
+set(compiler_core
+    src/common.cc
     src/generate/t_generator.cc
-    src/generate/t_generator_registry.h
-    src/globals.h
-    src/main.h
-    src/platform.h
-    src/audit/t_audit.cpp
-    src/parse/t_doc.h
-    src/parse/t_type.h
-    src/parse/t_base_type.h
-    src/parse/t_enum.h
-    src/parse/t_enum_value.h
-    src/parse/t_typedef.h
     src/parse/t_typedef.cc
-    src/parse/t_container.h
-    src/parse/t_list.h
-    src/parse/t_set.h
-    src/parse/t_map.h
-    src/parse/t_struct.h
-    src/parse/t_field.h
-    src/parse/t_service.h
-    src/parse/t_function.h
-    src/parse/t_program.h
-    src/parse/t_scope.h
-    src/parse/t_const.h
-    src/parse/t_const_value.h
     src/parse/parse.cc
-    src/generate/t_generator.h
-    src/generate/t_oop_generator.h
-    src/generate/t_html_generator.h
-    src/windows/config.h
-    version.h
+    ${CMAKE_CURRENT_BINARY_DIR}/version.h
+)
+
+set(thrift-compiler_SOURCES
+    src/main.cc
+    src/audit/t_audit.cpp
 )
 
 # This macro adds an option THRIFT_COMPILER_${NAME}
@@ -89,7 +67,7 @@
     set(src "src/generate/t_${name}_generator.cc")
     option(${enabler} ${description} ${initial})
     if(${enabler})
-        list(APPEND thrift_SOURCES ${src})
+        list(APPEND thrift-compiler_SOURCES ${src})
     endif()
 endmacro()
 
@@ -127,9 +105,56 @@
 # we also add the current binary directory for generated files
 include_directories(${CMAKE_CURRENT_BINARY_DIR} src)
 
-add_executable(thrift-compiler ${thrift_SOURCES})
+if(NOT ${WITH_PLUGIN})
+    list(APPEND thrift-compiler_SOURCES ${compiler_core})
+endif()
+add_executable(thrift-compiler ${thrift-compiler_SOURCES})
+
+if(${WITH_PLUGIN})
+    add_executable(thrift-bootstrap ${compiler_core}
+        src/main.cc
+        src/audit/t_audit.cpp
+        src/generate/t_cpp_generator.cc
+    )
+    target_link_libraries(thrift-bootstrap libparse)
+
+    set(PLUGIN_GEN_SOURCES
+        ${CMAKE_CURRENT_BINARY_DIR}/plugin/plugin_types.h
+        ${CMAKE_CURRENT_BINARY_DIR}/plugin/plugin_types.cpp
+        ${CMAKE_CURRENT_BINARY_DIR}/plugin/plugin_constants.h
+        ${CMAKE_CURRENT_BINARY_DIR}/plugin/plugin_constants.cpp
+    )
+
+    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/plugin)
+    add_custom_command(OUTPUT ${PLUGIN_GEN_SOURCES}
+        DEPENDS thrift-bootstrap src/plugin/plugin.thrift
+        COMMAND thrift-bootstrap -gen cpp
+        -out ${CMAKE_CURRENT_BINARY_DIR}/plugin
+        ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin/plugin.thrift
+    )
+
+    include_directories(../../lib/cpp/src)
+
+    include(ThriftMacros)
+    ADD_LIBRARY_THRIFT(thriftc
+        ${compiler_core}
+        ${PLUGIN_GEN_SOURCES}
+        src/logging.cc
+        src/plugin/plugin_output.cc
+        src/plugin/plugin.cc
+    )
+    TARGET_INCLUDE_DIRECTORIES_THRIFT(thriftc PUBLIC ${Boost_INCLUDE_DIRS})
+    TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY(thriftc thrift PUBLIC)
+    target_compile_definitions(thrift-compiler PUBLIC THRIFT_ENABLE_PLUGIN)
+    LINK_AGAINST_THRIFT_LIBRARY(thrift-compiler thriftc)
+endif()
+
 set_target_properties(thrift-compiler PROPERTIES OUTPUT_NAME thrift)
 
 target_link_libraries(thrift-compiler libparse)
 
 install(TARGETS thrift-compiler DESTINATION "${BIN_INSTALL_DIR}")
+
+if(BUILD_TESTING)
+    add_subdirectory(test)
+endif()
diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am
index 49ec26e..f7a7171 100644
--- a/compiler/cpp/Makefile.am
+++ b/compiler/cpp/Makefile.am
@@ -21,25 +21,34 @@
 # Please see doc/old-thrift-license.txt in the Thrift distribution for
 # details.
 
-AM_YFLAGS = -d
-LIBS =
-BUILT_SOURCES = src/thrifty.cc
+AUTOMAKE_OPTIONS = subdir-objects
+
+# Note on why we have src and src/plugin directories:
+# Since Automake supports only one set of BUILT_SOURCES per file and does not allow
+# SUBDIRS built before BUILT_SOURCES, we end up separate Makefile.am for each source
+# code generation, i.e. lex-yacc and Thrift, to achieve stable parallel make.
+
+SUBDIRS = src src/plugin .
+if WITH_TESTS
+SUBDIRS += test
+endif
 
 bin_PROGRAMS = thrift
 
-noinst_LIBRARIES = libparse.a
-
 thrift_OBJDIR = obj
 
-thrift_SOURCES = src/main.cc \
+plugin_gen = src/plugin/plugin_types.h \
+             src/plugin/plugin_types.cpp \
+             src/plugin/plugin_constants.h \
+             src/plugin/plugin_constants.cpp
+
+compiler_core =  src/common.h \
+                 src/common.cc \
                  src/generate/t_generator.cc \
                  src/generate/t_generator_registry.h \
                  src/globals.h \
-                 src/main.h \
                  src/platform.h \
                  src/logging.h \
-                 src/audit/t_audit.cpp \
-                 src/audit/t_audit.h \
                  src/parse/t_doc.h \
                  src/parse/t_type.h \
                  src/parse/t_base_type.h \
@@ -66,6 +75,11 @@
                  src/windows/config.h \
                  src/windows/version.h
 
+thrift_SOURCES = src/main.h \
+                 src/main.cc \
+                 src/audit/t_audit.cpp \
+                 src/audit/t_audit.h
+
 # Specific client generator source
 thrift_SOURCES += src/generate/t_c_glib_generator.cc \
                   src/generate/t_cpp_generator.cc \
@@ -98,13 +112,32 @@
 
 thrift_CPPFLAGS = -I$(srcdir)/src
 thrift_CXXFLAGS = -Wall -Wextra -pedantic
-thrift_LDADD = @LEXLIB@ libparse.a
+thrift_LDADD = @LEXLIB@ src/libparse.a
 
-libparse_a_CPPFLAGS = -I$(srcdir)/src
-libparse_a_CXXFLAGS = -Wall -Wno-sign-compare -Wno-unused
+if !WITH_PLUGIN
+thrift_SOURCES += $(compiler_core)
+else
 
-libparse_a_SOURCES = src/thrifty.yy \
-                     src/thriftl.ll
+lib_LTLIBRARIES = libthriftc.la
+
+thrift_CPPFLAGS += -DTHRIFT_ENABLE_PLUGIN=1
+thrift_LDADD += libthriftc.la
+
+nodist_libthriftc_la_SOURCES = $(plugin_gen)
+libthriftc_la_SOURCES = $(compiler_core) \
+                        src/plugin/type_util.h \
+                        src/plugin/plugin.h \
+                        src/plugin/plugin.cc \
+                        src/plugin/plugin_output.h \
+                        src/plugin/plugin_output.cc \
+                        src/plugin/plugin.thrift \
+                        src/logging.cc
+
+
+libthriftc_la_CPPFLAGS = -I$(srcdir)/src -Isrc -I$(top_builddir)/lib/cpp/src -DTHRIFT_ENABLE_PLUGIN=1
+libthriftc_la_CXXFLAGS = -Wall -Wextra -pedantic
+libthriftc_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la
+endif
 
 WINDOWS_DIST = \
              compiler.sln \
@@ -118,9 +151,9 @@
              $(WINDOWS_DIST)
 
 clean-local:
-	$(RM) thriftl.cc thrifty.cc thrifty.h thrifty.hh version.h windows/version.h
+	$(RM) version.h windows/version.h $(plugin_gen)
 
-src/main.cc: version.h
+src/main.cc: src/version.h
 
 style-local:
 	$(CPPSTYLE_CMD)
diff --git a/compiler/cpp/compiler.vcxproj b/compiler/cpp/compiler.vcxproj
index a2548d4..3dbe42c 100644
--- a/compiler/cpp/compiler.vcxproj
+++ b/compiler/cpp/compiler.vcxproj
@@ -20,6 +20,7 @@
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\audit\t_audit.h" />
+    <ClInclude Include="src\common.h" />
     <ClInclude Include="src\generate\t_generator.h" />
     <ClInclude Include="src\generate\t_generator_registry.h" />
     <ClInclude Include="src\generate\t_oop_generator.h" />
@@ -50,7 +51,8 @@
     <ClInclude Include="src\windows\version.h" />
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="src\audit\t_audit.cpp" />
+    <ClCompile Include="src\audit\t_audit.cpp"/>
+    <ClCompile Include="src\common.cc" />
     <ClCompile Include="src\generate\t_as3_generator.cc" />
     <ClCompile Include="src\generate\t_cocoa_generator.cc" />
     <ClCompile Include="src\generate\t_cpp_generator.cc" />
@@ -245,4 +247,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/compiler/cpp/src/Makefile.am b/compiler/cpp/src/Makefile.am
new file mode 100644
index 0000000..e2fae70
--- /dev/null
+++ b/compiler/cpp/src/Makefile.am
@@ -0,0 +1,87 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+# Contains some contributions under the Thrift Software License.
+# Please see doc/old-thrift-license.txt in the Thrift distribution for
+# details.
+
+AUTOMAKE_OPTIONS = subdir-objects
+
+AM_YFLAGS = -d
+
+BUILT_SOURCES = thrifty.cc
+
+noinst_LIBRARIES = libparse.a
+
+libparse_a_CPPFLAGS = -I$(srcdir)
+libparse_a_CXXFLAGS = -Wall -Wno-sign-compare -Wno-unused
+
+libparse_a_SOURCES = thrifty.yy \
+                     thriftl.ll
+
+clean-local:
+	$(RM) thriftl.cc thrifty.cc thrifty.h thrifty.hh
+
+if WITH_PLUGIN
+noinst_PROGRAMS = thrift-bootstrap
+
+thrift_bootstrap_SOURCES = \
+                 common.h \
+                 common.cc \
+                 audit/t_audit.h \
+                 audit/t_audit.cpp \
+                 generate/t_generator.cc \
+                 generate/t_generator_registry.h \
+                 globals.h \
+                 platform.h \
+                 logging.h \
+                 parse/t_doc.h \
+                 parse/t_type.h \
+                 parse/t_base_type.h \
+                 parse/t_enum.h \
+                 parse/t_enum_value.h \
+                 parse/t_typedef.h \
+                 parse/t_typedef.cc \
+                 parse/t_container.h \
+                 parse/t_list.h \
+                 parse/t_set.h \
+                 parse/t_map.h \
+                 parse/t_struct.h \
+                 parse/t_field.h \
+                 parse/t_service.h \
+                 parse/t_function.h \
+                 parse/t_program.h \
+                 parse/t_scope.h \
+                 parse/t_const.h \
+                 parse/t_const_value.h \
+                 parse/parse.cc \
+                 generate/t_generator.h \
+                 generate/t_oop_generator.h \
+                 generate/t_html_generator.h \
+                 windows/config.h \
+                 windows/version.h \
+                 generate/t_cpp_generator.cc \
+                 main.h \
+                 main.cc
+
+main.cc: version.h
+
+thrift_bootstrap_CXXFLAGS = -Wall -Wextra -pedantic
+thrift_bootstrap_LDADD = @LEXLIB@ libparse.a
+endif
diff --git a/compiler/cpp/src/common.cc b/compiler/cpp/src/common.cc
new file mode 100644
index 0000000..6e11bb2
--- /dev/null
+++ b/compiler/cpp/src/common.cc
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "common.h"
+#include "parse/t_base_type.h"
+
+t_type* g_type_void;
+t_type* g_type_string;
+t_type* g_type_binary;
+t_type* g_type_slist;
+t_type* g_type_bool;
+t_type* g_type_i8;
+t_type* g_type_i16;
+t_type* g_type_i32;
+t_type* g_type_i64;
+t_type* g_type_double;
+
+void initGlobals() {
+  g_type_void = new t_base_type("void", t_base_type::TYPE_VOID);
+  g_type_string = new t_base_type("string", t_base_type::TYPE_STRING);
+  g_type_binary = new t_base_type("string", t_base_type::TYPE_STRING);
+  ((t_base_type*)g_type_binary)->set_binary(true);
+  g_type_slist = new t_base_type("string", t_base_type::TYPE_STRING);
+  ((t_base_type*)g_type_slist)->set_string_list(true);
+  g_type_bool = new t_base_type("bool", t_base_type::TYPE_BOOL);
+  g_type_i8 = new t_base_type("i8", t_base_type::TYPE_I8);
+  g_type_i16 = new t_base_type("i16", t_base_type::TYPE_I16);
+  g_type_i32 = new t_base_type("i32", t_base_type::TYPE_I32);
+  g_type_i64 = new t_base_type("i64", t_base_type::TYPE_I64);
+  g_type_double = new t_base_type("double", t_base_type::TYPE_DOUBLE);
+}
+
+void clearGlobals() {
+  delete g_type_void;
+  delete g_type_string;
+  delete g_type_bool;
+  delete g_type_i8;
+  delete g_type_i16;
+  delete g_type_i32;
+  delete g_type_i64;
+  delete g_type_double;
+}
+
+/**
+ * Those are not really needed for plugins but causes link errors without
+ */
+
+/**
+ * The location of the last parsed doctext comment.
+ */
+int g_doctext_lineno;
+int g_program_doctext_lineno = 0;
+PROGDOCTEXT_STATUS g_program_doctext_status = INVALID;
diff --git a/compiler/cpp/src/common.h b/compiler/cpp/src/common.h
new file mode 100644
index 0000000..ab7c423
--- /dev/null
+++ b/compiler/cpp/src/common.h
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef T_COMMON_H
+#define T_COMMON_H
+
+#include "parse/t_type.h"
+
+/**
+ * Global types for the parser to be able to reference
+ */
+
+extern t_type* g_type_void;
+extern t_type* g_type_string;
+extern t_type* g_type_binary;
+extern t_type* g_type_slist;
+extern t_type* g_type_bool;
+extern t_type* g_type_i8;
+extern t_type* g_type_i16;
+extern t_type* g_type_i32;
+extern t_type* g_type_i64;
+extern t_type* g_type_double;
+
+void initGlobals();
+void clearGlobals();
+
+#endif
diff --git a/compiler/cpp/src/generate/t_generator.cc b/compiler/cpp/src/generate/t_generator.cc
index e7760d7..7f5daf4 100644
--- a/compiler/cpp/src/generate/t_generator.cc
+++ b/compiler/cpp/src/generate/t_generator.cc
@@ -127,11 +127,12 @@
   the_map[factory->get_short_name()] = factory;
 }
 
-t_generator* t_generator_registry::get_generator(t_program* program, const string& options) {
+void t_generator::parse_options(const string& options,
+                                string& language,
+                                map<string, string>& parsed_options) {
   string::size_type colon = options.find(':');
-  string language = options.substr(0, colon);
+  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()) {
@@ -152,7 +153,12 @@
       parsed_options[key] = value;
     }
   }
+}
 
+t_generator* t_generator_registry::get_generator(t_program* program,
+                                                 const string& language,
+                                                 const map<string, string>& parsed_options,
+                                                 const std::string& options) {
   gen_map_t& the_map = get_generator_map();
   gen_map_t::iterator iter = the_map.find(language);
 
@@ -163,6 +169,13 @@
   return iter->second->get_generator(program, parsed_options, options);
 }
 
+t_generator* t_generator_registry::get_generator(t_program* program, const string& options) {
+  string language;
+  map<string, string> parsed_options;
+  t_generator::parse_options(options, language, parsed_options);
+  return get_generator(program, language, 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();
diff --git a/compiler/cpp/src/generate/t_generator.h b/compiler/cpp/src/generate/t_generator.h
index 4136ab6..590efdb 100644
--- a/compiler/cpp/src/generate/t_generator.h
+++ b/compiler/cpp/src/generate/t_generator.h
@@ -25,7 +25,7 @@
 #include <fstream>
 #include <sstream>
 #include "parse/t_program.h"
-#include "globals.h"
+#include "common.h"
 #include "t_generator_registry.h"
 #include "version.h"
 
@@ -66,6 +66,9 @@
                                   const std::string& contents,
                                   const std::string& comment_end);
 
+  static void parse_options(const std::string& options, std::string& language,
+                     std::map<std::string, std::string>& parsed_options);
+
   /**
    * check whether sub-namespace declaraction is used by generator.
    * e.g. allow
@@ -257,6 +260,7 @@
   /**
    * Get the true type behind a series of typedefs.
    */
+  static const t_type* get_true_type(const t_type* type) { return type->get_true_type(); }
   static t_type* get_true_type(t_type* type) { return type->get_true_type(); }
 
 protected:
diff --git a/compiler/cpp/src/generate/t_generator_registry.h b/compiler/cpp/src/generate/t_generator_registry.h
index a852385..1f02167 100644
--- a/compiler/cpp/src/generate/t_generator_registry.h
+++ b/compiler/cpp/src/generate/t_generator_registry.h
@@ -81,6 +81,10 @@
   static void register_generator(t_generator_factory* factory);
 
   static t_generator* get_generator(t_program* program, const std::string& options);
+  static t_generator* get_generator(t_program* program,
+                                    const std::string& laugnage,
+                                    const std::map<std::string, std::string>& parsed_options,
+                                    const std::string& options);
 
   typedef std::map<std::string, t_generator_factory*> gen_map_t;
   static gen_map_t& get_generator_map();
diff --git a/compiler/cpp/src/generate/t_oop_generator.h b/compiler/cpp/src/generate/t_oop_generator.h
index 07c9d85..e5a4698 100644
--- a/compiler/cpp/src/generate/t_oop_generator.h
+++ b/compiler/cpp/src/generate/t_oop_generator.h
@@ -23,7 +23,7 @@
 #include <string>
 #include <iostream>
 
-#include "globals.h"
+#include "common.h"
 #include "t_generator.h"
 
 #include <algorithm>
diff --git a/compiler/cpp/src/generate/t_rb_generator.cc b/compiler/cpp/src/generate/t_rb_generator.cc
index 49bf7e1..abd3320 100644
--- a/compiler/cpp/src/generate/t_rb_generator.cc
+++ b/compiler/cpp/src/generate/t_rb_generator.cc
@@ -195,14 +195,14 @@
   std::string render_require_thrift();
   std::string render_includes();
   std::string declare_field(t_field* tfield);
-  std::string type_name(t_type* ttype);
-  std::string full_type_name(t_type* ttype);
+  std::string type_name(const t_type* ttype);
+  std::string full_type_name(const t_type* ttype);
   std::string function_signature(t_function* tfunction, std::string prefix = "");
   std::string argument_list(t_struct* tstruct);
   std::string type_to_enum(t_type* ttype);
   std::string rb_namespace_to_path_prefix(std::string rb_namespace);
 
-  std::vector<std::string> ruby_modules(t_program* p) {
+  std::vector<std::string> ruby_modules(const t_program* p) {
     std::string ns = p->get_namespace("rb");
     std::vector<std::string> modules;
     if (ns.empty()) {
@@ -1101,7 +1101,7 @@
   return result;
 }
 
-string t_rb_generator::type_name(t_type* ttype) {
+string t_rb_generator::type_name(const t_type* ttype) {
   string prefix = "";
 
   string name = ttype->get_name();
@@ -1112,7 +1112,7 @@
   return prefix + name;
 }
 
-string t_rb_generator::full_type_name(t_type* ttype) {
+string t_rb_generator::full_type_name(const t_type* ttype) {
   string prefix = "::";
   vector<std::string> modules = ruby_modules(ttype->get_program());
   for (vector<std::string>::iterator m_iter = modules.begin(); m_iter != modules.end(); ++m_iter) {
diff --git a/compiler/cpp/src/globals.h b/compiler/cpp/src/globals.h
index c5c0394..961c6ef 100644
--- a/compiler/cpp/src/globals.h
+++ b/compiler/cpp/src/globals.h
@@ -62,21 +62,6 @@
 extern t_program* g_program;
 
 /**
- * Global types for the parser to be able to reference
- */
-
-extern t_type* g_type_void;
-extern t_type* g_type_string;
-extern t_type* g_type_binary;
-extern t_type* g_type_slist;
-extern t_type* g_type_bool;
-extern t_type* g_type_i8;
-extern t_type* g_type_i16;
-extern t_type* g_type_i32;
-extern t_type* g_type_i64;
-extern t_type* g_type_double;
-
-/**
  * The scope that we are currently parsing into
  */
 extern t_scope* g_scope;
diff --git a/compiler/cpp/src/logging.cc b/compiler/cpp/src/logging.cc
new file mode 100644
index 0000000..2daaaec
--- /dev/null
+++ b/compiler/cpp/src/logging.cc
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Logging functions copied from main.cc to avoid link errors for plugins
+ */
+
+#include "logging.h"
+#include "globals.h"
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+// TODO: make plugins accept log options from main compiler
+int g_debug = 0;
+int g_warn = 1;
+int g_verbose = 0;
+
+void pdebug(const char* fmt, ...) {
+  if (g_debug == 0) {
+    return;
+  }
+  va_list args;
+  // printf("[PARSE:%d] ", yylineno);
+  va_start(args, fmt);
+  vprintf(fmt, args);
+  va_end(args);
+  printf("\n");
+}
+
+void pverbose(const char* fmt, ...) {
+  if (g_verbose == 0) {
+    return;
+  }
+  va_list args;
+  va_start(args, fmt);
+  vprintf(fmt, args);
+  va_end(args);
+}
+
+void pwarning(int level, const char* fmt, ...) {
+  if (g_warn < level) {
+    return;
+  }
+  va_list args;
+  // printf("[WARNING:%s:%d] ", g_curpath.c_str(), yylineno);
+  va_start(args, fmt);
+  vprintf(fmt, args);
+  va_end(args);
+  printf("\n");
+}
+
+void failure(const char* fmt, ...) {
+  va_list args;
+  // fprintf(stderr, "[FAILURE:%s:%d] ", g_curpath.c_str(), yylineno);
+  va_start(args, fmt);
+  vfprintf(stderr, fmt, args);
+  va_end(args);
+  printf("\n");
+  exit(1);
+}
diff --git a/compiler/cpp/src/logging.h b/compiler/cpp/src/logging.h
index 3f1fce8..ebefbf2 100644
--- a/compiler/cpp/src/logging.h
+++ b/compiler/cpp/src/logging.h
@@ -20,7 +20,9 @@
 #ifndef T_LOGGING_H
 #define T_LOGGING_H
 
-#include <string>
+extern int g_debug;
+extern int g_warn;
+extern int g_verbose;
 
 /**
  * Parse debugging output, used to print helpful info
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index 89dd9f9..510d69f 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -44,6 +44,7 @@
 #endif
 
 // Careful: must include globals first for extern definitions
+#include "common.h"
 #include "globals.h"
 
 #include "platform.h"
@@ -52,6 +53,9 @@
 #include "parse/t_scope.h"
 #include "generate/t_generator.h"
 #include "audit/t_audit.h"
+#ifdef THRIFT_ENABLE_PLUGIN
+#include "plugin/plugin_output.h"
+#endif
 
 #include "version.h"
 
@@ -63,21 +67,6 @@
 t_program* g_program;
 
 /**
- * Global types
- */
-
-t_type* g_type_void;
-t_type* g_type_string;
-t_type* g_type_binary;
-t_type* g_type_slist;
-t_type* g_type_bool;
-t_type* g_type_i8;
-t_type* g_type_i16;
-t_type* g_type_i32;
-t_type* g_type_i64;
-t_type* g_type_double;
-
-/**
  * Global scope
  */
 t_scope* g_scope;
@@ -143,16 +132,9 @@
 char* g_doctext;
 
 /**
- * The location of the last parsed doctext comment.
- */
-int g_doctext_lineno;
-
-/**
  * The First doctext comment
  */
 char* g_program_doctext_candidate;
-int g_program_doctext_lineno = 0;
-PROGDOCTEXT_STATUS g_program_doctext_status = INVALID;
 
 /**
  * Whether or not negative field keys are accepted.
@@ -179,6 +161,7 @@
  */
 bool g_return_failure = false;
 bool g_audit_fatal = true;
+bool g_generator_failure = false;
 
 /**
  * Win32 doesn't have realpath, so use fallback implementation in that case,
@@ -1024,8 +1007,27 @@
       t_generator* generator = t_generator_registry::get_generator(program, *iter);
 
       if (generator == NULL) {
+#ifdef THRIFT_ENABLE_PLUGIN
+        switch (plugin_output::delegateToPlugin(program, *iter)) {
+          case plugin_output::PLUGIN_NOT_FOUND:
+            pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str());
+            g_generator_failure = true;
+            break;
+          case plugin_output::PLUGIN_FAILURE:
+            pwarning(1, "Plugin generator for \"%s\" failed.\n", iter->c_str());
+            g_generator_failure = true;
+            break;
+          case plugin_output::PLUGIN_SUCCEESS:
+            break;
+          default:
+            assert(false);
+            break;
+        }
+#else
         pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str());
-      } else {
+        g_generator_failure = true;
+#endif
+      } else if (generator) {
         pverbose("Generating \"%s\"\n", iter->c_str());
         generator->generate_program();
         delete generator;
@@ -1208,18 +1210,7 @@
   }
 
   // Initialize global types
-  g_type_void = new t_base_type("void", t_base_type::TYPE_VOID);
-  g_type_string = new t_base_type("string", t_base_type::TYPE_STRING);
-  g_type_binary = new t_base_type("string", t_base_type::TYPE_STRING);
-  ((t_base_type*)g_type_binary)->set_binary(true);
-  g_type_slist = new t_base_type("string", t_base_type::TYPE_STRING);
-  ((t_base_type*)g_type_slist)->set_string_list(true);
-  g_type_bool = new t_base_type("bool", t_base_type::TYPE_BOOL);
-  g_type_i8 = new t_base_type("i8", t_base_type::TYPE_I8);
-  g_type_i16 = new t_base_type("i16", t_base_type::TYPE_I16);
-  g_type_i32 = new t_base_type("i32", t_base_type::TYPE_I32);
-  g_type_i64 = new t_base_type("i64", t_base_type::TYPE_I64);
-  g_type_double = new t_base_type("double", t_base_type::TYPE_DOUBLE);
+  initGlobals();
 
   if (g_audit) {
     // Audit operation
@@ -1302,20 +1293,15 @@
   // 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
   // all referenced and used by this wacky parse tree up until now anyways.
-
-  delete g_type_void;
-  delete g_type_string;
-  delete g_type_bool;
-  delete g_type_i8;
-  delete g_type_i16;
-  delete g_type_i32;
-  delete g_type_i64;
-  delete g_type_double;
+  clearGlobals();
 
   // Finished
   if (g_return_failure && g_audit_fatal) {
     exit(2);
   }
+  if (g_generator_failure) {
+    exit(3);
+  }
   // Finished
   return 0;
 }
diff --git a/compiler/cpp/src/parse/parse.cc b/compiler/cpp/src/parse/parse.cc
index b22ee52..81a557b 100644
--- a/compiler/cpp/src/parse/parse.cc
+++ b/compiler/cpp/src/parse/parse.cc
@@ -29,3 +29,11 @@
   }
   return type;
 }
+
+const t_type* t_type::get_true_type() const {
+  const t_type* type = this;
+  while (type->is_typedef()) {
+    type = ((t_typedef*)type)->get_type();
+  }
+  return type;
+}
diff --git a/compiler/cpp/src/parse/t_const_value.h b/compiler/cpp/src/parse/t_const_value.h
index 7e6e3f6..15366ad 100644
--- a/compiler/cpp/src/parse/t_const_value.h
+++ b/compiler/cpp/src/parse/t_const_value.h
@@ -26,6 +26,11 @@
 #include <vector>
 #include <string>
 
+namespace plugin_output {
+template <typename From, typename To>
+void convert(From*, To&);
+}
+
 /**
  * A const value is something parsed that could be a map, set, list, struct
  * or whatever.
@@ -141,6 +146,10 @@
   t_enum* enum_;
 
   t_const_value_type valType_;
+
+  // to read enum_
+  template <typename From, typename To>
+  friend void plugin_output::convert(From*, To&);
 };
 
 #endif
diff --git a/compiler/cpp/src/parse/t_container.h b/compiler/cpp/src/parse/t_container.h
index 0d992b7..2cdcf7e 100644
--- a/compiler/cpp/src/parse/t_container.h
+++ b/compiler/cpp/src/parse/t_container.h
@@ -33,9 +33,9 @@
     has_cpp_name_ = true;
   }
 
-  bool has_cpp_name() { return has_cpp_name_; }
+  bool has_cpp_name() const { return has_cpp_name_; }
 
-  std::string get_cpp_name() { return cpp_name_; }
+  std::string get_cpp_name() const { return cpp_name_; }
 
   bool is_container() const { return true; }
 
diff --git a/compiler/cpp/src/parse/t_doc.h b/compiler/cpp/src/parse/t_doc.h
index 9d310b7..621513a 100644
--- a/compiler/cpp/src/parse/t_doc.h
+++ b/compiler/cpp/src/parse/t_doc.h
@@ -31,6 +31,7 @@
 
 public:
   t_doc() : has_doc_(false) {}
+  virtual ~t_doc() {}
 
   void set_doc(const std::string& doc) {
     doc_ = doc;
diff --git a/compiler/cpp/src/parse/t_enum.h b/compiler/cpp/src/parse/t_enum.h
index 64f4ff4..268f89f 100644
--- a/compiler/cpp/src/parse/t_enum.h
+++ b/compiler/cpp/src/parse/t_enum.h
@@ -20,9 +20,11 @@
 #ifndef T_ENUM_H
 #define T_ENUM_H
 
-#include "t_enum_value.h"
 #include <vector>
 
+#include "t_enum_value.h"
+#include "t_type.h"
+
 /**
  * An enumerated type. A list of constant objects with a name for the type.
  *
@@ -35,7 +37,7 @@
 
   void append(t_enum_value* constant) { constants_.push_back(constant); }
 
-  const std::vector<t_enum_value*>& get_constants() { return constants_; }
+  const std::vector<t_enum_value*>& get_constants() const { return constants_; }
 
   t_enum_value* get_constant_by_name(const std::string& name) {
     const std::vector<t_enum_value*>& enum_values = get_constants();
diff --git a/compiler/cpp/src/parse/t_enum_value.h b/compiler/cpp/src/parse/t_enum_value.h
index 5979f06..296029b 100644
--- a/compiler/cpp/src/parse/t_enum_value.h
+++ b/compiler/cpp/src/parse/t_enum_value.h
@@ -20,6 +20,7 @@
 #ifndef T_ENUM_VALUE_H
 #define T_ENUM_VALUE_H
 
+#include <map>
 #include <string>
 #include "t_doc.h"
 
diff --git a/compiler/cpp/src/parse/t_field.h b/compiler/cpp/src/parse/t_field.h
index eece7bb..8b459a3 100644
--- a/compiler/cpp/src/parse/t_field.h
+++ b/compiler/cpp/src/parse/t_field.h
@@ -20,10 +20,12 @@
 #ifndef T_FIELD_H
 #define T_FIELD_H
 
+#include <map>
 #include <string>
 #include <sstream>
 
 #include "t_doc.h"
+#include "t_type.h"
 
 // Forward declare for xsd_attrs
 class t_struct;
@@ -58,7 +60,9 @@
 
   ~t_field() {}
 
-  t_type* get_type() const { return type_; }
+  t_type* get_type() { return type_; }
+
+  const t_type* get_type() const { return type_; }
 
   const std::string& get_name() const { return name_; }
 
@@ -74,6 +78,8 @@
 
   t_const_value* get_value() { return value_; }
 
+  const t_const_value* get_value() const { return value_; }
+
   void set_xsd_optional(bool xsd_optional) { xsd_optional_ = xsd_optional; }
 
   bool get_xsd_optional() const { return xsd_optional_; }
diff --git a/compiler/cpp/src/parse/t_program.h b/compiler/cpp/src/parse/t_program.h
index 812106c..563e9e0 100644
--- a/compiler/cpp/src/parse/t_program.h
+++ b/compiler/cpp/src/parse/t_program.h
@@ -58,9 +58,7 @@
 class t_program : public t_doc {
 public:
   t_program(std::string path, std::string name)
-    : path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false) {
-    scope_ = new t_scope();
-  }
+    : path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false), scope_(new t_scope) {}
 
   t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false) {
     name_ = program_name(path);
@@ -250,6 +248,10 @@
 
   // Includes
 
+  void add_include(t_program* program) {
+    includes_.push_back(program);
+  }
+
   void add_include(std::string path, std::string include_site) {
     t_program* program = new t_program(path);
 
diff --git a/compiler/cpp/src/parse/t_scope.h b/compiler/cpp/src/parse/t_scope.h
index 5acb94e..565fd8f 100644
--- a/compiler/cpp/src/parse/t_scope.h
+++ b/compiler/cpp/src/parse/t_scope.h
@@ -32,6 +32,11 @@
 #include "t_map.h"
 #include "t_list.h"
 
+namespace plugin_output {
+template <typename From, typename To>
+void convert(From*, To&);
+}
+
 /**
  * This represents a variable scope used for looking up predefined types and
  * services. Typically, a scope is associated with a t_program. Scopes are not
@@ -167,6 +172,10 @@
 
   // Map of names to services
   std::map<std::string, t_service*> services_;
+
+  // to list map entries
+  template <typename From, typename To>
+    friend void plugin_output::convert(From*, To&);
 };
 
 #endif
diff --git a/compiler/cpp/src/parse/t_service.h b/compiler/cpp/src/parse/t_service.h
index 2b01f9c..6fa8398 100644
--- a/compiler/cpp/src/parse/t_service.h
+++ b/compiler/cpp/src/parse/t_service.h
@@ -51,6 +51,8 @@
 
   t_service* get_extends() { return extends_; }
 
+  const t_service* get_extends() const { return extends_; }
+
 private:
   std::vector<t_function*> functions_;
   t_service* extends_;
diff --git a/compiler/cpp/src/parse/t_struct.h b/compiler/cpp/src/parse/t_struct.h
index d19447c..1f48f91 100644
--- a/compiler/cpp/src/parse/t_struct.h
+++ b/compiler/cpp/src/parse/t_struct.h
@@ -127,7 +127,7 @@
     return true;
   }
 
-  const members_type& get_members() { return members_; }
+  const members_type& get_members() const { return members_; }
 
   const members_type& get_sorted_members() { return members_in_id_order_; }
 
@@ -147,6 +147,16 @@
     return NULL;
   }
 
+  const t_field* get_field_by_name(std::string field_name) const {
+    members_type::const_iterator m_iter;
+    for (m_iter = members_in_id_order_.begin(); m_iter != members_in_id_order_.end(); ++m_iter) {
+      if ((*m_iter)->get_name() == field_name) {
+        return *m_iter;
+      }
+    }
+    return NULL;
+  }
+
 private:
   members_type members_;
   members_type members_in_id_order_;
diff --git a/compiler/cpp/src/parse/t_type.h b/compiler/cpp/src/parse/t_type.h
index 416cc6f..bea4ee1 100644
--- a/compiler/cpp/src/parse/t_type.h
+++ b/compiler/cpp/src/parse/t_type.h
@@ -63,6 +63,7 @@
   const t_program* get_program() const { return program_; }
 
   t_type* get_true_type();
+  const t_type* get_true_type() const;
 
   // This function will break (maybe badly) unless 0 <= num <= 16.
   static char nybble_to_xdigit(int num) {
diff --git a/compiler/cpp/src/plugin/Makefile.am b/compiler/cpp/src/plugin/Makefile.am
new file mode 100644
index 0000000..7e3c82d
--- /dev/null
+++ b/compiler/cpp/src/plugin/Makefile.am
@@ -0,0 +1,47 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+# Contains some contributions under the Thrift Software License.
+# Please see doc/old-thrift-license.txt in the Thrift distribution for
+# details.
+
+AUTOMAKE_OPTIONS = subdir-objects
+
+if WITH_PLUGIN
+plugin_gen = plugin_types.h \
+             plugin_types.cpp \
+             plugin_constants.h \
+             plugin_constants.cpp
+
+BUILT_SOURCES = $(plugin_gen)
+gen.stamp: plugin.thrift $(top_builddir)/compiler/cpp/src/thrift-bootstrap
+	@$(RM) -f gen.tmp
+	@touch gen.tmp
+	$(top_builddir)/compiler/cpp/src/thrift-bootstrap -gen cpp -out . $<
+	@mv -f gen.tmp $@
+
+$(plugin_gen): gen.stamp
+	@if test -f $@; then :; else \
+	$(RM) -f gen.stamp; \
+	$(MAKE) $(AM_MAKEFLAGS) gen.stamp; \
+	fi
+
+clean-local:
+	$(RM) version.h windows/version.h $(plugin_gen)
+endif
diff --git a/compiler/cpp/src/plugin/plugin.cc b/compiler/cpp/src/plugin/plugin.cc
new file mode 100644
index 0000000..d969f50
--- /dev/null
+++ b/compiler/cpp/src/plugin/plugin.cc
@@ -0,0 +1,503 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "plugin/plugin.h"
+
+#ifdef _WIN32
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+#include <cassert>
+#include <iostream>
+
+#include <boost/bind.hpp>
+#include <boost/range/adaptor/map.hpp>
+#include <boost/range/algorithm/for_each.hpp>
+#include <boost/smart_ptr.hpp>
+
+#include "generate/t_generator.h"
+#include "plugin/type_util.h"
+#include "thrift/protocol/TBinaryProtocol.h"
+#include "thrift/transport/TBufferTransports.h"
+#include "thrift/transport/TFDTransport.h"
+
+#include "plugin/plugin_types.h"
+
+namespace apache {
+namespace thrift {
+namespace plugin {
+
+using apache::thrift::protocol::TBinaryProtocol;
+using apache::thrift::transport::TFDTransport;
+using apache::thrift::transport::TFramedTransport;
+
+#define THRIFT_CONVERT_FORWARD(from_type)                                                          \
+  template <>                                                                                      \
+  typename ToType<from_type>::type* convert_forward<from_type>(const from_type& from)
+
+#define THRIFT_CONVERT_COMPLETE_DECL(from_type)                                                    \
+  template <>                                                                                      \
+  void convert(const from_type& from, ToType<from_type>::type* to)
+
+#define THRIFT_CONVERT_UNARY_DECL(from_type)                                                       \
+  template <>                                                                                      \
+  typename ToType<from_type>::type* convert<from_type>(const from_type& from)
+
+#define THRIFT_CONVERSION_DECL(from_type)                                                          \
+  THRIFT_CONVERT_FORWARD(from_type);                                                               \
+  THRIFT_CONVERT_COMPLETE_DECL(from_type);                                                         \
+  THRIFT_CONVERT_UNARY_DECL(from_type)
+
+#define THRIFT_CONVERT_COMPLETE(from_type)                                                         \
+  THRIFT_CONVERSION_DECL(from_type) {                                                              \
+    ToType<from_type>::type* to = convert_forward(from);                                           \
+    convert(from, to);                                                                             \
+    return to;                                                                                     \
+  }                                                                                                \
+  THRIFT_CONVERT_COMPLETE_DECL(from_type)
+
+#define THRIFT_CONVERSION(from_type, ...)                                                          \
+  THRIFT_CONVERT_FORWARD(from_type) {                                                              \
+    (void)from;                                                                                    \
+    return new ToType<from_type>::type(__VA_ARGS__);                                               \
+  }                                                                                                \
+  THRIFT_CONVERT_COMPLETE(from_type)
+
+#define THRIFT_ASSIGN_DOC()                                                                        \
+  do {                                                                                             \
+    if (from.__isset.doc)                                                                          \
+      to->set_doc(from.doc);                                                                       \
+  } while (0)
+
+#define THRIFT_ASSIGN_ANNOTATIONS()                                                                \
+  THRIFT_ASSIGN_DOC();                                                                             \
+  do {                                                                                             \
+    if (from.__isset.annotations)                                                                  \
+      to->annotations_ = from.annotations;                                                         \
+  } while (0)
+
+#define THRIFT_ASSIGN_METADATA()                                                                   \
+  do {                                                                                             \
+    to->set_name(from.metadata.name);                                                              \
+    if (from.metadata.__isset.doc)                                                                 \
+      to->set_doc(from.metadata.doc);                                                              \
+    if (from.metadata.__isset.annotations)                                                         \
+      to->annotations_ = from.metadata.annotations;                                                \
+  } while (0)
+
+::t_program* g_program = 0;
+
+template <typename C, typename S>
+struct TypeCache {
+  C* operator[](const int64_t& k) {
+    typename std::map<int64_t, C*>::iterator it = cache.find(k);
+    if (it != cache.end()) {
+      return it->second;
+    } else {
+      typename std::map<int64_t, S>::const_iterator cit = source->find(k);
+      if (cit == source->end()) {
+        throw ThriftPluginError("Type not found");
+      }
+      return (cache)[k] = convert_forward(cit->second);
+    }
+  }
+
+  void compileAll() {
+    boost::for_each(*source | boost::adaptors::map_keys,
+                    boost::bind(&TypeCache::compile, this, _1));
+  }
+
+  std::map<int64_t, S> const* source;
+
+protected:
+  std::map<int64_t, C*> cache;
+
+private:
+  void compile(const int64_t& k) {
+    typename std::map<int64_t, S>::const_iterator cit = source->find(k);
+    if (cit == source->end()) {
+      throw ThriftPluginError("Type not found ");
+    }
+    convert(cit->second, (*this)[k]);
+  }
+};
+std::map<int64_t, ::t_program*> g_program_cache;
+TypeCache< ::t_type, t_type> g_type_cache;
+TypeCache< ::t_const, t_const> g_const_cache;
+TypeCache< ::t_service, t_service> g_service_cache;
+
+void set_global_cache(const TypeRegistry& from) {
+  g_type_cache.source = &from.types;
+  g_const_cache.source = &from.constants;
+  g_service_cache.source = &from.services;
+
+  g_type_cache.compileAll();
+  g_const_cache.compileAll();
+  g_service_cache.compileAll();
+}
+
+template <typename T>
+T* resolve_type(int64_t name) {
+  return reinterpret_cast<T*>(g_type_cache[name]);
+}
+
+::t_const* resolve_const(int64_t name) {
+  return g_const_cache[name];
+}
+
+::t_service* resolve_service(int64_t name) {
+  return g_service_cache[name];
+}
+
+THRIFT_CONVERT_FORWARD(t_base_type) {
+#define T_BASETYPE_CASE(type)                                                                      \
+  case t_base::TYPE_##type:                                                                        \
+    t = ::t_base_type::TYPE_##type;                                                                \
+    break
+
+  ::t_base_type::t_base t = ::t_base_type::TYPE_VOID;
+  bool is_binary = false;
+  switch (from.value) {
+    T_BASETYPE_CASE(VOID);
+    T_BASETYPE_CASE(STRING);
+    T_BASETYPE_CASE(BOOL);
+    T_BASETYPE_CASE(I8);
+    T_BASETYPE_CASE(I16);
+    T_BASETYPE_CASE(I32);
+    T_BASETYPE_CASE(I64);
+    T_BASETYPE_CASE(DOUBLE);
+  case t_base::TYPE_BINARY:
+    t = ::t_base_type::TYPE_STRING;
+    is_binary = true;
+    break;
+  }
+  ::t_base_type* to = new ::t_base_type(from.metadata.name, t);
+  to->set_binary(is_binary);
+  return to;
+#undef T_BASETYPE_CASE
+}
+THRIFT_CONVERT_COMPLETE(t_base_type) {
+  THRIFT_ASSIGN_METADATA();
+}
+
+THRIFT_CONVERT_FORWARD(t_typedef) {
+  ::t_typedef* to;
+  if (from.forward) {
+    to = new ::t_typedef(g_program_cache[from.metadata.program_id], from.symbolic, true);
+  } else {
+    to = new ::t_typedef(g_program_cache[from.metadata.program_id],
+                         resolve_type< ::t_type>(from.type), from.symbolic);
+  }
+  return to;
+}
+THRIFT_CONVERT_COMPLETE(t_typedef) {
+  THRIFT_ASSIGN_METADATA();
+}
+THRIFT_CONVERSION(t_enum_value, from.name, from.value) {
+  assert(to);
+  THRIFT_ASSIGN_ANNOTATIONS();
+}
+THRIFT_CONVERSION(t_enum, g_program_cache[from.metadata.program_id]) {
+  assert(to);
+  THRIFT_ASSIGN_METADATA();
+  boost::for_each(from.constants | boost::adaptors::transformed(convert<t_enum_value>),
+                  boost::bind(&::t_enum::append, to, _1));
+}
+THRIFT_CONVERSION(t_list, resolve_type< ::t_type>(from.elem_type)) {
+  assert(to);
+  THRIFT_ASSIGN_METADATA();
+  if (from.__isset.cpp_name)
+    to->set_cpp_name(from.cpp_name);
+}
+THRIFT_CONVERSION(t_set, resolve_type< ::t_type>(from.elem_type)) {
+  assert(to);
+  THRIFT_ASSIGN_METADATA();
+  if (from.__isset.cpp_name)
+    to->set_cpp_name(from.cpp_name);
+}
+THRIFT_CONVERSION(t_map,
+                  resolve_type< ::t_type>(from.key_type),
+                  resolve_type< ::t_type>(from.val_type)) {
+  assert(to);
+  THRIFT_ASSIGN_METADATA();
+  if (from.__isset.cpp_name)
+    to->set_cpp_name(from.cpp_name);
+}
+THRIFT_CONVERSION(t_const_value, ) {
+#define T_CONST_VALUE_CASE(type)                                                                   \
+  if (from.__isset.type##_val)                                                                     \
+  to->set_##type(from.type##_val)
+
+  assert(to);
+  if (from.__isset.map_val) {
+    to->set_map();
+    for (std::map<t_const_value, t_const_value>::const_iterator it = from.map_val.begin();
+         it != from.map_val.end(); it++) {
+      to->add_map(convert(it->first), convert(it->second));
+    }
+  } else if (from.__isset.list_val) {
+    to->set_list();
+    boost::for_each(from.list_val | boost::adaptors::transformed(&convert<t_const_value>),
+                    boost::bind(&::t_const_value::add_list, to, _1));
+  } else
+    T_CONST_VALUE_CASE(string);
+  else T_CONST_VALUE_CASE(integer);
+  else T_CONST_VALUE_CASE(double);
+  else {
+    T_CONST_VALUE_CASE(identifier);
+    if (from.__isset.enum_val)
+      to->set_enum(resolve_type< ::t_enum>(from.enum_val));
+  }
+#undef T_CONST_VALUE_CASE
+}
+THRIFT_CONVERSION(t_field, resolve_type< ::t_type>(from.type), from.name, from.key) {
+  assert(to);
+  THRIFT_ASSIGN_ANNOTATIONS();
+  to->set_reference(from.reference);
+  to->set_req(static_cast< ::t_field::e_req>(from.req));
+  if (from.__isset.value) {
+    to->set_value(convert(from.value));
+  }
+}
+THRIFT_CONVERSION(t_struct, g_program_cache[from.metadata.program_id]) {
+  assert(to);
+  THRIFT_ASSIGN_METADATA();
+  to->set_union(from.is_union);
+  to->set_xception(from.is_xception);
+  boost::for_each(from.members | boost::adaptors::transformed(convert<t_field>),
+                  boost::bind(&::t_struct::append, to, _1));
+}
+THRIFT_CONVERSION(t_const,
+                  resolve_type< ::t_type>(from.type),
+                  from.name,
+                  convert<t_const_value>(from.value)) {
+  assert(to);
+  THRIFT_ASSIGN_DOC();
+}
+
+THRIFT_CONVERSION(t_function,
+                  resolve_type< ::t_type>(from.returntype),
+                  from.name,
+                  resolve_type< ::t_struct>(from.arglist),
+                  resolve_type< ::t_struct>(from.xceptions),
+                  from.is_oneway) {
+  assert(to);
+  THRIFT_ASSIGN_DOC();
+}
+
+THRIFT_CONVERSION(t_service, g_program_cache[from.metadata.program_id]) {
+  assert(to);
+  assert(from.metadata.program_id);
+  assert(g_program_cache[from.metadata.program_id]);
+  THRIFT_ASSIGN_METADATA();
+
+  boost::for_each(from.functions | boost::adaptors::transformed(convert<t_function>),
+                  boost::bind(&::t_service::add_function, to, _1));
+
+  if (from.__isset.extends_)
+    to->set_extends(resolve_service(from.extends_));
+}
+
+THRIFT_CONVERT_FORWARD(t_type) {
+#define T_TYPE_CASE_FW_T(case, type)                                                               \
+  if (from.__isset.case##_val)                                                                     \
+  return convert_forward<type>(from.case##_val)
+#define T_TYPE_CASE_FW(case) T_TYPE_CASE_FW_T(case, t_##case)
+
+  T_TYPE_CASE_FW(base_type);
+  T_TYPE_CASE_FW(typedef);
+  T_TYPE_CASE_FW(enum);
+  T_TYPE_CASE_FW(struct);
+  T_TYPE_CASE_FW_T(xception, t_struct);
+  T_TYPE_CASE_FW(list);
+  T_TYPE_CASE_FW(set);
+  T_TYPE_CASE_FW(map);
+  T_TYPE_CASE_FW(service);
+  throw ThriftPluginError("Invalid data: Type union has no value.");
+#undef T_TYPE_CASE_FW_T
+#undef T_TYPE_CASE_FW
+}
+THRIFT_CONVERT_COMPLETE(t_type) {
+#define T_TYPE_CASE_T(case, type)                                                                  \
+  else if (from.__isset.case##_val)                                                                \
+      convert<type, ::type>(from.case##_val, reinterpret_cast< ::type*>(to))
+#define T_TYPE_CASE(case) T_TYPE_CASE_T(case, t_##case)
+
+  if (false) {
+  }
+  T_TYPE_CASE(base_type);
+  T_TYPE_CASE(typedef);
+  T_TYPE_CASE(enum);
+  T_TYPE_CASE(struct);
+  T_TYPE_CASE_T(xception, t_struct);
+  T_TYPE_CASE(list);
+  T_TYPE_CASE(set);
+  T_TYPE_CASE(map);
+  T_TYPE_CASE(service);
+  else {
+    throw ThriftPluginError("Invalid data: Type union has no value.");
+  }
+#undef T_TYPE_CASE_T
+#undef T_TYPE_CASE
+}
+
+THRIFT_CONVERSION(t_scope, ) {
+  assert(to);
+#define T_SCOPE_RESOLVE(type, name, a)                                                             \
+  for (std::vector<int64_t>::const_iterator it = from.name##s.begin(); it != from.name##s.end();   \
+       it++) {                                                                                     \
+    ::t_##type* t = resolve_##type a(*it);                                                         \
+    to->add_##name(t->get_name(), t);                                                              \
+  }
+  T_SCOPE_RESOLVE(type, type, < ::t_type>);
+  T_SCOPE_RESOLVE(const, constant, );
+  T_SCOPE_RESOLVE(service, service, );
+#undef T_SCOPE_RESOLVE
+}
+
+THRIFT_CONVERT_FORWARD(t_program) {
+  ::t_program* to = new ::t_program(from.path, from.name);
+  for (std::vector<t_program>::const_iterator it = from.includes.begin(); it != from.includes.end();
+       it++) {
+    to->add_include(convert_forward(*it));
+  }
+  g_program_cache[from.program_id] = to;
+  return to;
+}
+THRIFT_CONVERT_COMPLETE(t_program) {
+  assert(to);
+  g_program = to;
+  convert<t_scope, ::t_scope>(from.scope, to->scope());
+  THRIFT_ASSIGN_DOC();
+
+  to->set_out_path(from.out_path, from.out_path_is_absolute);
+
+  boost::for_each(from.typedefs | boost::adaptors::transformed(&resolve_type< ::t_typedef>),
+                  boost::bind(&::t_program::add_typedef, to, _1));
+  boost::for_each(from.enums | boost::adaptors::transformed(&resolve_type< ::t_enum>),
+                  boost::bind(&::t_program::add_enum, to, _1));
+  for (std::vector<int64_t>::const_iterator it = from.objects.begin(); it != from.objects.end();
+       it++) {
+    ::t_struct* t2 = resolve_type< ::t_struct>(*it);
+    if (t2->is_xception()) {
+      to->add_xception(t2);
+    } else {
+      to->add_struct(t2);
+    }
+  }
+  boost::for_each(from.consts | boost::adaptors::transformed(&resolve_const),
+                  boost::bind(&::t_program::add_const, to, _1));
+  boost::for_each(from.services | boost::adaptors::transformed(&resolve_service),
+                  boost::bind(&::t_program::add_service, to, _1));
+
+  for (std::vector<t_program>::const_iterator it = from.includes.begin(); it != from.includes.end();
+       it++) {
+    convert(*it, g_program_cache[it->program_id]);
+  }
+  std::for_each(from.c_includes.begin(), from.c_includes.end(),
+                boost::bind(&::t_program::add_c_include, to, _1));
+  std::for_each(from.cpp_includes.begin(), from.cpp_includes.end(),
+                boost::bind(&::t_program::add_cpp_include, to, _1));
+  for (std::map<std::string, std::string>::const_iterator it = from.namespaces.begin();
+       it != from.namespaces.end(); it++) {
+    to->set_namespace(it->first, it->second);
+  }
+
+  to->set_include_prefix(from.include_prefix);
+  to->set_namespace(from.namespace_);
+}
+
+int GeneratorPlugin::exec(int, char* []) {
+#ifdef _WIN32
+  _setmode(fileno(stdin), _O_BINARY);
+#endif
+  boost::shared_ptr<TFramedTransport> transport(
+      new TFramedTransport(boost::make_shared<TFDTransport>(fileno(stdin))));
+  TBinaryProtocol proto(transport);
+  GeneratorInput input;
+  try {
+    input.read(&proto);
+  } catch (std::exception& err) {
+    std::cerr << "Error while receiving plugin data: " << err.what() << std::endl;
+    return -1;
+  }
+  initGlobals();
+  ::t_program* p = g_program = convert_forward(input.program);
+  set_global_cache(input.type_registry);
+  convert(input.program, p);
+
+  int ret = generate(p, input.parsed_options);
+  clearGlobals();
+
+  return ret;
+}
+
+::t_const_value::t_const_value_type const_value_case(const t_const_value& v) {
+  if (v.__isset.map_val)
+    return ::t_const_value::CV_MAP;
+  if (v.__isset.list_val)
+    return ::t_const_value::CV_LIST;
+  if (v.__isset.string_val)
+    return ::t_const_value::CV_STRING;
+  if (v.__isset.integer_val)
+    return ::t_const_value::CV_INTEGER;
+  if (v.__isset.double_val)
+    return ::t_const_value::CV_DOUBLE;
+  if (v.__isset.identifier_val)
+    return ::t_const_value::CV_IDENTIFIER;
+  if (v.__isset.enum_val)
+    return ::t_const_value::CV_IDENTIFIER;
+  throw ThriftPluginError("Unknown const value type");
+}
+
+bool t_const_value::operator<(const t_const_value& that) const {
+  ::t_const_value::t_const_value_type t1 = const_value_case(*this);
+  ::t_const_value::t_const_value_type t2 = const_value_case(that);
+  if (t1 != t2)
+    return t1 < t2;
+  switch (t1) {
+  case ::t_const_value::CV_INTEGER:
+    return integer_val < that.integer_val;
+  case ::t_const_value::CV_DOUBLE:
+    return double_val < that.double_val;
+  case ::t_const_value::CV_STRING:
+    return string_val < that.string_val;
+  case ::t_const_value::CV_MAP:
+    if (that.map_val.empty())
+      return false;
+    else if (map_val.empty())
+      return true;
+    else
+      return map_val.begin()->first < that.map_val.begin()->first;
+  case ::t_const_value::CV_LIST:
+    if (that.list_val.empty())
+      return false;
+    else if (list_val.empty())
+      return true;
+    else
+      return list_val.front() < that.list_val.front();
+  case ::t_const_value::CV_IDENTIFIER:
+    return integer_val < that.integer_val;
+  }
+  throw ThriftPluginError("Unknown const value type");
+}
+}
+}
+}
diff --git a/compiler/cpp/src/plugin/plugin.h b/compiler/cpp/src/plugin/plugin.h
new file mode 100644
index 0000000..705cd41
--- /dev/null
+++ b/compiler/cpp/src/plugin/plugin.h
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef T_PLUGIN_PLUGIN_H
+#define T_PLUGIN_PLUGIN_H
+
+#include "thrift/Thrift.h"
+
+class t_program;
+
+namespace apache {
+namespace thrift {
+namespace plugin {
+
+struct ThriftPluginError : public apache::thrift::TException {
+  ThriftPluginError(const std::string& msg) : apache::thrift::TException(msg) {}
+};
+
+class GeneratorPlugin {
+public:
+  int exec(int argc, char* argv[]);
+  virtual int generate(::t_program*, const std::map<std::string, std::string>&) = 0;
+};
+}
+}
+}
+
+#endif
diff --git a/compiler/cpp/src/plugin/plugin.thrift b/compiler/cpp/src/plugin/plugin.thrift
new file mode 100644
index 0000000..a93873d
--- /dev/null
+++ b/compiler/cpp/src/plugin/plugin.thrift
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace as3 org.apache.thrift.plugin
+namespace cpp apache.thrift.plugin
+namespace csharp Thrift.Plugin
+namespace d thrift.plugin
+namespace delphi Thrift.Plugin
+namespace erl thrift.plugin
+namespace go thrift
+namespace haxe org.apache.thrift.plugin
+namespace hs Thrift.Plugin
+namespace java org.apache.thrift.plugin
+namespace ocaml Thrift
+namespace perl Thrift.Plugin
+namespace php thrift.plugin
+namespace py thrift.plugin
+namespace rb Thrift
+
+typedef i64 t_program_id
+typedef i64 t_type_id
+typedef i64 t_const_id
+typedef i64 t_service_id
+
+enum t_base {
+    TYPE_VOID
+    TYPE_STRING
+    TYPE_BOOL
+    TYPE_I8
+    TYPE_I16
+    TYPE_I32
+    TYPE_I64
+    TYPE_DOUBLE
+    TYPE_BINARY
+}
+
+struct TypeMetadata {
+  1: required string name
+  2: required t_program_id program_id
+  99: optional map<string, string> annotations
+  100: optional string doc
+}
+
+struct t_base_type {
+  1: required TypeMetadata metadata
+  2: required t_base value
+}
+
+struct t_list {
+  1: required TypeMetadata metadata
+  2: optional string cpp_name
+  3: required t_type_id elem_type
+}
+
+struct t_set {
+  1: required TypeMetadata metadata
+  2: optional string cpp_name
+  3: required t_type_id elem_type
+}
+
+struct t_map {
+  1: required TypeMetadata metadata
+  2: optional string cpp_name
+  3: required t_type_id key_type
+  4: required t_type_id val_type
+}
+
+struct t_typedef {
+  1: required TypeMetadata metadata
+  2: required t_type_id type
+  3: required string symbolic
+  4: required bool forward
+}
+
+struct t_enum_value {
+  1: required string name
+  2: required i32 value
+  99: optional map<string, string> annotations
+  100: optional string doc
+}
+struct t_enum {
+  1: required TypeMetadata metadata
+  2: required list<t_enum_value> constants
+}
+
+enum Requiredness {
+  T_REQUIRED = 0
+  T_OPTIONAL = 1
+  T_OPT_IN_REQ_OUT = 2
+}
+
+union t_const_value {
+  1: optional map<t_const_value, t_const_value> map_val
+  2: optional list<t_const_value> list_val
+  3: optional string string_val
+  4: optional i64 integer_val
+  5: optional double double_val
+  6: optional string identifier_val
+  7: optional t_type_id enum_val
+}
+struct t_const {
+  1: required string name
+  2: required t_type_id type
+  3: required t_const_value value
+  100: optional string doc
+}
+struct t_struct {
+  1: required TypeMetadata metadata
+  2: required list<t_field> members
+  3: required bool is_union
+  4: required bool is_xception
+}
+struct t_field {
+  1: required string name
+  2: required t_type_id type
+  3: required i32 key
+  4: required Requiredness req
+  5: optional t_const_value value
+  10: required bool reference
+  99: optional map<string, string> annotations
+  100: optional string doc
+}
+struct t_function {
+  1: required string name
+  2: required t_type_id returntype
+  3: required t_type_id arglist
+  4: required t_type_id xceptions
+  5: required bool is_oneway
+  100: optional string doc
+}
+struct t_service {
+  1: required TypeMetadata metadata
+  2: required list<t_function> functions
+  3: optional t_service_id extends_
+}
+union t_type {
+  1: optional t_base_type base_type_val
+  2: optional t_typedef typedef_val
+  3: optional t_enum enum_val
+  4: optional t_struct struct_val
+  5: optional t_struct xception_val
+  6: optional t_list list_val
+  7: optional t_set set_val
+  8: optional t_map map_val
+  9: optional t_service service_val
+}
+struct t_scope {
+  1: required list<t_type_id> types
+  2: required list<t_const_id> constants
+  3: required list<t_service_id> services
+}
+
+struct TypeRegistry {
+  1: required map<t_type_id, t_type> types
+  2: required map<t_const_id, t_const> constants
+  3: required map<t_service_id, t_service> services
+}
+
+struct t_program {
+  1: required string name
+  2: required t_program_id program_id
+  3: required string path
+  4: required string namespace_
+  5: required string out_path
+  6: required bool out_path_is_absolute
+  8: required list<t_program> includes
+  9: required string include_prefix
+  10: required t_scope scope
+
+  11: required list<t_type_id> typedefs
+  12: required list<t_type_id> enums
+  13: required list<t_const_id> consts
+  14: required list<t_type_id> objects
+  15: required list<t_service_id> services
+
+  16: required map<string, string> namespaces
+  17: required list<string> cpp_includes
+  18: required list<string> c_includes
+  100: optional string doc
+}
+
+struct GeneratorInput {
+  1: required t_program program
+  2: required TypeRegistry type_registry
+  3: required map<string, string> parsed_options
+}
diff --git a/compiler/cpp/src/plugin/plugin_output.cc b/compiler/cpp/src/plugin/plugin_output.cc
new file mode 100644
index 0000000..1ab015e
--- /dev/null
+++ b/compiler/cpp/src/plugin/plugin_output.cc
@@ -0,0 +1,410 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifdef _WIN32
+#include <cstdio>
+#include <fcntl.h>
+#include <io.h>
+#include <iostream>
+#define THRIFT_POPEN(cmd) _popen(cmd, "wb")
+#define THRIFT_PCLOSE _pclose
+#else
+#define THRIFT_POPEN(cmd) popen(cmd, "w")
+#define THRIFT_PCLOSE pclose
+#endif
+
+#include "plugin/plugin_output.h"
+
+#include <boost/range/adaptor/map.hpp>
+#include <boost/range/algorithm/copy.hpp>
+#include <boost/range/algorithm/transform.hpp>
+#include <boost/smart_ptr.hpp>
+
+#include "generate/t_generator.h"
+#include "plugin/plugin.h"
+#include "plugin/type_util.h"
+#include "thrift/protocol/TBinaryProtocol.h"
+#include "thrift/transport/TBufferTransports.h"
+#include "thrift/transport/TFDTransport.h"
+
+#include "plugin/plugin_types.h"
+
+namespace plugin_output {
+
+template <typename From>
+typename apache::thrift::plugin::ToType<From>::type convert(From* from) {
+  typename apache::thrift::plugin::ToType<From>::type to;
+  convert(from, to);
+  return to;
+}
+
+using apache::thrift::protocol::TBinaryProtocol;
+using apache::thrift::transport::TFDTransport;
+using apache::thrift::transport::TFramedTransport;
+
+using namespace apache::thrift;
+
+#define THRIFT_CONVERSION_N(from_type, to_type)                                                    \
+  template <>                                                                                      \
+  void convert<from_type, to_type>(from_type * from, to_type & to)
+#define THRIFT_CONVERSION(type) THRIFT_CONVERSION_N(::type, plugin::type)
+
+#define THRIFT_ASSIGN_N(from_name, to_name, prefix)                                                \
+  do {                                                                                             \
+    if (from)                                                                                      \
+      to.__set_##to_name(prefix(from->from_name));                                                 \
+  } while (0)
+
+#define THRIFT_ASSIGN(name) THRIFT_ASSIGN_N(get_##name(), name, )
+#define THRIFT_ASSIGN_CONVERT(type, from_name, to_name)                                            \
+  do {                                                                                             \
+    if (from && from->from_name) {                                                                 \
+      to.__set_##to_name(convert(from->from_name));                                                \
+    }                                                                                              \
+  } while (0)
+
+#define THRIFT_ASSIGN_OPT(name)                                                                    \
+  do {                                                                                             \
+    if (from->has_##name())                                                                        \
+      THRIFT_ASSIGN(name);                                                                         \
+  } while (0)
+
+#define THRIFT_ASSIGN_LIST_N(type, from_name, to_name)                                             \
+  do {                                                                                             \
+    if (from && !from->from_name.empty()) {                                                        \
+      std::transform(from->from_name.begin(),                                                      \
+                     from->from_name.end(),                                                        \
+                     std::back_inserter(to.to_name),                                               \
+                     convert< ::type>);                                                            \
+    }                                                                                              \
+  } while (0)
+
+#define THRIFT_ASSIGN_METADATA() convert(reinterpret_cast<t_type*>(from), to.metadata)
+
+// To avoid multiple instances of same type, t_type, t_const and t_service are stored in one place
+// and referenced by ID.
+template <typename T>
+struct TypeCache {
+  typedef typename plugin::ToType<T>::type to_type;
+  std::map<int64_t, to_type> cache;
+
+  template <typename T2>
+  int64_t store(T2* t) {
+    intptr_t id = reinterpret_cast<intptr_t>(t);
+    if (id) {
+      typename std::map<int64_t, to_type>::iterator it = cache.find(id);
+      if (it == cache.end()) {
+        // HACK: fake resolve for recursive type
+        cache.insert(std::make_pair(id, to_type()));
+        // overwrite with true value
+        cache[id] = convert(t);
+      }
+    }
+    return static_cast<int64_t>(id);
+  }
+
+  void clear() { cache.clear(); }
+};
+
+template <typename T>
+int64_t store_type(T* t);
+
+#define T_STORE(type)                                                                              \
+  TypeCache<t_##type> type##_cache;                                                                \
+  template <>                                                                                      \
+  plugin::t_##type##_id store_type<t_##type>(t_##type * t) {                                       \
+    return type##_cache.store<t_##type>(t);                                                        \
+  }
+T_STORE(type)
+T_STORE(const)
+T_STORE(service)
+#undef T_STORE
+
+#define THRIFT_ASSIGN_ID_N(t, from_name, to_name)                                                  \
+  do {                                                                                             \
+    if (from && from->from_name)                                                                   \
+      to.__set_##to_name(store_type<t>(from->from_name));                                          \
+  } while (0)
+
+#define THRIFT_ASSIGN_ID(name) THRIFT_ASSIGN_ID_N(t_type, get_##name(), name)
+
+#define THRIFT_ASSIGN_LIST_ID(t, name)                                                             \
+  do {                                                                                             \
+    if (from && !from->get_##name##s().empty()) {                                                  \
+      std::transform(from->get_##name##s().begin(),                                                \
+                     from->get_##name##s().end(),                                                  \
+                     std::back_inserter(to.name##s),                                               \
+                     &store_type<t>);                                                              \
+    }                                                                                              \
+  } while (0)
+
+THRIFT_CONVERSION_N(::t_type, plugin::TypeMetadata) {
+  to.program_id = reinterpret_cast<int64_t>(from->get_program());
+  THRIFT_ASSIGN_N(annotations_, annotations, );
+  if (from->has_doc()) {
+    to.__set_doc(from->get_doc());
+  }
+  THRIFT_ASSIGN(name);
+}
+
+THRIFT_CONVERSION(t_typedef) {
+  THRIFT_ASSIGN_METADATA();
+  THRIFT_ASSIGN_ID(type);
+  THRIFT_ASSIGN(symbolic);
+  THRIFT_ASSIGN_N(is_forward_typedef(), forward, );
+}
+
+THRIFT_CONVERSION(t_enum_value) {
+  THRIFT_ASSIGN_OPT(doc);
+  THRIFT_ASSIGN(name);
+  THRIFT_ASSIGN(value);
+}
+
+THRIFT_CONVERSION(t_enum) {
+  THRIFT_ASSIGN_METADATA();
+  THRIFT_ASSIGN_LIST_N(t_enum_value, get_constants(), constants);
+}
+
+THRIFT_CONVERSION(t_const_value) {
+  switch (from->get_type()) {
+  case t_const_value::CV_INTEGER:
+    THRIFT_ASSIGN_N(get_integer(), integer_val, );
+    break;
+  case t_const_value::CV_DOUBLE:
+    THRIFT_ASSIGN_N(get_double(), double_val, );
+    break;
+  case t_const_value::CV_STRING:
+    THRIFT_ASSIGN_N(get_string(), string_val, );
+    break;
+  case t_const_value::CV_IDENTIFIER:
+    THRIFT_ASSIGN_ID_N(t_type, enum_, enum_val);
+    THRIFT_ASSIGN_N(get_identifier(), identifier_val, );
+    break;
+  case t_const_value::CV_MAP:
+    to.__isset.map_val = true;
+    if (from && !from->get_map().empty()) {
+      for (std::map< ::t_const_value*, ::t_const_value*>::const_iterator it
+           = from->get_map().begin();
+           it != from->get_map().end();
+           it++) {
+        to.map_val.insert(std::make_pair(convert(it->first), convert(it->second)));
+      }
+    }
+    break;
+  case t_const_value::CV_LIST:
+    to.__isset.list_val = true;
+    THRIFT_ASSIGN_LIST_N(t_const_value, get_list(), list_val);
+    break;
+  default:
+    throw plugin::ThriftPluginError("const value has no value");
+  }
+}
+THRIFT_CONVERSION(t_const) {
+  THRIFT_ASSIGN_OPT(doc);
+  THRIFT_ASSIGN(name);
+  THRIFT_ASSIGN_ID(type);
+  THRIFT_ASSIGN_CONVERT(t_const_value, get_value(), value);
+}
+THRIFT_CONVERSION(t_field) {
+  THRIFT_ASSIGN_OPT(doc);
+  THRIFT_ASSIGN(name);
+  THRIFT_ASSIGN(key);
+  THRIFT_ASSIGN_N(get_req(), req, (plugin::Requiredness::type));
+  THRIFT_ASSIGN(reference);
+  THRIFT_ASSIGN_ID(type);
+  THRIFT_ASSIGN_CONVERT(t_const_value, get_value(), value);
+}
+THRIFT_CONVERSION(t_struct) {
+  THRIFT_ASSIGN_METADATA();
+  THRIFT_ASSIGN_LIST_N(t_field, get_members(), members);
+  THRIFT_ASSIGN_N(is_union(), is_union, );
+  THRIFT_ASSIGN_N(is_xception(), is_xception, );
+}
+THRIFT_CONVERSION(t_function) {
+  THRIFT_ASSIGN_OPT(doc);
+  THRIFT_ASSIGN(name);
+  THRIFT_ASSIGN_ID(returntype);
+  THRIFT_ASSIGN_N(is_oneway(), is_oneway, );
+  THRIFT_ASSIGN_ID(arglist);
+  THRIFT_ASSIGN_ID(xceptions);
+}
+
+THRIFT_CONVERSION(t_list) {
+  THRIFT_ASSIGN_METADATA();
+  THRIFT_ASSIGN_OPT(cpp_name);
+  THRIFT_ASSIGN_ID(elem_type);
+}
+THRIFT_CONVERSION(t_set) {
+  THRIFT_ASSIGN_METADATA();
+  THRIFT_ASSIGN_OPT(cpp_name);
+  THRIFT_ASSIGN_ID(elem_type);
+}
+THRIFT_CONVERSION(t_map) {
+  THRIFT_ASSIGN_METADATA();
+  THRIFT_ASSIGN_OPT(cpp_name);
+  THRIFT_ASSIGN_ID(key_type);
+  THRIFT_ASSIGN_ID(val_type);
+}
+
+THRIFT_CONVERSION(t_service) {
+  THRIFT_ASSIGN_METADATA();
+  THRIFT_ASSIGN_LIST_N(t_function, get_functions(), functions);
+  THRIFT_ASSIGN_ID_N(t_service, get_extends(), extends_);
+}
+
+THRIFT_CONVERSION(t_base_type) {
+  THRIFT_ASSIGN_METADATA();
+  if (from->is_binary()) {
+    to.value = plugin::t_base::TYPE_BINARY;
+  } else {
+    switch (from->get_base()) {
+#define T_BASETYPE_CASE(name)                                                                      \
+  case t_base_type::TYPE_##name:                                                                   \
+    to.value = plugin::t_base::TYPE_##name;                                                        \
+    break
+      T_BASETYPE_CASE(VOID);
+      T_BASETYPE_CASE(STRING);
+      T_BASETYPE_CASE(BOOL);
+      T_BASETYPE_CASE(I8);
+      T_BASETYPE_CASE(I16);
+      T_BASETYPE_CASE(I32);
+      T_BASETYPE_CASE(I64);
+      T_BASETYPE_CASE(DOUBLE);
+    default:
+      throw plugin::ThriftPluginError("Base type union has no value");
+      break;
+#undef T_BASETYPE_CASE
+    }
+  }
+}
+THRIFT_CONVERSION(t_type) {
+#define T_CONVERT_UNION_N(name, type)                                                              \
+  else if (from->is_##name()) {                                                                    \
+    to.__isset.name##_val = true;                                                                  \
+    convert(reinterpret_cast< ::type*>(from), to.name##_val);                                      \
+  }
+#define T_CONVERT_UNION(name) T_CONVERT_UNION_N(name, t_##name)
+  if (false) {
+  }
+  T_CONVERT_UNION(base_type)
+  T_CONVERT_UNION(typedef)
+  T_CONVERT_UNION(enum)
+  T_CONVERT_UNION(struct)
+  T_CONVERT_UNION_N(xception, t_struct)
+  T_CONVERT_UNION(list)
+  T_CONVERT_UNION(set)
+  T_CONVERT_UNION(map)
+  T_CONVERT_UNION(service)
+  else {
+    throw plugin::ThriftPluginError("Type union has no value");
+  }
+#undef T_CONVERT_UNION_N
+#undef T_CONVERT_UNION
+}
+
+THRIFT_CONVERSION(t_scope) {
+#define T_SCOPE_ASSIGN(name, type)                                                                 \
+  boost::copy(from->name##s_ | boost::adaptors::map_values                                         \
+              | boost::adaptors::transformed(&store_type<type>),                                   \
+              std::back_inserter(to.name##s))
+  T_SCOPE_ASSIGN(type, t_type);
+  T_SCOPE_ASSIGN(constant, t_const);
+  T_SCOPE_ASSIGN(service, t_service);
+#undef T_SCOPE_ASSIGN
+}
+
+void get_global_cache(plugin::TypeRegistry& reg) {
+  reg.types = type_cache.cache;
+  reg.constants = const_cache.cache;
+  reg.services = service_cache.cache;
+}
+
+void clear_global_cache() {
+  type_cache.clear();
+  const_cache.clear();
+  service_cache.clear();
+}
+
+THRIFT_CONVERSION(t_program) {
+  THRIFT_ASSIGN_CONVERT(t_scope, scope(), scope);
+  THRIFT_ASSIGN(path);
+  THRIFT_ASSIGN(out_path);
+  THRIFT_ASSIGN(name);
+  THRIFT_ASSIGN(include_prefix);
+  THRIFT_ASSIGN(cpp_includes);
+  THRIFT_ASSIGN(c_includes);
+  THRIFT_ASSIGN(namespaces);
+  THRIFT_ASSIGN_N(is_out_path_absolute(), out_path_is_absolute, );
+  THRIFT_ASSIGN_N(get_namespace(), namespace_, );
+  THRIFT_ASSIGN_LIST_ID(t_type, typedef);
+  THRIFT_ASSIGN_LIST_ID(t_type, enum);
+  THRIFT_ASSIGN_LIST_ID(t_type, object);
+  THRIFT_ASSIGN_LIST_ID(t_const, const);
+  THRIFT_ASSIGN_LIST_ID(t_service, service);
+  THRIFT_ASSIGN_LIST_N(t_program, get_includes(), includes);
+  to.program_id = reinterpret_cast<plugin::t_program_id>(from);
+}
+
+PluginDelegateResult delegateToPlugin(t_program* program, const std::string& options) {
+  std::string language;
+  std::map<std::string, std::string> parsed_options;
+  t_generator::parse_options(options, language, parsed_options);
+  std::string cmd = "thrift-gen-";
+  if (language.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-0123456789")
+      != std::string::npos) {
+    std::cerr << "Invalid language name" << std::endl;
+    return PLUGIN_FAILURE;
+  }
+  cmd.append(language);
+  FILE* fd = THRIFT_POPEN(cmd.c_str());
+  if (fd) {
+#ifdef _WIN32
+    _setmode(fileno(fd), _O_BINARY);
+#endif
+    boost::shared_ptr<TFramedTransport> transport(
+        new TFramedTransport(boost::make_shared<TFDTransport>(fileno(fd))));
+    TBinaryProtocol proto(transport);
+
+    plugin::GeneratorInput input;
+    input.__set_parsed_options(parsed_options);
+    clear_global_cache();
+    convert(program, input.program);
+    get_global_cache(input.type_registry);
+    try {
+      input.write(&proto);
+      transport->flush();
+    } catch (std::exception& err) {
+      std::cerr << "Error while sending data to plugin: " << err.what() << std::endl;
+      THRIFT_PCLOSE(fd);
+      return PLUGIN_FAILURE;
+    }
+
+    // TODO: be prepared for hang or crash of child process
+    int ret = THRIFT_PCLOSE(fd);
+    if (!ret) {
+      return PLUGIN_SUCCEESS;
+    } else {
+      std::cerr << "plugin process returned non zero exit code: " << ret << std::endl;
+      return PLUGIN_FAILURE;
+    }
+  }
+  clear_global_cache();
+  return PLUGIN_NOT_FOUND;
+}
+}
diff --git a/compiler/cpp/src/plugin/plugin_output.h b/compiler/cpp/src/plugin/plugin_output.h
new file mode 100644
index 0000000..eab2d1b
--- /dev/null
+++ b/compiler/cpp/src/plugin/plugin_output.h
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef T_PLUGIN_PLUGIN_OUTPUT_H
+#define T_PLUGIN_PLUGIN_OUTPUT_H
+
+#include <string>
+
+class t_program;
+
+namespace plugin_output {
+
+enum PluginDelegateResult {
+  PLUGIN_NOT_FOUND,
+  PLUGIN_FAILURE,
+  PLUGIN_SUCCEESS,
+};
+
+PluginDelegateResult delegateToPlugin(t_program* program, const std::string& options);
+}
+
+#endif
diff --git a/compiler/cpp/src/plugin/type_util.h b/compiler/cpp/src/plugin/type_util.h
new file mode 100644
index 0000000..508b741
--- /dev/null
+++ b/compiler/cpp/src/plugin/type_util.h
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef T_PLUGIN_TYPE_UTIL_H
+#define T_PLUGIN_TYPE_UTIL_H
+
+namespace apache {
+namespace thrift {
+namespace plugin {
+
+template <typename From>
+struct ToType {};
+
+template <typename From>
+typename ToType<From>::type* convert_forward(const From&);
+
+template <typename From, typename To>
+void convert(const From&, To*);
+
+template <typename From>
+typename ToType<From>::type* convert(const From& from);
+
+class TypeRegistry;
+void set_global_cache(const TypeRegistry&);
+}
+}
+}
+
+// conversion from raw compiler types to plugin wire type
+namespace plugin_output {
+
+template <typename From, typename To>
+void convert(From* from, To& to);
+
+template <typename From>
+typename apache::thrift::plugin::ToType<From>::type convert(From* from);
+
+void get_global_cache(apache::thrift::plugin::TypeRegistry&);
+void clear_global_cache();
+}
+
+#define THRIFT_TYPE_MAPPING(TYPE)                                                                  \
+  class TYPE;                                                                                      \
+  namespace apache {                                                                               \
+  namespace thrift {                                                                               \
+  namespace plugin {                                                                               \
+  class TYPE;                                                                                      \
+  template <>                                                                                      \
+  struct ToType< ::TYPE> {                                                                         \
+    typedef TYPE type;                                                                             \
+  };                                                                                               \
+  template <>                                                                                      \
+  struct ToType<TYPE> {                                                                            \
+    typedef ::TYPE type;                                                                           \
+  };                                                                                               \
+  }                                                                                                \
+  }                                                                                                \
+  }
+THRIFT_TYPE_MAPPING(t_base_type)
+THRIFT_TYPE_MAPPING(t_const)
+THRIFT_TYPE_MAPPING(t_const_value)
+THRIFT_TYPE_MAPPING(t_container)
+THRIFT_TYPE_MAPPING(t_doc)
+THRIFT_TYPE_MAPPING(t_enum)
+THRIFT_TYPE_MAPPING(t_enum_value)
+THRIFT_TYPE_MAPPING(t_field)
+THRIFT_TYPE_MAPPING(t_function)
+THRIFT_TYPE_MAPPING(t_list)
+THRIFT_TYPE_MAPPING(t_map)
+THRIFT_TYPE_MAPPING(t_program)
+THRIFT_TYPE_MAPPING(t_scope)
+THRIFT_TYPE_MAPPING(t_service)
+THRIFT_TYPE_MAPPING(t_set)
+THRIFT_TYPE_MAPPING(t_struct)
+THRIFT_TYPE_MAPPING(t_type)
+THRIFT_TYPE_MAPPING(t_typedef)
+#undef THRIFT_TYPE_MAPPING
+#endif
diff --git a/compiler/cpp/src/thriftl.ll b/compiler/cpp/src/thriftl.ll
index 5c3187d..f479280 100644
--- a/compiler/cpp/src/thriftl.ll
+++ b/compiler/cpp/src/thriftl.ll
@@ -56,6 +56,7 @@
 #include "windows/config.h"
 #endif
 #include "main.h"
+#include "common.h"
 #include "globals.h"
 #include "parse/t_program.h"
 
diff --git a/compiler/cpp/src/thrifty.yy b/compiler/cpp/src/thrifty.yy
index 292670d..51de58f 100644
--- a/compiler/cpp/src/thrifty.yy
+++ b/compiler/cpp/src/thrifty.yy
@@ -38,6 +38,7 @@
 #include "windows/config.h"
 #endif
 #include "main.h"
+#include "common.h"
 #include "globals.h"
 #include "parse/t_program.h"
 #include "parse/t_scope.h"
diff --git a/compiler/cpp/version.h.in b/compiler/cpp/src/version.h.in
similarity index 100%
rename from compiler/cpp/version.h.in
rename to compiler/cpp/src/version.h.in
diff --git a/compiler/cpp/test/CMakeLists.txt b/compiler/cpp/test/CMakeLists.txt
new file mode 100644
index 0000000..047e23e
--- /dev/null
+++ b/compiler/cpp/test/CMakeLists.txt
@@ -0,0 +1,77 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+if(${WITH_PLUGIN})
+    include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
+
+    # Make sure gen-cpp files can be included
+    include_directories("${CMAKE_CURRENT_BINARY_DIR}")
+
+    set(plugintest_SOURCES
+        plugin/conversion_test.cc
+    )
+    add_executable(plugintest ${plugintest_SOURCES})
+    if(WITH_SHARED_LIB AND NOT MSVC)
+        target_link_libraries(plugintest
+            thriftc
+            ${Boost_LIBRARIES}
+        )
+    else()
+        target_link_libraries(plugintest
+            thriftc_static
+            thrift_static
+            ${Boost_LIBRARIES}
+        )
+    endif()
+    add_test(NAME PluginUnitTest COMMAND plugintest)
+
+    set(thrift-gen-mycpp_SOURCES
+        ../src/generate/t_cpp_generator.cc
+        plugin/cpp_plugin.cc
+    )
+    add_executable(thrift-gen-mycpp ${thrift-gen-mycpp_SOURCES})
+    if(WITH_SHARED_LIB AND NOT MSVC)
+        target_link_libraries(thrift-gen-mycpp thriftc)
+    else()
+        target_link_libraries(thrift-gen-mycpp thriftc_static thrift_static)
+    endif()
+
+    if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+        set(BUILDTYPE "Debug")
+    else()
+        # RelWithDebInfo generates binaries in "Release" directory too
+        set(BUILDTYPE "Release")
+    endif()
+
+    set_directory_properties(PROPERTIES
+        ADDITIONAL_MAKE_CLEAN_FILES gen-cpp
+        ADDITIONAL_MAKE_CLEAN_FILES gen-mycpp)
+
+    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp)
+    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gen-mycpp)
+    add_test(NAME PluginIntegrationTest
+             COMMAND ${CMAKE_COMMAND}
+                 -DTHRIFT_COMPILER=${THRIFT_COMPILER}
+                 -DBINDIR=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
+                 -DBUILDTYPE=${BUILDTYPE}
+                 -DCURDIR=${CMAKE_CURRENT_BINARY_DIR}
+                 -DSRCDIR=${CMAKE_CURRENT_SOURCE_DIR}
+                 -P ${CMAKE_CURRENT_SOURCE_DIR}/cpp_plugin_test.cmake)
+endif()
diff --git a/compiler/cpp/test/Makefile.am b/compiler/cpp/test/Makefile.am
new file mode 100644
index 0000000..5a23202
--- /dev/null
+++ b/compiler/cpp/test/Makefile.am
@@ -0,0 +1,51 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+# Contains some contributions under the Thrift Software License.
+# Please see doc/old-thrift-license.txt in the Thrift distribution for
+# details.
+
+AUTOMAKE_OPTIONS = subdir-objects serial-tests
+
+AM_CPPFLAGS = $(BOOST_CPPFLAGS) -I$(top_srcdir)/compiler/cpp/src
+AM_LDFLAGS = $(BOOST_LDFLAGS)
+AM_CXXFLAGS = -Wall -Wextra -pedantic
+
+if WITH_PLUGIN
+check_PROGRAMS = plugintest
+
+noinst_PROGRAMS = thrift-gen-mycpp
+
+AM_CPPFLAGS += -I$(top_srcdir)/lib/cpp/src -I$(top_builddir)/lib/cpp/src
+
+plugintest_SOURCES = plugin/conversion_test.cc
+plugintest_LDADD = $(top_builddir)/compiler/cpp/libthriftc.la
+
+thrift_gen_mycpp_SOURCES = plugin/cpp_plugin.cc \
+                           plugin/t_cpp_generator.cc
+thrift_gen_mycpp_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/compiler/cpp -I$(top_srcdir)/compiler/cpp/src/generate
+thrift_gen_mycpp_LDADD = $(top_builddir)/compiler/cpp/libthriftc.la
+
+cpp_plugin_test.sh: thrift-gen-mycpp
+TESTS = $(check_PROGRAMS) cpp_plugin_test.sh
+
+clean-local:
+	$(RM) -rf gen-cpp gen-mycpp
+
+endif
diff --git a/compiler/cpp/test/cpp_plugin_test.cmake b/compiler/cpp/test/cpp_plugin_test.cmake
new file mode 100644
index 0000000..fd18281
--- /dev/null
+++ b/compiler/cpp/test/cpp_plugin_test.cmake
@@ -0,0 +1,45 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+file(MAKE_DIRECTORY ${CURDIR}/gen-cpp)
+execute_process(COMMAND ${THRIFT_COMPILER} -r -out ${CURDIR}/gen-cpp -gen cpp ${SRCDIR}/../../../test/Include.thrift)
+if(EXITCODE)
+  message(FATAL_ERROR "FAILED: \"${ARGV}\": \"${EXITCODE}\"")
+endif()
+if(WIN32)
+    set(ENV{PATH} "${BINDIR}/${BUILDTYPE};${BINDIR};$ENV{PATH}")
+else()
+    set(ENV{PATH} "${BINDIR}:$ENV{PATH}")
+endif()
+
+file(MAKE_DIRECTORY ${CURDIR}/gen-mycpp)
+execute_process(COMMAND ${THRIFT_COMPILER} -r -out ${CURDIR}/gen-mycpp -gen mycpp ${SRCDIR}/../../../test/Include.thrift RESULT_VARIABLE EXITCODE)
+if(EXITCODE)
+  message(FATAL_ERROR "FAILED: \"${EXITCODE}\"")
+endif()
+
+find_program(DIFF diff)
+if(DIFF)
+  execute_process(COMMAND ${DIFF} -urN gen-cpp gen-mycpp RESULT_VARIABLE EXITCODE)
+  if(EXITCODE)
+    message(FATAL_ERROR "FAILED: \"${EXITCODE}\"")
+  endif()
+else()
+    message(WARNING "diff executable is not available. Not validating plugin-generated code.")
+endif()
diff --git a/compiler/cpp/test/cpp_plugin_test.sh b/compiler/cpp/test/cpp_plugin_test.sh
new file mode 100755
index 0000000..ddb2e0a
--- /dev/null
+++ b/compiler/cpp/test/cpp_plugin_test.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# this file is intended to be invoked by make.
+set -e
+mkdir -p gen-cpp gen-mycpp
+PATH=.:"$PATH" ../thrift -r -out gen-cpp -gen cpp ../../../test/Include.thrift
+PATH=.:"$PATH" ../thrift -r -out gen-mycpp -gen mycpp ../../../test/Include.thrift
+diff -urN gen-cpp gen-mycpp
diff --git a/compiler/cpp/test/plugin/conversion_test.cc b/compiler/cpp/test/plugin/conversion_test.cc
new file mode 100644
index 0000000..f2d1407
--- /dev/null
+++ b/compiler/cpp/test/plugin/conversion_test.cc
@@ -0,0 +1,496 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "parse/t_program.h"
+#include "plugin/type_util.h"
+#include "plugin/plugin_types.h"
+
+#include <map>
+#include <vector>
+
+#include <boost/preprocessor.hpp>
+#include <boost/test/included/unit_test.hpp>
+#include <boost/test/parameterized_test.hpp>
+
+using namespace apache::thrift;
+using namespace boost::unit_test;
+
+namespace test_data {
+#define T_TEST_TYPES                                                                               \
+  BOOST_PP_TUPLE_TO_LIST(14,                                                                       \
+                         (program,                                                                 \
+                          base_type,                                                               \
+                          enum_value,                                                              \
+                          enum,                                                                    \
+                          const_value,                                                             \
+                          const,                                                                   \
+                          list,                                                                    \
+                          set,                                                                     \
+                          map,                                                                     \
+                          field,                                                                   \
+                          struct,                                                                  \
+                          typedef,                                                                 \
+                          function,                                                                \
+                          service))
+#define T_DELETE_TESTDATA(r, d, elem)                                                              \
+  for (std::vector<t_##elem*>::reverse_iterator it = elem##s.rbegin(); it != elem##s.rend(); it++) \
+    delete *it;
+#define T_DECL_TESTDATA(r, d, elem) static std::vector< ::t_##elem*> elem##s;
+BOOST_PP_LIST_FOR_EACH(T_DECL_TESTDATA, _, T_TEST_TYPES)
+#undef T_DECL_TESTDATA
+
+bool has_data = false;
+void cleanup() {
+  if (has_data) {
+    has_data = false;
+    BOOST_PP_LIST_FOR_EACH(T_DELETE_TESTDATA, _, T_TEST_TYPES)
+  }
+}
+
+void init_programs() {
+  programs.push_back(new t_program("prog path", "prog_name"));
+}
+
+void init_base_types() {
+  base_types.push_back(new ::t_base_type("name0", ::t_base_type::TYPE_VOID));
+  base_types.push_back(new ::t_base_type("name1", ::t_base_type::TYPE_STRING));
+  base_types.push_back(new ::t_base_type("name2", ::t_base_type::TYPE_BOOL));
+  base_types.push_back(new ::t_base_type("name3", ::t_base_type::TYPE_I8));
+  base_types.push_back(new ::t_base_type("name4", ::t_base_type::TYPE_I16));
+  base_types.push_back(new ::t_base_type("name5", ::t_base_type::TYPE_I32));
+  base_types.push_back(new ::t_base_type("name6", ::t_base_type::TYPE_I64));
+  base_types.push_back(new ::t_base_type("name7", ::t_base_type::TYPE_DOUBLE));
+}
+
+void init_const_values() {
+  const_values.push_back(new t_const_value(42));
+  const_values.push_back(new t_const_value("foo"));
+  {
+    t_const_value* x = new t_const_value;
+    x->set_double(3.1415);
+    const_values.push_back(x);
+  }
+  {
+    t_const_value* x = new t_const_value;
+    x->set_identifier("bar");
+    x->set_enum(enums[0]);
+    const_values.push_back(x);
+  }
+  {
+    t_const_value* x = new t_const_value;
+    x->set_map();
+    x->add_map(const_values[0], const_values[1]);
+    x->add_map(const_values[1], const_values[0]);
+    const_values.push_back(x);
+  }
+  {
+    t_const_value* x = new t_const_value;
+    x->set_list();
+    x->add_list(const_values[0]);
+    x->add_list(const_values[1]);
+    const_values.push_back(x);
+  }
+}
+
+void init_consts() {
+  // base_type/enum indexes for this and other tests are arbitrary
+  consts.push_back(new t_const(base_types[2], "aaa", const_values[0]));
+  consts.back()->set_doc("soem doc");
+  consts.push_back(new t_const(base_types[3], "bbb", const_values[1]));
+}
+
+void init_enum_values() {
+  enum_values.push_back(new t_enum_value("VAL1", 11));
+  enum_values.back()->set_doc("enum doc 1");
+  enum_values.back()->annotations_.insert(std::make_pair("anno1", "val1"));
+
+  enum_values.push_back(new t_enum_value("VAL2", 22));
+}
+
+void init_enums() {
+  enums.push_back(new t_enum(programs[0]));
+  enums.back()->set_doc("enum doc 1");
+  enums.back()->annotations_.insert(std::make_pair("anno1", "val1"));
+  enums.back()->set_name("fooo");
+  enums.back()->append(enum_values[0]);
+  enums.back()->append(enum_values[1]);
+}
+
+void init_lists() {
+  lists.push_back(new t_list(enums[0]));
+  lists.push_back(new t_list(base_types[5]));
+  lists.back()->set_cpp_name("list_cpp_name_1");
+}
+void init_sets() {
+  sets.push_back(new t_set(base_types[4]));
+  sets.push_back(new t_set(enums[0]));
+  sets.back()->set_cpp_name("set_cpp_name_1");
+}
+void init_maps() {
+  maps.push_back(new t_map(base_types[4], base_types[1]));
+  maps.push_back(new t_map(base_types[5], enums[0]));
+  maps.back()->set_cpp_name("map_cpp_name_1");
+}
+
+void init_typedefs() {
+  typedefs.push_back(new t_typedef(programs[0], base_types[3], "VAL1"));
+}
+void init_fields() {
+  fields.push_back(new t_field(base_types[1], "f1"));
+  fields.back()->set_reference(false);
+  fields.back()->set_req(t_field::T_OPTIONAL);
+  fields.push_back(new t_field(base_types[2], "f2", 9));
+  fields.back()->set_reference(true);
+  fields.push_back(new t_field(base_types[3], "f3", 11));
+  fields.back()->set_req(t_field::T_REQUIRED);
+  fields.back()->set_value(const_values[0]);
+}
+void init_structs() {
+  structs.push_back(new t_struct(programs[0], "struct1"));
+  structs.back()->append(fields[0]);
+  structs.back()->append(fields[1]);
+  structs.push_back(new t_struct(programs[0], "union1"));
+  structs.back()->append(fields[0]);
+  structs.back()->append(fields[1]);
+  structs.back()->set_union(true);
+  structs.push_back(new t_struct(programs[0], "xcept1"));
+  structs.back()->set_xception(true);
+}
+void init_functions() {
+  structs.push_back(new t_struct(programs[0], "errs1"));
+  t_struct* errors = structs.back();
+  structs.push_back(new t_struct(programs[0], "args1"));
+  t_struct* arglist = structs.back();
+  functions.push_back(new t_function(base_types[0], "func1", errors, arglist, false));
+  functions.push_back(new t_function(base_types[0], "func2", errors, arglist, true));
+}
+void init_services() {
+  services.push_back(new t_service(programs[0]));
+  services.back()->set_doc("srv1 doc");
+  services.back()->set_name("srv1");
+  services.back()->add_function(functions[0]);
+  services.back()->add_function(functions[1]);
+
+  services.push_back(new t_service(programs[0]));
+  services.back()->set_name("srv2");
+  services.back()->set_extends(services[0]);
+}
+
+std::vector<t_type*> types;
+void init_types() {
+#define T_COPY_TYPES(type) std::copy(type##s.begin(), type##s.end(), std::back_inserter(types))
+  T_COPY_TYPES(base_type);
+  T_COPY_TYPES(enum);
+  T_COPY_TYPES(typedef);
+  T_COPY_TYPES(struct);
+  T_COPY_TYPES(list);
+  T_COPY_TYPES(set);
+  T_COPY_TYPES(map);
+// T_COPY_TYPES(service);
+#undef T_COPY_TYPES
+}
+
+void init() {
+  if (!has_data) {
+    has_data = true;
+#define T_INIT_TESTDATA(r, d, elem) init_##elem##s();
+    BOOST_PP_LIST_FOR_EACH(T_INIT_TESTDATA, _, T_TEST_TYPES)
+    init_types();
+#undef T_INIT_TESTDATA
+  }
+}
+}
+struct GlobalFixture {
+  ~GlobalFixture() { test_data::cleanup(); }
+};
+#if (BOOST_VERSION >= 105900)
+BOOST_GLOBAL_FIXTURE(GlobalFixture);
+#else
+BOOST_GLOBAL_FIXTURE(GlobalFixture)
+#endif
+
+void migrate_global_cache() {
+  plugin::TypeRegistry reg;
+  plugin_output::get_global_cache(reg);
+  plugin::set_global_cache(reg);
+  plugin_output::clear_global_cache();
+}
+template <typename T>
+T* round_trip(T* t) {
+  typename plugin::ToType<T>::type p;
+  plugin_output::convert(t, p);
+  migrate_global_cache();
+  return plugin::convert(p);
+}
+
+void test_base_type(::t_base_type* sut) {
+  plugin::t_base_type p;
+  plugin_output::convert(sut, p);
+  boost::scoped_ptr< ::t_base_type> sut2(plugin::convert(p));
+
+#define THRIFT_CHECK(r, data, elem) BOOST_PP_EXPAND(BOOST_CHECK_EQUAL(data elem, sut2->elem));
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(7,
+                                                (is_void(),
+                                                 is_string(),
+                                                 is_bool(),
+                                                 is_string_list(),
+                                                 is_binary(),
+                                                 is_string_enum(),
+                                                 is_base_type())))
+}
+
+void test_const_value(t_const_value* sut) {
+  boost::scoped_ptr<t_const_value> sut2(round_trip(sut));
+
+  BOOST_CHECK_EQUAL(sut->get_type(), sut2->get_type());
+  switch (sut->get_type()) {
+#define T_CONST_VALUE_CASE(type, name)                                                             \
+  case t_const_value::type:                                                                        \
+    BOOST_CHECK_EQUAL(sut->get_##name(), sut2->get_##name());                                      \
+    break
+    T_CONST_VALUE_CASE(CV_INTEGER, integer);
+    T_CONST_VALUE_CASE(CV_DOUBLE, double);
+    T_CONST_VALUE_CASE(CV_STRING, string);
+    T_CONST_VALUE_CASE(CV_IDENTIFIER, identifier);
+#undef T_CONST_VALUE_CASE
+  case t_const_value::CV_MAP:
+    BOOST_CHECK_EQUAL(sut->get_map().size(), sut2->get_map().size());
+    {
+      std::map<t_const_value::t_const_value_type, t_const_value::t_const_value_type> sut_values;
+      for (std::map<t_const_value*, t_const_value*>::const_iterator it = sut->get_map().begin();
+           it != sut->get_map().end(); it++) {
+        sut_values[it->first->get_type()] = it->second->get_type();
+      }
+      std::map<t_const_value::t_const_value_type, t_const_value::t_const_value_type> sut2_values;
+      for (std::map<t_const_value*, t_const_value*>::const_iterator it = sut2->get_map().begin();
+           it != sut2->get_map().end(); it++) {
+        sut2_values[it->first->get_type()] = it->second->get_type();
+      }
+      BOOST_CHECK_EQUAL(sut_values.begin()->first, sut2_values.begin()->first);
+      BOOST_CHECK_EQUAL(sut_values.begin()->second, sut2_values.begin()->second);
+    }
+    break;
+  case t_const_value::CV_LIST:
+    BOOST_CHECK_EQUAL(sut->get_list().size(), sut2->get_list().size());
+    BOOST_CHECK_EQUAL(sut->get_list().front()->get_type(), sut2->get_list().front()->get_type());
+    break;
+  default:
+    BOOST_ASSERT(false);
+    break;
+  }
+}
+
+void test_const(t_const* sut) {
+  boost::scoped_ptr< ::t_const> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(4,
+                                                (get_type()->get_name(),
+                                                 get_name(),
+                                                 get_value()->get_type(),
+                                                 get_doc())))
+}
+
+void test_enum_value(t_enum_value* sut) {
+  boost::scoped_ptr<t_enum_value> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(3, (get_name(), get_value(), get_doc())))
+}
+
+void test_enum(t_enum* sut) {
+  boost::scoped_ptr< ::t_enum> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(6,
+                                                (get_name(),
+                                                 get_min_value()->get_value(),
+                                                 get_max_value()->get_value(),
+                                                 get_constant_by_value(11)->get_value(),
+                                                 get_constant_by_name("VAL1")->get_value(),
+                                                 get_doc())))
+}
+
+void test_list(t_list* sut) {
+  boost::scoped_ptr<t_list> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(4,
+                                                (get_elem_type()->get_name(),
+                                                 has_cpp_name(),
+                                                 get_doc(),
+                                                 get_name())))
+  if (sut->has_cpp_name())
+    BOOST_CHECK_EQUAL(sut->get_cpp_name(), sut2->get_cpp_name());
+}
+void test_set(t_set* sut) {
+  boost::scoped_ptr<t_set> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(4,
+                                                (get_elem_type()->get_name(),
+                                                 has_cpp_name(),
+                                                 get_doc(),
+                                                 get_name())))
+  if (sut->has_cpp_name())
+    BOOST_CHECK_EQUAL(sut->get_cpp_name(), sut2->get_cpp_name());
+}
+void test_map(t_map* sut) {
+  boost::scoped_ptr<t_map> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(5,
+                                                (get_key_type()->get_name(),
+                                                 get_val_type()->get_name(),
+                                                 has_cpp_name(),
+                                                 get_doc(),
+                                                 get_name())))
+  if (sut->has_cpp_name())
+    BOOST_CHECK_EQUAL(sut->get_cpp_name(), sut2->get_cpp_name());
+}
+
+void test_typedef(t_typedef* sut) {
+  boost::scoped_ptr<t_typedef> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(4,
+                                                (get_doc(),
+                                                 get_name(),
+                                                 get_symbolic(),
+                                                 is_forward_typedef())))
+}
+
+void test_type(t_type* sut) {
+  boost::scoped_ptr<t_type> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(15,
+                                                (is_void(),
+                                                 is_base_type(),
+                                                 is_string(),
+                                                 is_bool(),
+                                                 is_typedef(),
+                                                 is_enum(),
+                                                 is_struct(),
+                                                 is_xception(),
+                                                 is_container(),
+                                                 is_list(),
+                                                 is_set(),
+                                                 is_map(),
+                                                 is_service(),
+                                                 get_doc(),
+                                                 get_name())))
+}
+
+void test_field(t_field* sut) {
+  boost::scoped_ptr<t_field> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(5,
+                                                (get_req(),
+                                                 get_reference(),
+                                                 get_key(),
+                                                 get_doc(),
+                                                 get_name())))
+  if (sut->get_value()) {
+    THRIFT_CHECK(, sut->, get_value()->get_type());
+  } else {
+    BOOST_CHECK(!sut2->get_value());
+  }
+  if (sut->get_type()) {
+    THRIFT_CHECK(, sut->, get_type()->get_name());
+  } else {
+    BOOST_CHECK(!sut2->get_type());
+  }
+}
+void test_struct(t_struct* sut) {
+  boost::scoped_ptr<t_struct> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(5,
+                                                (is_union(),
+                                                 is_xception(),
+                                                 is_struct(),
+                                                 get_doc(),
+                                                 get_name())))
+}
+
+void test_function(t_function* sut) {
+  boost::scoped_ptr<t_function> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(
+      THRIFT_CHECK,
+      sut->,
+      BOOST_PP_TUPLE_TO_LIST(4, (get_doc(), get_name(), get_returntype()->get_name(), is_oneway())))
+}
+void test_service(t_service* sut) {
+  boost::scoped_ptr<t_service> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK,
+                         sut->,
+                         BOOST_PP_TUPLE_TO_LIST(3, (get_doc(), get_name(), get_functions().size())))
+  if (sut->get_extends()) {
+    THRIFT_CHECK(, sut->, get_extends()->get_name());
+  } else {
+    BOOST_CHECK(!sut2->get_extends());
+  }
+}
+
+void test_program(t_program* sut) {
+  boost::scoped_ptr<t_program> sut2(round_trip(sut));
+
+  BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, sut->, BOOST_PP_TUPLE_TO_LIST(2, (get_doc(), get_name())))
+}
+boost::unit_test::test_suite* do_init_unit_test_suite() {
+  test_data::init();
+  test_suite* ts = BOOST_TEST_SUITE("PluginConversionTest");
+
+#define T_TEST_CASE(r, d, type)                                                                    \
+  ts->add(BOOST_PARAM_TEST_CASE(test_##type, test_data::type##s.begin(), test_data::type##s.end()));
+  BOOST_PP_LIST_FOR_EACH(T_TEST_CASE, _, T_TEST_TYPES)
+  T_TEST_CASE(_, _, type)
+#undef T_TEST_CASE
+  return ts;
+}
+
+#ifdef BOOST_TEST_DYN_LINK
+bool init_unit_test_suite() {
+  framework::master_test_suite().add(do_init_unit_test_suite());
+  return true;
+}
+int main(int argc, char* argv[]) {
+  return ::boost::unit_test::unit_test_main(&init_unit_test_suite, argc, argv);
+}
+#else
+boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+  return do_init_unit_test_suite();
+}
+#endif
diff --git a/compiler/cpp/test/plugin/cpp_plugin.cc b/compiler/cpp/test/plugin/cpp_plugin.cc
new file mode 100644
index 0000000..43a62d4
--- /dev/null
+++ b/compiler/cpp/test/plugin/cpp_plugin.cc
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "plugin/plugin.h"
+#include "generate/t_generator.h"
+
+namespace apache {
+namespace thrift {
+namespace plugin {
+
+class MyCppGenerator : public GeneratorPlugin {
+  virtual int generate(::t_program* program,
+                       const std::map<std::string, std::string>& parsed_options) {
+    t_generator* gen = t_generator_registry::get_generator(program, "cpp", parsed_options, "");
+    gen->generate_program();
+    delete gen;
+    return 0;
+  }
+};
+}
+}
+}
+
+int main(int argc, char* argv[]) {
+  apache::thrift::plugin::MyCppGenerator p;
+  return p.exec(argc, argv);
+}