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);
+}