From e8540637aaaf017f9e08e1e0d68663f814538862 Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Tue, 30 May 2006 09:24:40 +0000 Subject: [PATCH] Rev 2 of Thrift, the Pillar successor Summary: End-to-end communications and serialization in C++ is working Reviewed By: aditya Test Plan: See the new top-level test/ folder. It vaguely resembles a unit test, though it could be more automated. Revert Plan: Revertible Notes: Still a LOT of optimization work to be done on the generated C++ code, which should be using dynamic memory in a number of places. Next major task is writing the PHP/Java/Python generators. git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664712 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/Makefile | 77 +-- compiler/src/generate/t_cpp_generator.cc | 716 ++++++++++++++++++----- compiler/src/generate/t_cpp_generator.h | 81 ++- compiler/src/generate/t_generator.cc | 4 + compiler/src/generate/t_generator.h | 35 +- compiler/src/main.cc | 3 + compiler/src/parse/t_base_type.h | 1 + compiler/src/parse/t_list.h | 22 +- compiler/src/parse/t_map.h | 19 + compiler/src/parse/t_program.h | 40 +- compiler/src/parse/t_set.h | 18 + compiler/src/parse/t_struct.h | 17 +- compiler/src/parse/t_type.h | 6 + compiler/src/thrift.l | 2 +- compiler/src/thrift.y | 82 ++- compiler/test/test.thrift | 27 - lib/cpp/Makefile | 28 + lib/cpp/TDispatcher.h | 22 + lib/cpp/Thrift.h | 10 + lib/cpp/client/TClient.h | 16 + lib/cpp/client/TSimpleClient.cc | 44 ++ lib/cpp/client/TSimpleClient.h | 21 + lib/cpp/protocol/TBinaryProtocol.cc | 140 +++++ lib/cpp/protocol/TBinaryProtocol.h | 42 ++ lib/cpp/protocol/TProtocol.h | 88 +++ lib/cpp/server/TServer.h | 36 ++ lib/cpp/server/TSimpleServer.cc | 60 ++ lib/cpp/server/TSimpleServer.h | 30 + lib/cpp/transport/TServerSocket.cc | 90 +++ lib/cpp/transport/TServerSocket.h | 29 + lib/cpp/transport/TServerTransport.h | 24 + lib/cpp/transport/TSocket.cc | 180 ++++++ lib/cpp/transport/TSocket.h | 39 ++ lib/cpp/transport/TTransport.h | 25 + test/ThriftTest.thrift | 57 ++ test/cpp/Makefile | 34 ++ test/cpp/TestClient.cc | 327 +++++++++++ test/cpp/TestServer.cc | 254 ++++++++ 38 files changed, 2471 insertions(+), 275 deletions(-) create mode 100644 compiler/src/parse/t_map.h create mode 100644 compiler/src/parse/t_set.h delete mode 100644 compiler/test/test.thrift create mode 100644 lib/cpp/Makefile create mode 100644 lib/cpp/TDispatcher.h create mode 100644 lib/cpp/Thrift.h create mode 100644 lib/cpp/client/TClient.h create mode 100644 lib/cpp/client/TSimpleClient.cc create mode 100644 lib/cpp/client/TSimpleClient.h create mode 100644 lib/cpp/protocol/TBinaryProtocol.cc create mode 100644 lib/cpp/protocol/TBinaryProtocol.h create mode 100644 lib/cpp/protocol/TProtocol.h create mode 100644 lib/cpp/server/TServer.h create mode 100644 lib/cpp/server/TSimpleServer.cc create mode 100644 lib/cpp/server/TSimpleServer.h create mode 100644 lib/cpp/transport/TServerSocket.cc create mode 100644 lib/cpp/transport/TServerSocket.h create mode 100644 lib/cpp/transport/TServerTransport.h create mode 100644 lib/cpp/transport/TSocket.cc create mode 100644 lib/cpp/transport/TSocket.h create mode 100644 lib/cpp/transport/TTransport.h create mode 100644 test/ThriftTest.thrift create mode 100644 test/cpp/Makefile create mode 100644 test/cpp/TestClient.cc create mode 100644 test/cpp/TestServer.cc diff --git a/compiler/Makefile b/compiler/Makefile index 8fa9e60a..62ce2752 100644 --- a/compiler/Makefile +++ b/compiler/Makefile @@ -43,14 +43,17 @@ GEN_FILES = thrift.tab.hh \ lex.yy.cc # Object files -OBJ_FILES = thrift.tab.o \ - lex.yy.o \ - ${SRC_FILES:.cc=.o} +OBJ_FILES = ${SRC_FILES:.cc=.o} + +# Generated object files +GOB_FILES = thrift.tab.o \ + lex.yy.o # Apply directory prefixes SRCS = ${addprefix $(SRC_DIR), $(SRC_FILES)} GENS = ${addprefix $(GEN_DIR), $(GEN_FILES)} OBJS = ${addprefix $(OBJ_DIR), $(OBJ_FILES)} +GOBS = ${addprefix $(OBJ_DIR), $(GOB_FILES)} # Compile with strict warnings CFL = -g -Wall -I$(SRC_DIR) -I$(OBJ_DIR) @@ -60,7 +63,7 @@ LIBS = -lfl # Scanner generation $(GEN_DIR)lex.yy.cc: $(SRC_DIR)thrift.l - $(LEX) -o$(GEN_DIR)lex.yy.cc $(SRC_DIR)thrift.l + $(LEX) -o$@ $(SRC_DIR)thrift.l # Parser generation $(GEN_DIR)thrift.tab.hh: $(GEN_DIR)thrift.tab.cc @@ -69,68 +72,36 @@ $(GEN_DIR)thrift.tab.cc: $(SRC_DIR)thrift.y $(YACC) -d -o$(GEN_DIR)thrift.tab.cc $(SRC_DIR)thrift.y # C++ compilation -$(OBJS): $(GEN_DIR)thrift.tab.cc $(GEN_DIR)lex.yy.cc obj_dir - $(CC) $(CFL) -c -o $@ ${subst $(OBJ_DIR),$(SRC_DIR),$*.cc} +$(OBJ_DIR)lex.yy.o: $(GEN_DIR)lex.yy.cc + $(CC) $(CFL) -c -o $@ $(GEN_DIR)lex.yy.cc -# Header dependency -$(SRC_DIR)main.cc: $(SRC_DIR)*.h +# C++ compilation +$(OBJ_DIR)thrift.tab.o: $(GEN_DIR)thrift.tab.cc + $(CC) $(CFL) -c -o $@ $(GEN_DIR)thrift.tab.cc + +# C++ compilation +$(OBJS): $(SRCS) + $(CC) $(CFL) -c -o $@ ${subst $(OBJ_DIR),$(SRC_DIR),$*.cc} # Main build rule -thrift: $(OBJS) - $(LD) $(CFL) -o $(BIN_DIR)thrift $(OBJS) $(LIBS) +thrift: $(OBJS) $(GOBS) + $(LD) $(CFL) -o $(BIN_DIR)thrift $(OBJS) $(GOBS) $(LIBS) # Build directory -obj_dir: +obj_dirs: $(MKDIR) -p $(OBJ_DIR)parse $(MKDIR) -p $(OBJ_DIR)generate -# This will auto-make dependency rules -depend: - makedepend -- $(CFL) -- $(SRCS) +# Install it +install: thrift + sudo install bin/thrift /usr/local/bin/thrift # Remove auto-gen'd files and binaries clean: rm -f \ $(OBJS) \ + $(GOBS) \ $(GENS) \ $(BIN_DIR)thrift.exe \ $(BIN_DIR)thrift -# DO NOT DELETE - -src/main.o: /usr/include/stdlib.h /usr/include/features.h -src/main.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h -src/main.o: /usr/include/bits/wordsize.h /usr/include/gnu/stubs-64.h -src/main.o: /usr/include/stdio.h /usr/include/bits/types.h -src/main.o: /usr/include/bits/typesizes.h /usr/include/libio.h -src/main.o: /usr/include/_G_config.h /usr/include/wchar.h -src/main.o: /usr/include/bits/wchar.h /usr/include/gconv.h -src/main.o: /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h -src/main.o: src/globals.h src/main.h src/parse/t_program.h -src/main.o: src/parse/t_base_type.h src/parse/t_type.h src/parse/t_typedef.h -src/main.o: src/parse/t_enum.h src/parse/t_constant.h src/parse/t_struct.h -src/main.o: src/parse/t_list.h src/parse/t_field.h src/parse/t_service.h -src/main.o: src/parse/t_function.h src/generate/t_cpp_generator.h -src/main.o: src/generate/t_generator.h -src/generate/t_generator.o: src/generate/t_generator.h src/parse/t_program.h -src/generate/t_generator.o: src/parse/t_base_type.h src/parse/t_type.h -src/generate/t_generator.o: src/parse/t_typedef.h src/parse/t_enum.h -src/generate/t_generator.o: src/parse/t_constant.h src/parse/t_struct.h -src/generate/t_generator.o: src/parse/t_list.h src/parse/t_field.h -src/generate/t_generator.o: src/parse/t_service.h src/parse/t_function.h -src/generate/t_cpp_generator.o: /usr/include/sys/stat.h -src/generate/t_cpp_generator.o: /usr/include/features.h -src/generate/t_cpp_generator.o: /usr/include/sys/cdefs.h -src/generate/t_cpp_generator.o: /usr/include/gnu/stubs.h -src/generate/t_cpp_generator.o: /usr/include/bits/wordsize.h -src/generate/t_cpp_generator.o: /usr/include/gnu/stubs-64.h -src/generate/t_cpp_generator.o: /usr/include/bits/types.h -src/generate/t_cpp_generator.o: /usr/include/bits/typesizes.h -src/generate/t_cpp_generator.o: /usr/include/bits/stat.h -src/generate/t_cpp_generator.o: src/generate/t_cpp_generator.h -src/generate/t_cpp_generator.o: src/generate/t_generator.h -src/generate/t_cpp_generator.o: src/parse/t_program.h src/parse/t_base_type.h -src/generate/t_cpp_generator.o: src/parse/t_type.h src/parse/t_typedef.h -src/generate/t_cpp_generator.o: src/parse/t_enum.h src/parse/t_constant.h -src/generate/t_cpp_generator.o: src/parse/t_struct.h src/parse/t_list.h -src/generate/t_cpp_generator.o: src/parse/t_field.h src/parse/t_service.h -src/generate/t_cpp_generator.o: src/parse/t_function.h src/globals.h + diff --git a/compiler/src/generate/t_cpp_generator.cc b/compiler/src/generate/t_cpp_generator.cc index b328a1ad..e47c13b2 100644 --- a/compiler/src/generate/t_cpp_generator.cc +++ b/compiler/src/generate/t_cpp_generator.cc @@ -1,4 +1,6 @@ +#include #include +#include #include "t_cpp_generator.h" #include "globals.h" using namespace std; @@ -14,7 +16,7 @@ void t_cpp_generator::init_generator(t_program* tprogram) { mkdir(T_CPP_DIR, S_IREAD | S_IWRITE | S_IEXEC); // Make output file - string f_types_name = string(T_CPP_DIR)+"/"+tprogram->get_name()+"_types.h"; + string f_types_name = string(T_CPP_DIR)+"/"+program_name_+"Types.h"; f_types_.open(f_types_name.c_str()); // Print header @@ -23,13 +25,13 @@ void t_cpp_generator::init_generator(t_program* tprogram) { // Start ifndef f_types_ << - "#ifndef thrift_" << tprogram->get_name() << "_types_h" << endl << - "#define thrift_" << tprogram->get_name() << "_types_h" << endl << + "#ifndef " << program_name_ << "_TYPES_H" << endl << + "#define " << program_name_ << "_TYPES_H" << endl << endl; // Include base types f_types_ << - "#include " << endl << + "#include \"Thrift.h\"" << endl << endl; } @@ -109,13 +111,11 @@ void t_cpp_generator::generate_struct(t_struct* tstruct) { indent_up(); - const t_list* memberlist = tstruct->get_members(); - vector members = memberlist->elems(); - vector::iterator m_iter; + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - f_types_ << - indent() << (*m_iter)->get_type()->get_name() << " " << - (*m_iter)->get_name() << ";" << endl; + indent(f_types_) << + declare_field(*m_iter) << endl; } indent_down(); @@ -135,24 +135,24 @@ void t_cpp_generator::generate_struct(t_struct* tstruct) { */ void t_cpp_generator::generate_service(t_service* tservice) { // Make output files - string f_header_name = string(T_CPP_DIR)+"/"+tservice->get_name()+".h"; + string f_header_name = string(T_CPP_DIR)+"/"+service_name_+".h"; f_header_.open(f_header_name.c_str()); - string f_service_name = string(T_CPP_DIR)+"/"+tservice->get_name()+".cc"; + string f_service_name = string(T_CPP_DIR)+"/"+service_name_+".cc"; f_service_.open(f_service_name.c_str()); // Print header file includes f_header_ << autogen_comment(); f_header_ << - "#ifndef " << tservice->get_name() << "_h" << endl << - "#define " << tservice->get_name() << "_h" << endl << + "#ifndef " << service_name_ << "_H" << endl << + "#define " << service_name_ << "_H" << endl << endl << - "#include \"TInterface.h\"" << endl << "#include \"TDispatcher.h\"" << endl << - "#include \"TProtocol.h\"" << endl << + "#include \"protocol/TProtocol.h\"" << endl << + "#include \"" << program_name_ << "Types.h\"" << endl << endl; f_service_ << autogen_comment(); f_service_ << - "#include \"" << tservice->get_name() << "_h\"" << endl << endl; + "#include \"" << service_name_ << ".h\"" << endl << endl; // Generate the three main parts of the service generate_service_interface(tservice); @@ -174,7 +174,7 @@ void t_cpp_generator::generate_service(t_service* tservice) { */ void t_cpp_generator::generate_service_interface(t_service* tservice) { f_header_ << - "class " << tservice->get_name() << " : public TInterface {" << endl << + "class " << service_name_ << "If {" << endl << " public: " << endl; indent_up(); vector functions = tservice->get_functions(); @@ -184,12 +184,84 @@ void t_cpp_generator::generate_service_interface(t_service* tservice) { indent() << "virtual " << function_signature(*f_iter) << " = 0;" << endl; } f_header_ << - indent() << "virtual ~" << tservice->get_name() << "() = 0;" << endl; + indent() << "virtual ~" << service_name_ << "If() {}" << endl; indent_down(); f_header_ << "}; " << endl << endl; } +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_cpp_generator::generate_service_client(t_service* tservice) { + // Generate the header portion + f_header_ << + "class " << service_name_ << "Client : " << + "public " << service_name_ << "If {" << endl << + " public:" << endl; + indent_up(); + f_header_ << + indent() << service_name_ << "Client" << + "(TDispatcher* dispatcher, TProtocol* protocol) : " << + "_dispatcher(dispatcher), _protocol(protocol) {}" << endl; + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_header_ << + indent() << function_signature(*f_iter) << ";" << endl; + } + indent_down(); + + f_header_ << + " protected:" << endl; + indent_up(); + f_header_ << + indent() << "TDispatcher* _dispatcher;" << endl << + indent() << "TProtocol* _protocol;" << endl; + indent_down(); + f_header_ << + "};" << endl << + endl; + + // Generate client method implementations + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string prefix = service_name_ + "Client::"; + f_service_ << + function_signature(*f_iter, prefix) << " {" << endl; + indent_up(); + + indent(f_service_) << + "std::string _argbuf = \"\";" << endl; + generate_serialize_struct("_argbuf", (*f_iter)->get_arglist()); + indent(f_service_) << + "std::string _sbuf = _protocol->writeFunction(\"" << + (*f_iter)->get_name() << "\", _argbuf);" << endl; + indent(f_service_) << + "std::string _rbuf = _dispatcher->dispatch(_sbuf);" << endl; + + if (!(*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << + "TBuf _tbuf((uint8_t*)_rbuf.data(), _rbuf.size());" << endl; + indent(f_service_) << + type_name((*f_iter)->get_returntype()) << " _result;" << endl; + t_field result_field((*f_iter)->get_returntype(), "_result", 0); + generate_deserialize_field("_tbuf", &result_field); + indent(f_service_) << + "return _result;" << endl; + } else { + indent(f_service_) << + "return;" << endl; + } + + indent_down(); + f_service_ << + "}" << endl << + endl; + } +} + /** * Generates a service server definition. * @@ -198,15 +270,23 @@ void t_cpp_generator::generate_service_interface(t_service* tservice) { void t_cpp_generator::generate_service_server(t_service* tservice) { // Generate the header portion f_header_ << - "class " << tservice->get_name() << "Server : " << - "public " << tservice->get_name() << ", " << + "class " << service_name_ << "ServerIf : " << + "public " << service_name_ << "If, " << "public TDispatcher {" << endl << " public: " << endl; indent_up(); f_header_ << - indent() << "std::string dispatch(const std::string& buff);" << endl << - indent() << "virtual ~" << tservice->get_name() << "Server();" << endl; + indent() << service_name_ << "ServerIf(TProtocol* protocol) : " << + "_protocol(protocol) {}" << endl << + indent() << "std::string dispatch(const std::string& _in);" << endl << + indent() << "virtual ~" << service_name_ << "ServerIf() {}" << endl; indent_down(); + f_header_ << + " protected:" << endl; + indent_up(); + f_header_ << + indent() << "TProtocol* _protocol;" << endl; + indent_down(); f_header_ << "};" << endl << endl; @@ -216,6 +296,54 @@ void t_cpp_generator::generate_service_server(t_service* tservice) { for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { generate_dispatch_function(tservice, *f_iter); } + + // Generate the server implementation + f_service_ << + "std::string " << service_name_ << "ServerIf::" << + "dispatch(const std::string& _in) {" << endl; + indent_up(); + + f_service_ << + indent() << "TBuf _tbuf((uint8_t*)_in.data(), _in.size());" << endl << + indent() << "std::string _fname = " << + "_protocol->readFunction(_tbuf);" << endl << + indent() << "std::string _result;" << endl; + + bool first = true; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + if (!first) { + f_service_ << " else "; + } else { + f_service_ << indent(); + first = false; + } + f_service_ << + "if (_fname.compare(\"" << (*f_iter)->get_name() <<"\") == 0) {" << endl; + indent_up(); + indent(f_service_) << + "_result = dispatch_" << (*f_iter)->get_name() << + "(_tbuf, this, _protocol);" << endl; + indent_down(); + indent(f_service_) << "}"; + } + indent(f_service_) << + " else {" << endl; + indent_up(); + f_service_ << + indent() << "fprintf(stderr, \"Unknown function: '%s'\\n\", " << + "_fname.c_str());" << endl << + indent() << "_result = \"\";" << endl; + indent_down(); + indent(f_service_) << + "}" << endl; + + indent(f_service_) << + "return _result;" << endl; + + indent_down(); + f_service_ << + "}" << endl << + endl; } /** @@ -227,22 +355,32 @@ void t_cpp_generator::generate_dispatch_function(t_service* tservice, t_function* tfunction) { f_service_ << "std::string dispatch_" << tfunction->get_name() << - "(const char *_tbuf, " << - tservice->get_name() << " *dispatcher, " << - "TProtocol *protocol) {" << endl; + "(TBuf _tbuf, " << + service_name_ << "If *_dispatcher, " << + "const TProtocol *_protocol) {" << endl; indent_up(); - // Create a field to represent this function's arguments - t_field arg_field(tfunction->get_arglist(), "_targs", 0); + // Get the struct of function call params + t_struct* arg_struct = tfunction->get_arglist(); - generate_deserialize_struct(&arg_field); + // Declare the function arguments + const vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(f_service_) << + declare_field(*f_iter, true) << endl; + } + // Deserialize the function arguments as a struct + generate_deserialize_struct("_tbuf", arg_struct); + // Generate the function call - indent(f_service_) << - type_name(tfunction->get_returntype()) << " _tresult = dispatcher." << - tfunction->get_name() << "("; - vector fields = tfunction->get_arglist()->get_members()->elems(); - vector::iterator f_iter; + f_service_ << indent(); + if (!tfunction->get_returntype()->is_void()) { + f_service_ << type_name(tfunction->get_returntype()) << " _result = "; + } + f_service_ << + "_dispatcher->" << tfunction->get_name() << "("; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { @@ -250,166 +388,412 @@ void t_cpp_generator::generate_dispatch_function(t_service* tservice, } else { f_service_ << ", "; } - f_service_ << "_targs." << (*f_iter)->get_name(); + f_service_ << (*f_iter)->get_name(); } f_service_ << ");" << endl; - // Serialize the result + // Serialize the result into _sbuf indent(f_service_) << - "std::string _tout = "";" << endl; - t_field out_field(tfunction->get_returntype(), "_tout", 0); - generate_serialize_field(&out_field); - + "std::string _sbuf = \"\";" << endl; + + t_field result_field(tfunction->get_returntype(), "_result", 0); + generate_serialize_field("_sbuf", &result_field); + + // Return the serialized buffer + indent(f_service_) << + "return _sbuf;" << endl; + indent_down(); f_service_ << "}" << endl << endl; } +/** + * Deserializes a field of any type. + */ +void t_cpp_generator::generate_deserialize_field(string src, + t_field* tfield, + string prefix) { + t_type* type = tfield->get_type(); + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + if (type->is_void()) { + return; + } + + if (type->is_struct()) { + generate_deserialize_struct(src, + (t_struct*)(tfield->get_type()), + prefix + tfield->get_name() + "."); + } else if (type->is_container()) { + generate_deserialize_container(src, + tfield->get_type(), + prefix + tfield->get_name()); + } else if (type->is_base_type() || type->is_enum()) { + + indent(f_service_) << + prefix << tfield->get_name() << " = "; + if (type->is_enum()) { + f_service_ << "(" << type_name(type) << ")"; + } + f_service_ << + "_protocol->"; + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot serialize void field in a struct: " + + src + ":" + tfield->get_name() + ":" + prefix; + break; + case t_base_type::TYPE_STRING: + f_service_ << "readString(" << src << ");"; + break; + case t_base_type::TYPE_BYTE: + f_service_ << "readByte(" << src << ");"; + break; + case t_base_type::TYPE_I32: + f_service_ << "readI32(" << src << ");"; + break; + case t_base_type::TYPE_U32: + f_service_ << "readU32(" << src << ");"; + break; + case t_base_type::TYPE_I64: + f_service_ << "readI64(" << src << ");"; + break; + case t_base_type::TYPE_U64: + f_service_ << "readU64(" << src << ");"; + break; + default: + throw "compiler error: no C++ name for base type " + tbase; + } + } else if (type->is_enum()) { + f_service_ << "readI32(" << src << ");"; + } + + f_service_ << + endl; + } else { + printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", + tfield->get_name().c_str(), type_name(type).c_str()); + } +} + /** * Generates an unserializer for a variable. This makes two key assumptions, * first that there is a const char* variable named data that points to the * buffer for deserialization, and that there is a variable protocol which * is a reference to a TProtocol serialization object. */ -void t_cpp_generator::generate_deserialize_struct(t_field* tfield) { - t_struct* tstruct = (t_struct*)(tfield->get_type()); - vector fields = tstruct->get_members()->elems(); - vector::iterator f_iter; +void t_cpp_generator::generate_deserialize_struct(string src, + t_struct* tstruct, + string prefix) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Ensure that there are some fields + if (fields.size() == 0) { + return; + } + scope_up(f_service_); + + // Read the struct fields from the protocol + string _struct = tmp("_struct"); indent(f_service_) << - declare_field(tfield) << endl; + "std::map " << _struct << + " = _protocol->readStruct(" << src << ");" << endl; indent(f_service_) << - "map fmap = protocol.readFieldMap(_tbuf);" << endl; + "std::map::iterator _field;" << endl; + + // Decleare a temp buffer for working with fields + string fbuf = tmp("_fbuf"); - // Declare the fields up front + // Iterate over the fields and extract them for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + // Check for existence of the field indent(f_service_) << - declare_field(*f_iter) << endl; - indent(f_service_) << - "if (fmap.find(" << tfield->get_key() << ") != fmap.end()) {" << endl; + "if ((_field = " << _struct << ".find(" << (*f_iter)->get_key() << + ")) != " << _struct << ".end()) {" << + endl; indent_up(); + + // Copy the field buffer into temp buffer indent(f_service_) << - "_tbuf = fmap[" << tfield->get_key() << "];" << endl; - generate_deserialize_field(*f_iter); + "TBuf& " << fbuf << " = _field->second;" << endl; + + // Deserialize from the field's buffer + generate_deserialize_field(fbuf, *f_iter, prefix); + indent_down(); indent(f_service_) << "}" << endl; } + + scope_down(f_service_); +} + +void t_cpp_generator::generate_deserialize_container(string src, + t_type* ttype, + string prefix) { + scope_up(f_service_); + + string size = tmp("_size"); + indent(f_service_) << + "uint32_t " << size << " = _protocol->readU32(" << src << ");" << endl; + + string i = tmp("_i"); + indent(f_service_) << + "uint32_t " << i << ";" << endl; + indent(f_service_) << + "for (" << + i << " = 0; " << i << " < " << size << "; ++" << i << ") {" << endl; + indent_up(); + + if (ttype->is_map()) { + generate_deserialize_map_element(src, (t_map*)ttype, prefix); + } else if (ttype->is_set()) { + generate_deserialize_set_element(src, (t_set*)ttype, prefix); + } else if (ttype->is_list()) { + generate_deserialize_list_element(src, (t_list*)ttype, prefix); + } + + indent_down(); + indent(f_service_) << + "}" << endl; + + scope_down(f_service_); } + /** - * Deserializes a field of any type. + * Generates code to deserialize a map */ -void t_cpp_generator::generate_deserialize_field(t_field* tfield) { +void t_cpp_generator::generate_deserialize_map_element(string src, + t_map* tmap, + string prefix) { + string key = tmp("_key"); + string val = tmp("_val"); + t_field fkey(tmap->get_key_type(), key, 0); + t_field fval(tmap->get_val_type(), val, 0); + + indent(f_service_) << + declare_field(&fkey) << endl; + indent(f_service_) << + declare_field(&fval) << endl; + + generate_deserialize_field(src, &fkey, ""); + generate_deserialize_field(src, &fval, ""); + + indent(f_service_) << + prefix << ".insert(std::make_pair(" << key << ", " << val << "));" << endl; +} + +void t_cpp_generator::generate_deserialize_set_element(string src, + t_set* tset, + string prefix) { + string elem = tmp("_elem"); + t_field felem(tset->get_elem_type(), elem, 0); + + indent(f_service_) << + declare_field(&felem) << endl; + + generate_deserialize_field(src, &felem, ""); + + indent(f_service_) << + prefix << ".insert(" << elem << ");" << endl; +} + +void t_cpp_generator::generate_deserialize_list_element(string src, + t_list* tlist, + string prefix) { + string elem = tmp("_elem"); + t_field felem(tlist->get_elem_type(), elem, 0); + + indent(f_service_) << + declare_field(&felem) << endl; + + generate_deserialize_field(src, &felem, ""); + + indent(f_service_) << + prefix << ".push_back(" << elem << ");" << endl; +} + + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_cpp_generator::generate_serialize_field(string dest, + t_field* tfield, + string prefix) { t_type* type = tfield->get_type(); while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); } - - if (type->is_struct()) { - generate_deserialize_struct(tfield); + + // Do nothing for void types + if (type->is_void()) { + return; } - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - return; - case t_base_type::TYPE_STRING: - return; - case t_base_type::TYPE_BYTE: - return; - case t_base_type::TYPE_I32: - return; - case t_base_type::TYPE_U32: - return; - case t_base_type::TYPE_I64: - return; - case t_base_type::TYPE_U64: - return; - default: - throw "compiler error: no C++ name for base type " + tbase; - } - } else if (type->is_enum()) { + if (type->is_struct()) { + generate_serialize_struct(dest, + (t_struct*)(tfield->get_type()), + prefix + tfield->get_name() + "."); + } else if (type->is_container()) { + generate_serialize_container(dest, + tfield->get_type(), + prefix + tfield->get_name()); + } else if (type->is_base_type() || type->is_enum()) { + indent(f_service_) << + dest << " += _protocol->"; + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot serialize void field in a struct: " + + dest + ":" + tfield->get_name() + ":" + prefix; + break; + case t_base_type::TYPE_STRING: + f_service_ << "writeString(" << prefix << tfield->get_name() << ");"; + break; + case t_base_type::TYPE_BYTE: + f_service_ << "writeByte(" << prefix << tfield->get_name() << ");"; + break; + case t_base_type::TYPE_I32: + f_service_ << "writeI32(" << prefix << tfield->get_name() << ");"; + break; + case t_base_type::TYPE_U32: + f_service_ << "writeU32(" << prefix << tfield->get_name() << ");"; + break; + case t_base_type::TYPE_I64: + f_service_ << "writeI64(" << prefix << tfield->get_name() << ");"; + break; + case t_base_type::TYPE_U64: + f_service_ << "writeU64(" << prefix << tfield->get_name() << ");"; + break; + default: + throw "compiler error: no C++ name for base type " + tbase; + } + } else if (type->is_enum()) { + f_service_ << "writeI32(" << prefix << tfield->get_name() << ");"; + } + f_service_ << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n", + tfield->get_name().c_str(), type_name(type).c_str()); } } /** - * Generates a service client definition. + * Serializes all the members of a struct. * - * @param tservice The service to generate a server for. + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields */ -void t_cpp_generator::generate_service_client(t_service* tservice) { - // Generate the header portion - f_header_ << - "class " << tservice->get_name() << "Client : " << - "public " << tservice->get_name() << " {" << endl << - " public:" << endl; +void t_cpp_generator::generate_serialize_struct(string dest, + t_struct* tstruct, + string prefix) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Ensure that there are some fields + if (fields.size() == 0) { + return; + } + + scope_up(f_service_); + string _struct = tmp("_struct"); + + indent(f_service_) << + "std::map " << _struct << ";" << endl; + + // Serialize each of the fields into the map + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + ostringstream fdest; + fdest << _struct << "[" << (*f_iter)->get_key() << "]"; + generate_serialize_field(fdest.str(), *f_iter, prefix); + } + + // Write the struct map + indent(f_service_) << + dest << " += _protocol->writeStruct(" << _struct << ");" << endl; + + scope_down(f_service_); +} + +void t_cpp_generator::generate_serialize_container(string dest, + t_type* ttype, + string prefix) { + scope_up(f_service_); + indent(f_service_) << + dest << " += _protocol->writeU32(" << prefix << ".size());" << endl; + + string iter = tmp("_iter"); + indent(f_service_) << + type_name(ttype) << "::const_iterator " << iter << ";" << endl; + indent(f_service_) << + "for (" << iter << " = " << prefix << ".begin(); " << + iter << " != " << prefix << ".end(); " << + "++" << iter << ") {" << endl; indent_up(); - f_header_ << - indent() << tservice->get_name() << - "(TDispatcher& dispatcher, TProtocol& protocol);" << endl; - vector functions = tservice->get_functions(); - vector::iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - f_header_ << - indent() << function_signature(*f_iter) << ";" << endl; + + if (ttype->is_map()) { + generate_serialize_map_element(dest, (t_map*)ttype, iter); + } else if (ttype->is_set()) { + generate_serialize_set_element(dest, (t_set*)ttype, iter); + } else if (ttype->is_list()) { + generate_serialize_list_element(dest, (t_list*)ttype, iter); } + indent_down(); + indent(f_service_) << + "}" << endl; - f_header_ << - " private:" << endl; - indent_up(); - f_header_ << - indent() << "TDispatcher& dispatcher;" << endl << - indent() << "TProtocol& protocol;" << endl; - indent_down(); - f_header_ << - "};" << endl << - endl; + scope_down(f_service_); } /** - * Deserializes a field of any type. + * Serializes the members of a map. + * */ -void t_cpp_generator::generate_serialize_field(t_field* tfield) { - t_type* type = tfield->get_type(); - while (type->is_typedef()) { - type = ((t_typedef*)type)->get_type(); - } - - if (type->is_struct()) { - - } +void t_cpp_generator::generate_serialize_map_element(string dest, + t_map* tmap, + string iter) { + t_field kfield(tmap->get_key_type(), iter + "->first", 0); + generate_serialize_field(dest, &kfield, ""); + + t_field vfield(tmap->get_val_type(), iter + "->second", 0); + generate_serialize_field(dest, &vfield, ""); +} - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - return; - case t_base_type::TYPE_STRING: - return; - case t_base_type::TYPE_BYTE: - return; - case t_base_type::TYPE_I32: - return; - case t_base_type::TYPE_U32: - return; - case t_base_type::TYPE_I64: - return; - case t_base_type::TYPE_U64: - return; - default: - throw "compiler error: no C++ name for base type " + tbase; - } - } else if (type->is_enum()) { - - } +/** + * Serializes the members of a set. + */ +void t_cpp_generator::generate_serialize_set_element(string dest, + t_set* tset, + string iter) { + t_field efield(tset->get_elem_type(), "(*" + iter + ")", 0); + generate_serialize_field(dest, &efield, ""); } +/** + * Serializes the members of a list. + */ +void t_cpp_generator::generate_serialize_list_element(string dest, + t_list* tlist, + string iter) { + t_field efield(tlist->get_elem_type(), "(*" + iter + ")", 0); + generate_serialize_field(dest, &efield, ""); +} /** * Generates a comment about this code being autogenerated. @@ -436,6 +820,17 @@ string t_cpp_generator::autogen_comment() { string t_cpp_generator::type_name(t_type* ttype) { if (ttype->is_base_type()) { return base_type_name(((t_base_type*)ttype)->get_base()); + } else if (ttype->is_map()) { + t_map* tmap = (t_map*) ttype; + return "std::map<" + + type_name(tmap->get_key_type()) + ", " + + type_name(tmap->get_val_type()) + "> "; + } else if (ttype->is_set()) { + t_set* tset = (t_set*) ttype; + return "std::set<" + type_name(tset->get_elem_type()) + "> "; + } else if (ttype->is_list()) { + t_list* tlist = (t_list*) ttype; + return "std::list<" + type_name(tlist->get_elem_type()) + "> "; } else { return ttype->get_name(); } @@ -472,9 +867,38 @@ string t_cpp_generator::base_type_name(t_base_type::t_base tbase) { * * @param ttype The type */ -string t_cpp_generator::declare_field(t_field* tfield) { +string t_cpp_generator::declare_field(t_field* tfield, bool init) { // TODO(mcslee): do we ever need to initialize the field? - return type_name(tfield->get_type()) + " " + tfield->get_name() + ";"; + string result = type_name(tfield->get_type()) + " " + tfield->get_name(); + if (init) { + t_type* type = tfield->get_type(); + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + break; + case t_base_type::TYPE_STRING: + result += " = \"\""; + break; + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_U32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_U64: + result += " = 0"; + break; + default: + throw "compiler error: no C++ initializer for base type " + tbase; + } + } else if (type->is_enum()) { + result += " = (" + type_name(type) + ")0"; + } + } + return result + ";"; } /** @@ -483,20 +907,22 @@ string t_cpp_generator::declare_field(t_field* tfield) { * @param tfunction Function definition * @return String of rendered function definition */ -string t_cpp_generator::function_signature(t_function* tfunction) { +string t_cpp_generator::function_signature(t_function* tfunction, + string prefix) { + t_type* ttype = tfunction->get_returntype(); return - type_name(tfunction->get_returntype()) + " " + tfunction->get_name() + - "(" + field_list(tfunction->get_arglist()->get_members()) + ")"; + type_name(ttype) + " " + prefix + tfunction->get_name() + + "(" + argument_list(tfunction->get_arglist()) + ")"; } /** * Renders a field list */ -string t_cpp_generator::field_list(t_list* tlist) { +string t_cpp_generator::argument_list(t_struct* tstruct) { string result = ""; - vector fields = tlist->elems(); - vector::iterator f_iter; + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { diff --git a/compiler/src/generate/t_cpp_generator.h b/compiler/src/generate/t_cpp_generator.h index 87eb72a7..079a26dd 100644 --- a/compiler/src/generate/t_cpp_generator.h +++ b/compiler/src/generate/t_cpp_generator.h @@ -3,13 +3,15 @@ #include #include +#include +#include #include "t_generator.h" #define T_CPP_DIR "gen-cpp" /** - * C++ code generator + * C++ code generator. * * @author Mark Slee */ @@ -19,35 +21,98 @@ class t_cpp_generator : public t_generator { ~t_cpp_generator() {} /** Init and close methods */ + void init_generator(t_program *tprogram); void close_generator(); /** Program-level generation functions */ + void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_struct (t_struct* tstruct); void generate_service (t_service* tservice); /** Service-level generation functions */ + void generate_service_interface (t_service* tservice); - void generate_service_server (t_service* tservice); void generate_service_client (t_service* tservice); + void generate_service_server (t_service* tservice); + void generate_dispatch_function (t_service* tservice, t_function* tfunction); /** Serialization constructs */ - void generate_dispatch_function (t_service* tservice, t_function* tfunction); - void generate_deserialize_struct(t_field* tfield); - void generate_deserialize_field(t_field* tfield); + + void generate_deserialize_field (std::string src, + t_field* tfield, + std::string prefix=""); + + void generate_deserialize_struct (std::string src, + t_struct* tstruct, + std::string prefix=""); + + void generate_deserialize_container (std::string src, + t_type* ttype, + std::string prefix=""); + + void generate_deserialize_set_element (std::string src, + t_set* tset, + std::string prefix=""); + + void generate_deserialize_map_element (std::string src, + t_map* tmap, + std::string prefix=""); + + void generate_deserialize_list_element (std::string src, + t_list* tlist, + std::string prefix=""); + + void generate_serialize_field (std::string dest, + t_field* tfield, + std::string prefix=""); + + void generate_serialize_struct (std::string dest, + t_struct* tstruct, + std::string prefix=""); + + void generate_serialize_container (std::string dest, + t_type* ttype, + std::string prefix=""); + + void generate_serialize_map_element (std::string dest, + t_map* tmap, + std::string iter); + + void generate_serialize_set_element (std::string dest, + t_set* tmap, + std::string iter); + + void generate_serialize_list_element (std::string dest, + t_list* tlist, + std::string iter); + + /** Scoping */ + + void scope_up(std::ostream& out) { + indent(out) << "{" << std::endl; + indent_up(); + } + + void scope_down(std::ostream& out) { + indent_down(); + indent(out) << "}" << std::endl; + } /** Helper rendering functions */ + std::string autogen_comment(); std::string type_name(t_type* ttype); std::string base_type_name(t_base_type::t_base tbase); - std::string declare_field(t_field* tfield); - std::string function_signature(t_function* tfunction); - std::string field_list(t_list* tlist); + std::string declare_field(t_field* tfield, bool init=false); + std::string function_signature(t_function* tfunction, std::string prefix=""); + std::string argument_list(t_struct* tstruct); private: /** File streams */ + std::ofstream f_types_; std::ofstream f_header_; std::ofstream f_service_; diff --git a/compiler/src/generate/t_generator.cc b/compiler/src/generate/t_generator.cc index 0ba44ac4..d68e66da 100644 --- a/compiler/src/generate/t_generator.cc +++ b/compiler/src/generate/t_generator.cc @@ -9,6 +9,9 @@ using namespace std; * @param program The thrift program to compile into C++ source */ void t_generator::generate_program(t_program *tprogram) { + // Set program name + program_name_ = get_program_name(tprogram); + // Initialize the generator init_generator(tprogram); @@ -37,6 +40,7 @@ void t_generator::generate_program(t_program *tprogram) { vector services = tprogram->get_services(); vector::iterator sv_iter; for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + service_name_ = get_service_name(*sv_iter); generate_service(*sv_iter); } diff --git a/compiler/src/generate/t_generator.h b/compiler/src/generate/t_generator.h index 71d0318e..be0b83e3 100644 --- a/compiler/src/generate/t_generator.h +++ b/compiler/src/generate/t_generator.h @@ -3,6 +3,7 @@ #include #include +#include #include "parse/t_program.h" /** @@ -14,7 +15,7 @@ */ class t_generator { public: - t_generator() {} + t_generator() { tmp_ = 0; } virtual ~t_generator() {} /** @@ -26,16 +27,37 @@ class t_generator { protected: /** Optional methods that may be imlemented by subclasses. */ + virtual void init_generator (t_program* tprogram) {} virtual void close_generator () {} /** Pure virtual methods implemented by the generator subclasses. */ + virtual void generate_typedef (t_typedef* ttypedef) = 0; virtual void generate_enum (t_enum* tenum) = 0; virtual void generate_struct (t_struct* tstruct) = 0; virtual void generate_service (t_service* tservice) = 0; + /** Method to get the program name, may be overridden */ + + virtual std::string get_program_name(t_program* tprogram) { + return tprogram->get_name(); + } + + /** Method to get the service name, may be overridden */ + virtual std::string get_service_name(t_service* tservice) { + return tservice->get_name(); + } + + /** Creates a unique temporary variable name. */ + std::string tmp(std::string name) { + std::ostringstream out; + out << name << tmp_++; + return out.str(); + } + /** Indentation level modifiers */ + void indent_up() { ++indent_; } void indent_down() { --indent_; } @@ -49,15 +71,24 @@ class t_generator { return ind; } - /** Indentation wrapper */ + /** Indentation utility wrapper */ std::ostream& indent(std::ostream &os) { return os << indent(); } + protected: + /** Quick accessor for formatted program name */ + std::string program_name_; + + /** Quick accessor for formatted service name */ + std::string service_name_; + private: /** Indentation level */ int indent_; + /** Temporary variable counter */ + int tmp_; }; #endif diff --git a/compiler/src/main.cc b/compiler/src/main.cc index cc126f81..8d8263c1 100644 --- a/compiler/src/main.cc +++ b/compiler/src/main.cc @@ -137,6 +137,7 @@ int main(int argc, char** argv) { // Parse it g_program = new t_program(name); + if (yyparse() != 0) { failure("Parser error."); } @@ -146,6 +147,8 @@ int main(int argc, char** argv) { t_cpp_generator* cpp = new t_cpp_generator(); cpp->generate_program(g_program); delete cpp; + } catch (string s) { + printf("Error: %s\n", s.c_str()); } catch (const char* exc) { printf("Error: %s\n", exc); } diff --git a/compiler/src/parse/t_base_type.h b/compiler/src/parse/t_base_type.h index bc961227..f6cc197b 100644 --- a/compiler/src/parse/t_base_type.h +++ b/compiler/src/parse/t_base_type.h @@ -25,6 +25,7 @@ class t_base_type : public t_type { t_type(name), base_(base) {} t_base get_base() const { return base_; } + bool is_void() const { return base_ == TYPE_VOID; } bool is_base_type() const { return true; } private: diff --git a/compiler/src/parse/t_list.h b/compiler/src/parse/t_list.h index 736ac48a..65874ccc 100644 --- a/compiler/src/parse/t_list.h +++ b/compiler/src/parse/t_list.h @@ -1,27 +1,19 @@ #ifndef T_LIST_H #define T_LIST_H -#include "t_field.h" -#include +#include "t_type.h" -/** - * List of elements. - * - * @author Mark Slee - */ -class t_list { +class t_list : public t_type { public: - t_list() {} + t_list(t_type* elem_type) : elem_type_(elem_type) {} ~t_list() {} - /** Add a new field to the list */ - void append(t_field* elem) { list_.push_back(elem); } - - /** Retrieve the list contents */ - const std::vector& elems() const { return list_; } + t_type* get_elem_type() const { return elem_type_; } + bool is_list() const { return true; } private: - std::vector list_; + t_type* elem_type_; }; #endif + diff --git a/compiler/src/parse/t_map.h b/compiler/src/parse/t_map.h new file mode 100644 index 00000000..1f61843f --- /dev/null +++ b/compiler/src/parse/t_map.h @@ -0,0 +1,19 @@ +#ifndef T_MAP_H +#define T_MAP_H + +class t_map : public t_type { + public: + t_map(t_type* key_type, t_type* val_type) : + key_type_(key_type), val_type_(val_type) {} + ~t_map() {} + + t_type* get_key_type() const { return key_type_; } + t_type* get_val_type() const { return val_type_; } + bool is_map() const { return true; } + + private: + t_type* key_type_; + t_type* val_type_; +}; + +#endif diff --git a/compiler/src/parse/t_program.h b/compiler/src/parse/t_program.h index fd357997..cda24c63 100644 --- a/compiler/src/parse/t_program.h +++ b/compiler/src/parse/t_program.h @@ -1,6 +1,7 @@ #ifndef T_PROGRAM_H #define T_PROGRAM_H +#include #include #include @@ -9,6 +10,9 @@ #include "t_enum.h" #include "t_struct.h" #include "t_service.h" +#include "t_list.h" +#include "t_map.h" +#include "t_set.h" /** * Top level class representing an entire thrift program. A program consists @@ -52,12 +56,6 @@ class t_program { const std::vector& get_structs() const { return structs_; } const std::vector& get_services() const { return services_; } - // New program element addition - void add_typedef(t_typedef *td) { typedefs_.push_back(td); } - void add_enum (t_enum *te) { enums_.push_back(te); } - void add_struct (t_struct *ts) { structs_.push_back(ts); } - void add_service(t_service *ts) { services_.push_back(ts); } - // Accessors for global types t_type* get_void_type() const { return type_void; } t_type* get_string_type() const { return type_string; } @@ -67,6 +65,31 @@ class t_program { t_type* get_i64_type() const { return type_i64; } t_type* get_u64_type() const { return type_u64; } + // Custom data type lookup + void add_custom_type(std::string name, t_type* type) { + custom_types_[name] = type; + } + t_type* get_custom_type(std::string name) { + return custom_types_[name]; + } + + // New program element addition + void add_typedef(t_typedef* td) { + typedefs_.push_back(td); + add_custom_type(td->get_symbolic(), td); + } + void add_enum(t_enum* te) { + enums_.push_back(te); + add_custom_type(te->get_name(), te); + } + void add_struct(t_struct* ts) { + structs_.push_back(ts); + add_custom_type(ts->get_name(), ts); + } + void add_service(t_service* ts) { + services_.push_back(ts); + } + private: // Name std::string name_; @@ -76,7 +99,10 @@ class t_program { std::vector enums_; std::vector structs_; std::vector services_; - + + // Type map + std::map custom_types_; + // Global base types t_type* type_void; t_type* type_string; diff --git a/compiler/src/parse/t_set.h b/compiler/src/parse/t_set.h new file mode 100644 index 00000000..3d34ace7 --- /dev/null +++ b/compiler/src/parse/t_set.h @@ -0,0 +1,18 @@ +#ifndef T_SET_H +#define T_SET_H + +#include "t_type.h" + +class t_set : public t_type { + public: + t_set(t_type* elem_type) : elem_type_(elem_type) {} + ~t_set() {} + + t_type* get_elem_type() const { return elem_type_; } + bool is_set() const { return true; } + + private: + t_type* elem_type_; +}; + +#endif diff --git a/compiler/src/parse/t_struct.h b/compiler/src/parse/t_struct.h index fcc00e7b..38eb7500 100644 --- a/compiler/src/parse/t_struct.h +++ b/compiler/src/parse/t_struct.h @@ -5,19 +5,24 @@ #include #include "t_type.h" -#include "t_list.h" +#include "t_field.h" class t_struct : public t_type { public: - t_struct(std::string name, t_list* members) : - t_type(name), members_(members) {} + t_struct() {} ~t_struct() {} - t_list* get_members() { return members_; } - bool is_struct() { return true; } + /** Set the struct name */ + void set_name(const std::string& name) { name_ = name; } + + /** Add a new field to the list */ + void append(t_field* elem) { members_.push_back(elem); } + + const std::vector& get_members() { return members_; } + bool is_struct() const { return true; } private: - t_list* members_; + std::vector members_; }; #endif diff --git a/compiler/src/parse/t_type.h b/compiler/src/parse/t_type.h index 436c59ea..6703bfa6 100644 --- a/compiler/src/parse/t_type.h +++ b/compiler/src/parse/t_type.h @@ -14,10 +14,16 @@ class t_type { virtual const std::string& get_name() const { return name_; } + virtual bool is_void() const { return false; } virtual bool is_base_type() const { return false; } virtual bool is_typedef() const { return false; } virtual bool is_enum() const { return false; } virtual bool is_struct() const { return false; } + virtual bool is_list() const { return false; } + virtual bool is_set() const { return false; } + virtual bool is_map() const { return false; } + + bool is_container() const { return is_map() || is_set() || is_list(); } protected: t_type() {} diff --git a/compiler/src/thrift.l b/compiler/src/thrift.l index 5164c894..61a0ce3c 100644 --- a/compiler/src/thrift.l +++ b/compiler/src/thrift.l @@ -23,7 +23,7 @@ identifier ([a-zA-Z_][a-zA-Z_0-9]*) whitespace ([ \t\r\n]*) multicomm ("/*""/"*([^*/]|[^*]"/"|"*"[^/])*"*"*"*/") comment ("//"[^\n]*) -symbol ([\,\{\}\(\)\=]) +symbol ([\,\{\}\(\)\=<>]) %% diff --git a/compiler/src/thrift.y b/compiler/src/thrift.y index f0fa3c49..a406c8e0 100644 --- a/compiler/src/thrift.y +++ b/compiler/src/thrift.y @@ -27,7 +27,6 @@ int y_field_val = 0; t_service* tservice; t_function* tfunction; t_field* tfield; - t_list* tlist; t_constant* tconstant; } @@ -63,13 +62,17 @@ int y_field_val = 0; /** Types */ %type BaseType +%type ContainerType +%type MapType +%type SetType +%type ListType %type Typedef %type DefinitionType %type Field %type FieldType -%type FieldList +%type FieldList %type Enum %type EnumDefList @@ -130,7 +133,6 @@ Typedef: tok_typedef DefinitionType tok_identifier { pdebug("TypeDef -> tok_typedef DefinitionType tok_identifier"); - t_typedef *td = new t_typedef($2, $3); $$ = td; } @@ -165,10 +167,10 @@ EnumDefList: EnumDef: tok_identifier '=' tok_int_constant { + pdebug("EnumDef => tok_identifier = tok_int_constant"); if ($3 < 0) { printf("WARNING (%d): Negative value supplied for enum %s.\n", yylineno, $1); } - pdebug("EnumDef => tok_identifier = tok_int_constant"); $$ = new t_constant($1, $3); } | @@ -182,7 +184,8 @@ Struct: tok_struct tok_identifier '{' FieldList '}' { pdebug("Struct -> tok_struct tok_identifier { FieldList }"); - $$ = new t_struct($2, $4); + $4->set_name($2); + $$ = $4; y_field_val = 0; } @@ -210,8 +213,7 @@ FunctionList: Function: FunctionType FunctionModifiers tok_identifier '(' FieldList ')' { - t_struct* fun_args = new t_struct("__targs", $5); - $$ = new t_function($1, $3, fun_args); + $$ = new t_function($1, $3, $5); y_field_val = 0; } @@ -231,26 +233,29 @@ FieldList: | Field { pdebug("FieldList -> Field"); - $$ = new t_list; + $$ = new t_struct; $$->append($1); } | { pdebug("FieldList -> "); - $$ = new t_list; + $$ = new t_struct; } Field: FieldType tok_identifier '=' tok_int_constant { + pdebug("Field -> FieldType tok_identifier = tok_int_constant"); if ($4 < 0) { yyerror("Negative value (%d) not allowed as a field key.", $4); + exit(1); } $$ = new t_field($1, $2, (uint32_t)$4); y_field_val = $4+1; } | FieldType tok_identifier { + pdebug("Field -> FieldType tok_identifier"); printf("WARNING (%d): No field key specified for %s, resulting protocol may have conflicts or not be backwards compatible!\n", yylineno, $2); $$ = new t_field($1, $2, y_field_val++); } @@ -261,25 +266,42 @@ DefinitionType: pdebug("DefinitionType -> BaseType"); $$ = $1; } +| ContainerType + { + pdebug("DefinitionType -> ContainerType"); + $$ = $1; + } FunctionType: FieldType { + pdebug("FunctionType -> FieldType"); $$ = $1; } | tok_void { + pdebug("FunctionType -> tok_void"); $$ = g_program->get_void_type(); } FieldType: tok_identifier { - /** TODO(mcslee): Dynamic type lookup */ - yyerror("No dynamic type lookup yet."); + pdebug("FieldType -> tok_identifier"); + $$ = g_program->get_custom_type($1); + if ($$ == NULL) { + yyerror("Type \"%s\" has not been defined.", $1); + exit(1); + } } | BaseType { + pdebug("FieldType -> BaseType"); + $$ = $1; + } +| ContainerType + { + pdebug("FieldType -> ContainerType"); $$ = $1; } @@ -315,4 +337,42 @@ BaseType: $$ = g_program->get_u64_type(); } +ContainerType: + MapType + { + pdebug("ContainerType -> MapType"); + $$ = $1; + } +| SetType + { + pdebug("ContainerType -> SetType"); + $$ = $1; + } +| ListType + { + pdebug("ContainerType -> ListType"); + $$ = $1; + } + +MapType: + tok_map '<' FieldType ',' FieldType '>' + { + pdebug("MapType -> tok_map "); + $$ = new t_map($3, $5); + } + +SetType: + tok_set '<' FieldType '>' + { + pdebug("SetType -> tok_set"); + $$ = new t_set($3); + } + +ListType: + tok_list '<' FieldType '>' + { + pdebug("ListType -> tok_list"); + $$ = new t_list($3); + } + %% diff --git a/compiler/test/test.thrift b/compiler/test/test.thrift deleted file mode 100644 index b2baab35..00000000 --- a/compiler/test/test.thrift +++ /dev/null @@ -1,27 +0,0 @@ -typedef i32 gozone_t - -enum numberz -{ - ONE = 1, - TWO, - THREE, - FIVE = 5, - SIX, - EIGHT = 8 -} - -struct things -{ - u32 first_num, - u32 second_num, - u32 third_num, - u32 fourth_num -} - -service worker -{ - void funky_void() - string funky_string(string thing) - u32 funky_u32(u32 thing) - i32 funky_i32(i32 thing) -} diff --git a/lib/cpp/Makefile b/lib/cpp/Makefile new file mode 100644 index 00000000..2045dbae --- /dev/null +++ b/lib/cpp/Makefile @@ -0,0 +1,28 @@ +# Makefile for Thrift C++ library. +# +# Author: +# Mark Slee + +target: libthrift + +# Tools +LD = g++ +LDFL = -shared -Wall -I. -fPIC -Wl,-soname=libthrift.so + +# Source files +SRCS = client/TSimpleClient.cc \ + protocol/TBinaryProtocol.cc \ + server/TSimpleServer.cc \ + transport/TSocket.cc \ + transport/TServerSocket.cc + +# Linked library +libthrift: + $(LD) -o libthrift.so $(LDFL) $(SRCS) + +clean: + rm -f libthrift.so + +# Install +install: libthrift + sudo install libthrift.so /usr/local/lib diff --git a/lib/cpp/TDispatcher.h b/lib/cpp/TDispatcher.h new file mode 100644 index 00000000..f8ff8472 --- /dev/null +++ b/lib/cpp/TDispatcher.h @@ -0,0 +1,22 @@ +#ifndef T_DISPATCHER_H +#define T_DISPATCHER_H + +#include + +/** + * A dispatcher is a generic object that accepts an input buffer and returns + * a buffer. It can be used in a variety of ways, i.e. as a client that + * sends data over the network and returns a response, or as a server that + * reads an input and returns an output. + * + * @author Mark Slee + */ +class TDispatcher { + public: + virtual ~TDispatcher() {}; + virtual std::string dispatch(const std::string& s) = 0; + protected: + TDispatcher() {} +}; + +#endif diff --git a/lib/cpp/Thrift.h b/lib/cpp/Thrift.h new file mode 100644 index 00000000..04fbaa1f --- /dev/null +++ b/lib/cpp/Thrift.h @@ -0,0 +1,10 @@ +#ifndef THRIFT_H +#define THRIFT_H + +#include +#include +#include +#include +#include + +#endif diff --git a/lib/cpp/client/TClient.h b/lib/cpp/client/TClient.h new file mode 100644 index 00000000..73dd0938 --- /dev/null +++ b/lib/cpp/client/TClient.h @@ -0,0 +1,16 @@ +#ifndef T_CLIENT_H +#define T_CLIENT_H + +#include "TDispatcher.h" + +class TClient : public TDispatcher { + public: + virtual ~TClient() {} + virtual bool open() = 0; + virtual void close() = 0; + protected: + TClient() {} +}; + +#endif + diff --git a/lib/cpp/client/TSimpleClient.cc b/lib/cpp/client/TSimpleClient.cc new file mode 100644 index 00000000..9069c91b --- /dev/null +++ b/lib/cpp/client/TSimpleClient.cc @@ -0,0 +1,44 @@ +#include "TSimpleClient.h" +using std::string; + +TSimpleClient::TSimpleClient(TTransport* transport) : + transport_(transport) {} + +bool TSimpleClient::open() { + return transport_->open(); +} + +void TSimpleClient::close() { + transport_->close(); +} + +std::string TSimpleClient::dispatch(const string& s) { + // Write size header + int32_t size = s.size(); + // fprintf(stderr, "Writing size header %d to server\n", size); + transport_->write(string((char*)&size, 4)); + + // Write data payload + // fprintf(stderr, "Writing %d byte payload to server\n", (int)s.size()); + transport_->write(s); + + // Read response size + // fprintf(stderr, "Reading 4-byte response size header\n"); + string response; + transport_->read(response, 4); + size = *(int32_t*)response.data(); + + // Read response data + if (size < 0) { + // TODO(mcslee): Handle exception + // fprintf(stderr, "Exception case! Response size < 0\n"); + return ""; + } else { + // fprintf(stderr, "Reading %d byte response payload\n", size); + transport_->read(response, size); + // TODO(mcslee): Check that we actually read enough data + // fprintf(stderr, "Done reading payload, returning.\n"); + return response; + } +} + diff --git a/lib/cpp/client/TSimpleClient.h b/lib/cpp/client/TSimpleClient.h new file mode 100644 index 00000000..249afe51 --- /dev/null +++ b/lib/cpp/client/TSimpleClient.h @@ -0,0 +1,21 @@ +#ifndef T_SIMPLE_CLIENT_H +#define T_SIMPLE_CLIENT_H + +#include "client/TClient.h" +#include "transport/TTransport.h" + +class TSimpleClient : public TClient { + public: + TSimpleClient(TTransport* transport); + ~TSimpleClient() {} + + bool open(); + void close(); + std::string dispatch(const std::string& in); + + protected: + TTransport* transport_; +}; + +#endif + diff --git a/lib/cpp/protocol/TBinaryProtocol.cc b/lib/cpp/protocol/TBinaryProtocol.cc new file mode 100644 index 00000000..4c10babd --- /dev/null +++ b/lib/cpp/protocol/TBinaryProtocol.cc @@ -0,0 +1,140 @@ +#include "protocol/TBinaryProtocol.h" +using namespace std; + +string TBinaryProtocol::readFunction(TBuf& buf) const { + // Let readString increment the buffer position + return readString(buf); +} + +string TBinaryProtocol::writeFunction(const string& name, + const string& args) const{ + return writeString(name) + args; +} + +map TBinaryProtocol::readStruct(TBuf& buf) const { + map fieldMap; + + if (buf.len < 4) { + return fieldMap; + } + uint32_t total_size = readU32(buf); + if (buf.len < total_size) { + // Data looks corrupt, we don't have that much, we will try to read what + // we can but be sure not to go over + total_size = buf.len; + } + + // Field headers are 8 bytes, 4 byte fid + 4 byte length + while (total_size > 0 && buf.len > 8) { + uint32_t fid = readU32(buf); + uint32_t flen = readU32(buf); + if (flen > buf.len) { + // flen corrupt, there isn't that much data left + break; + } + fieldMap.insert(make_pair(fid, TBuf(buf.data, flen))); + buf.data += flen; + buf.len -= flen; + total_size -= 8 + flen; + } + + return fieldMap; +} + +string TBinaryProtocol::writeStruct(const map& s) const { + string result = ""; + map::const_iterator s_iter; + for (s_iter = s.begin(); s_iter != s.end(); ++s_iter) { + result += writeU32(s_iter->first); + result += writeU32(s_iter->second.size()); + result += s_iter->second; + } + return writeU32(result.size()) + result; +} + +string TBinaryProtocol::readString(TBuf& buf) const { + uint32_t len = readU32(buf); + if (len == 0) { + return ""; + } + string result((const char*)(buf.data), len); + buf.data += len; + buf.len -= len; + return result; +} + +uint8_t TBinaryProtocol::readByte(TBuf& buf) const { + if (buf.len == 0) { + return 0; + } + uint8_t result = (uint8_t)buf.data[0]; + buf.data += 1; + buf.len -= 1; + return result; +} + +uint32_t TBinaryProtocol::readU32(TBuf& buf) const { + if (buf.len < 4) { + return 0; + } + uint32_t result = *(uint32_t*)buf.data; + buf.data += 4; + buf.len -= 4; + return result; +} + +int32_t TBinaryProtocol::readI32(TBuf& buf) const { + if (buf.len < 4) { + return 0; + } + int32_t result = *(int32_t*)buf.data; + buf.data += 4; + buf.len -= 4; + return result; +} + +uint64_t TBinaryProtocol::readU64(TBuf& buf) const { + if (buf.len < 8) { + return 0; + } + uint64_t result = *(uint64_t*)buf.data; + buf.data += 8; + buf.len -= 8; + return result; +} + +int64_t TBinaryProtocol::readI64(TBuf& buf) const { + if (buf.len < 8) { + return 0; + } + int64_t result = *(int64_t*)buf.data; + buf.data += 8; + buf.len -= 8; + return result; +} + +string TBinaryProtocol::writeString(const string& str) const { + uint32_t size = str.size(); + string result = string((const char*)&size, 4); + return result + str; +} + +string TBinaryProtocol::writeByte(const uint8_t byte) const { + return string((const char*)&byte, 1); +} + +string TBinaryProtocol::writeU32(const uint32_t u32) const { + return string((const char*)&u32, 4); +} + +string TBinaryProtocol::writeI32(int32_t i32) const { + return string((const char*)&i32, 4); +} + +string TBinaryProtocol::writeU64(uint64_t u64) const { + return string((const char*)&u64, 8); +} + +string TBinaryProtocol::writeI64(int64_t i64) const { + return string((const char*)&i64, 8); +} diff --git a/lib/cpp/protocol/TBinaryProtocol.h b/lib/cpp/protocol/TBinaryProtocol.h new file mode 100644 index 00000000..976c3835 --- /dev/null +++ b/lib/cpp/protocol/TBinaryProtocol.h @@ -0,0 +1,42 @@ +#ifndef T_BINARY_PROTOCOL_H +#define T_BINARY_PROTOCOL_H + +#include "protocol/TProtocol.h" + +/** + * The default binary protocol for thrift. Writes all data in a very basic + * binary format, essentially just spitting out the raw bytes. + * + * @author Mark Slee + */ +class TBinaryProtocol : public TProtocol { + public: + TBinaryProtocol() {} + ~TBinaryProtocol() {} + + std::string + readFunction(TBuf& buf) const; + std::string + writeFunction(const std::string& name, const std::string& args) const; + + std::map + readStruct(TBuf& buf) const; + std::string + writeStruct(const std::map& s) const; + + std::string readString (TBuf& buf) const; + uint8_t readByte (TBuf& buf) const; + uint32_t readU32 (TBuf& buf) const; + int32_t readI32 (TBuf& buf) const; + uint64_t readU64 (TBuf& buf) const; + int64_t readI64 (TBuf& buf) const; + + std::string writeString (const std::string& str) const; + std::string writeByte (const uint8_t byte) const; + std::string writeU32 (const uint32_t u32) const; + std::string writeI32 (const int32_t i32) const; + std::string writeU64 (const uint64_t u64) const; + std::string writeI64 (const int64_t i64) const; +}; + +#endif diff --git a/lib/cpp/protocol/TProtocol.h b/lib/cpp/protocol/TProtocol.h new file mode 100644 index 00000000..1f2e0c88 --- /dev/null +++ b/lib/cpp/protocol/TProtocol.h @@ -0,0 +1,88 @@ +#ifndef T_PROTOCOL_H +#define T_PROTOCOL_H + +#include +#include +#include + +/** Forward declaration for TProtocol */ +struct TBuf; + +/** + * Abstract class for a thrift protocol driver. These are all the methods that + * a protocol must implement. Essentially, there must be some way of reading + * and writing all the base types, plus a mechanism for writing out structs + * with indexed fields. Also notice that all methods are strictly const. This + * is by design. Protcol impelementations may NOT keep state, because the + * same TProtocol object may be used simultaneously by multiple threads. This + * theoretically introduces some limititations into the possible protocol + * formats, but with the benefit of performance, clarity, and simplicity. + * + * @author Mark Slee + */ +class TProtocol { + public: + virtual ~TProtocol() {} + + /** + * Function call serialization. + */ + + virtual std::string + readFunction(TBuf& buf) const = 0; + virtual std::string + writeFunction(const std::string& name, const std::string& args) const = 0; + + /** + * Struct serialization. + */ + + virtual std::map + readStruct(TBuf& buf) const = 0; + virtual std::string + writeStruct(const std::map& s) const = 0; + + /** + * Basic data type deserialization. Note that these read methods do not + * take a const reference to the TBuf object. They SHOULD change the TBuf + * object so that it reflects the buffer AFTER the basic data type has + * been consumed such that data may continue being read serially from the + * buffer. + */ + + virtual std::string readString (TBuf& buf) const = 0; + virtual uint8_t readByte (TBuf& buf) const = 0; + virtual uint32_t readU32 (TBuf& buf) const = 0; + virtual int32_t readI32 (TBuf& buf) const = 0; + virtual uint64_t readU64 (TBuf& buf) const = 0; + virtual int64_t readI64 (TBuf& buf) const = 0; + + virtual std::string writeString (const std::string& str) const = 0; + virtual std::string writeByte (const uint8_t byte) const = 0; + virtual std::string writeU32 (const uint32_t u32) const = 0; + virtual std::string writeI32 (const int32_t i32) const = 0; + virtual std::string writeU64 (const uint64_t u64) const = 0; + virtual std::string writeI64 (const int64_t i64) const = 0; + + protected: + TProtocol() {} +}; + +/** + * Wrapper around raw data that allows us to track the length of a data + * buffer. It is the responsibility of a robust TProtocol implementation + * to ensure that any reads that are done from data do NOT overrun the + * memory address at data+len. It is also a convention that TBuf objects + * do NOT own the memory pointed to by data. They are merely wrappers + * around buffers that have been allocated elsewhere. Therefore, the user + * should never allocate memory before putting it into a TBuf nor should + * they free the data pointed to by a TBuf. + */ +struct TBuf { + TBuf(const TBuf& that) : data(that.data), len(that.len) {} + TBuf(const uint8_t* d, uint32_t l) : data(d), len(l) {} + const uint8_t* data; + uint32_t len; +}; + +#endif diff --git a/lib/cpp/server/TServer.h b/lib/cpp/server/TServer.h new file mode 100644 index 00000000..9c4cc59e --- /dev/null +++ b/lib/cpp/server/TServer.h @@ -0,0 +1,36 @@ +#ifndef T_SERVER_H +#define T_SERVER_H + +#include "TDispatcher.h" + +class TServerOptions; + +/** + * Thrift server. + * + * @author Mark Slee + */ +class TServer { + public: + virtual ~TServer() {} + virtual void run() = 0; + + protected: + TServer(TDispatcher* dispatcher, TServerOptions* options) : + dispatcher_(dispatcher), options_(options) {} + + TDispatcher* dispatcher_; + TServerOptions* options_; +}; + +/** + * Class to encapsulate all generic server options. + */ +class TServerOptions { + public: + // TODO(mcslee): Fill in getters/setters here + protected: + // TODO(mcslee): Fill data members in here +}; + +#endif diff --git a/lib/cpp/server/TSimpleServer.cc b/lib/cpp/server/TSimpleServer.cc new file mode 100644 index 00000000..16f5006b --- /dev/null +++ b/lib/cpp/server/TSimpleServer.cc @@ -0,0 +1,60 @@ +#include "server/TSimpleServer.h" +#include +using namespace std; + +void TSimpleServer::run() { + TTransport* client; + + // Start the server listening + if (serverTransport_->listen() == false) { + // TODO(mcslee): Log error here + fprintf(stderr, "TSimpleServer::run(): Call to listen failed\n"); + return; + } + + // Fetch client from server + while (true) { + // fprintf(stderr, "Listening for connection\n"); + if ((client = serverTransport_->accept()) == NULL) { + // fprintf(stderr, "Got NULL connection, exiting.\n"); + break; + } + + while (true) { + // Read header from client + // fprintf(stderr, "Reading 4 byte header from client.\n"); + string in; + if (client->read(in, 4) <= 0) { + // fprintf(stderr, "Size header negative. Exception!\n"); + break; + } + + // Read payload from client + int32_t size = *(int32_t*)(in.data()); + // fprintf(stderr, "Reading %d byte payload from client.\n", size); + if (client->read(in, size) < size) { + // fprintf(stderr, "Didn't get enough data!!!\n"); + break; + } + + // Pass payload to dispatcher + // TODO(mcslee): Wrap this in try/catch and return exceptions + string out = dispatcher_->dispatch(in); + + size = out.size(); + + // Write size of response packet + client->write(string((char*)&size, 4)); + + // Write response payload + client->write(out); + } + + // Clean up that client + // fprintf(stderr, "Closing and cleaning up client\n"); + client->close(); + delete client; + } + + // TODO(mcslee): Is this a timeout case or the real thing? +} diff --git a/lib/cpp/server/TSimpleServer.h b/lib/cpp/server/TSimpleServer.h new file mode 100644 index 00000000..47ab69e8 --- /dev/null +++ b/lib/cpp/server/TSimpleServer.h @@ -0,0 +1,30 @@ +#ifndef T_SIMPLE_SERVER_H +#define T_SIMPLE_SERVER_H + +#include "server/TServer.h" +#include "transport/TServerTransport.h" + +/** + * This is the most basic simple server. It is single-threaded and runs a + * continuous loop of accepting a single connection, processing requests on + * that connection until it closes, and then repeating. It is a good example + * of how to extend the TServer interface. + * + * @author Mark Slee + */ +class TSimpleServer : public TServer { + public: + TSimpleServer(TDispatcher* dispatcher, + TServerOptions* options, + TServerTransport* serverTransport) : + TServer(dispatcher, options), serverTransport_(serverTransport) {} + + ~TSimpleServer() {} + + void run(); + + protected: + TServerTransport* serverTransport_; +}; + +#endif diff --git a/lib/cpp/transport/TServerSocket.cc b/lib/cpp/transport/TServerSocket.cc new file mode 100644 index 00000000..178de812 --- /dev/null +++ b/lib/cpp/transport/TServerSocket.cc @@ -0,0 +1,90 @@ +#include +#include + +#include "transport/TSocket.h" +#include "transport/TServerSocket.h" + +TServerSocket::TServerSocket(int port) : + port_(port), serverSocket_(0), acceptBacklog_(1024) {} + +TServerSocket::~TServerSocket() { + close(); +} + +bool TServerSocket::listen() { + serverSocket_ = socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket_ == -1) { + close(); + return false; + } + + // Set reusaddress to prevent 2MSL delay on accept + int one = 1; + if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_REUSEADDR, + &one, sizeof(one))) { + perror("TServerSocket::listen() SO_REUSEADDR"); + close(); + return false; + } + + // Turn linger off, don't want to block on calls to close + struct linger ling = {0, 0}; + if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_LINGER, + &ling, sizeof(ling))) { + perror("TServerSocket::listen() SO_LINGER"); + close(); + return false; + } + + // Bind to a port + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port_); + addr.sin_addr.s_addr = INADDR_ANY; + if (-1 == bind(serverSocket_, (struct sockaddr *)&addr, sizeof(addr))) { + char errbuf[1024]; + sprintf(errbuf, "TServerSocket::listen() BIND %d", port_); + perror(errbuf); + close(); + return false; + } + + // Call listen + if (-1 == ::listen(serverSocket_, acceptBacklog_)) { + perror("TServerSocket::listen() LISTEN"); + close(); + return false; + } + + // The socket is now listening! + return true; +} + +TTransport* TServerSocket::accept() { + if (serverSocket_ <= 0) { + // TODO(mcslee): Log error with common logging tool + return NULL; + } + + struct sockaddr_in clientAddress; + int size = sizeof(clientAddress); + int clientSocket = ::accept(serverSocket_, + (struct sockaddr *) &clientAddress, + (socklen_t *) &size); + + if (clientSocket <= 0) { + perror("TServerSocket::accept()"); + return NULL; + } + + return new TSocket(clientSocket); +} + +void TServerSocket::close() { + if (serverSocket_ > 0) { + shutdown(serverSocket_, SHUT_RDWR); + ::close(serverSocket_); + } + serverSocket_ = 0; +} diff --git a/lib/cpp/transport/TServerSocket.h b/lib/cpp/transport/TServerSocket.h new file mode 100644 index 00000000..8ded4e20 --- /dev/null +++ b/lib/cpp/transport/TServerSocket.h @@ -0,0 +1,29 @@ +#ifndef T_SERVER_SOCKET_H +#define T_SERVER_SOCKET_H + +#include "transport/TServerTransport.h" + +class TSocket; + +/** + * Server socket implementation of TServerTransport. Wrapper around a unix + * socket listen and accept calls. + * + * @author Mark Slee + */ +class TServerSocket : public TServerTransport { + public: + TServerSocket(int port); + ~TServerSocket(); + + bool listen(); + TTransport* accept(); + void close(); + + private: + int port_; + int serverSocket_; + int acceptBacklog_; +}; + +#endif diff --git a/lib/cpp/transport/TServerTransport.h b/lib/cpp/transport/TServerTransport.h new file mode 100644 index 00000000..4d063fcb --- /dev/null +++ b/lib/cpp/transport/TServerTransport.h @@ -0,0 +1,24 @@ +#ifndef T_SERVER_TRANSPORT_H +#define T_SERVER_TRANSPORT_H + +#include "TTransport.h" + +/** + * Server transport framework. A server needs to have some facility for + * creating base transports to read/write from. + * + * @author Mark Slee + */ +class TServerTransport { + public: + virtual ~TServerTransport() {} + + virtual bool listen() = 0; + virtual TTransport* accept() = 0; + virtual void close() = 0; + + protected: + TServerTransport() {} +}; + +#endif diff --git a/lib/cpp/transport/TSocket.cc b/lib/cpp/transport/TSocket.cc new file mode 100644 index 00000000..1dfe431f --- /dev/null +++ b/lib/cpp/transport/TSocket.cc @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "transport/TSocket.h" + +using namespace std; + +// Mutex to protect syscalls to netdb +pthread_mutex_t g_netdb_mutex = PTHREAD_MUTEX_INITIALIZER; + +// TODO(mcslee): Make this an option to the socket class +#define MAX_RECV_RETRIES 20 + +TSocket::TSocket(string host, int port) : + host_(host), port_(port), socket_(0) {} + +TSocket::TSocket(int socket) { + socket_ = socket; +} + +TSocket::~TSocket() { + close(); +} + +bool TSocket::open() { + // Create socket + socket_ = socket(AF_INET, SOCK_STREAM, 0); + if (socket_ == -1) { + socket_ = 0; + return false; + } + + // Lookup the host + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port_); + + /* + if (inet_pton(AF_INET, host_.c_str(), &addr.sin_addr) < 0) { + perror("TSocket::open() inet_pton"); + } + */ + + { + // TODO(mcslee): Fix scope-locking here to protect hostname lookups + // scopelock sl(&netdb_mutex); + struct hostent *host_entry = gethostbyname(host_.c_str()); + + if (host_entry == NULL) { + // perror("dns error: failed call to gethostbyname.\n"); + close(); + return false; + } + + addr.sin_port = htons(port_); + memcpy(&addr.sin_addr.s_addr, + host_entry->h_addr_list[0], + host_entry->h_length); + } + + // Connect the socket + int ret = connect(socket_, (struct sockaddr *)&addr, sizeof(addr)); + + // Connect failed + if (ret < 0) { + perror("TSocket::open() connect"); + close(); + return false; + } + + return true; +} + +void TSocket::close() { + if (socket_ > 0) { + shutdown(socket_, SHUT_RDWR); + ::close(socket_); + } + socket_ = 0; +} + +int TSocket::read(string& s, uint32_t len) { + char buff[len]; + s = ""; + + uint32_t have = 0; + uint32_t retries = 0; + + while (have < len) { + try_again: + // Read from the socket + int got = recv(socket_, buff+have, len-have, 0); + + // Check for error on read + if (got < 0) { + perror("TSocket::read()"); + + // If temporarily out of resources, sleep a bit and try again + if (errno == EAGAIN && retries++ < MAX_RECV_RETRIES) { + usleep(50); + goto try_again; + } + + // If interrupted, try again + if (errno == EINTR && retries++ < MAX_RECV_RETRIES) { + goto try_again; + } + + // If we disconnect with no linger time + if (errno == ECONNRESET) { + return 0; + } + + return 0; + } + + // Check for empty read + if (got == 0) { + return 0; + } + + // Update the count + have += (uint32_t) got; + } + + // Pack data into string + s = string(buff, have); + return have; +} + +void TSocket::write(const string& s) { + uint32_t sent = 0; + + while (sent < s.size()) { + int b = send(socket_, s.data() + sent, s.size() - sent, 0); + + // Fail on a send error + if (b < 0) { + // TODO(mcslee): Make the function return how many bytes it wrote or + // throw an exception + // throw_perror("send"); + return; + } + + // Fail on blocked send + if (b == 0) { + // TODO(mcslee): Make the function return how many bytes it wrote or + // throw string("couldn't send data.\n"); + return; + } + + sent += b; + } +} + +bool TSocket::setLinger(bool on, int linger) { + struct linger ling = {(on ? 1 : 0), linger}; + if (-1 == setsockopt(socket_, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling))) { + close(); + perror("TSocket::setLinger()"); + return false; + } + return true; +} + +bool TSocket::setNoDelay(bool noDelay) { + // Set socket to NODELAY + int val = (noDelay ? 1 : 0); + if (-1 == setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) { + close(); + perror("TSocket::setNoDelay()"); + return false; + } + return true; +} diff --git a/lib/cpp/transport/TSocket.h b/lib/cpp/transport/TSocket.h new file mode 100644 index 00000000..1da74c60 --- /dev/null +++ b/lib/cpp/transport/TSocket.h @@ -0,0 +1,39 @@ +#ifndef T_SOCKET_H +#define T_SOCKET_H + +#include + +#include "transport/TTransport.h" +#include "transport/TServerSocket.h" + +class TSocketOptions; + +/** + * TCP Socket implementation of the TTransport interface. + * + * @author Mark Slee + */ +class TSocket : public TTransport { + friend TTransport* TServerSocket::accept(); + + public: + TSocket(std::string host, int port); + ~TSocket(); + + bool open(); + void close(); + int read (std::string &s, uint32_t size); + void write(const std::string& s); + + bool setLinger(bool on, int linger); + bool setNoDelay(bool noDelay); + + private: + TSocket(int socket); + TSocketOptions *options_; + std::string host_; + int port_; + int socket_; +}; + +#endif diff --git a/lib/cpp/transport/TTransport.h b/lib/cpp/transport/TTransport.h new file mode 100644 index 00000000..a1f43d4b --- /dev/null +++ b/lib/cpp/transport/TTransport.h @@ -0,0 +1,25 @@ +#ifndef T_TRANSPORT_H +#define T_TRANSPORT_H + +#include + +/** + * Generic interface for a method of transporting data. + * + * @author Mark Slee + */ +class TTransport { + public: + virtual ~TTransport() {}; + + virtual bool open() = 0; + virtual void close() = 0; + + virtual int read (std::string& s, uint32_t size) = 0; + virtual void write(const std::string& s) = 0; + + protected: + TTransport() {}; +}; + +#endif diff --git a/test/ThriftTest.thrift b/test/ThriftTest.thrift new file mode 100644 index 00000000..6999f4e5 --- /dev/null +++ b/test/ThriftTest.thrift @@ -0,0 +1,57 @@ +enum Numberz +{ + ONE = 1, + TWO, + THREE, + FIVE = 5, + SIX, + EIGHT = 8 +} + +typedef u64 UserId + +struct Xtruct +{ + string string_thing = 0, + byte byte_thing = 1, + u32 u32_thing = 2, + i32 i32_thing = 3, + u64 u64_thing = 4, + i64 i64_thing = 5 +} + +struct Xtruct2 +{ + byte byte_thing = 0, + Xtruct struct_thing = 1, + i32 i32_thing = 2 +} + +struct Insanity +{ + map userMap = 0, + list xtructs = 1 +} + +service ThriftTest +{ + void testVoid() + string testString(string thing = 0) + byte testByte(byte thing = 0) + u32 testU32(u32 thing = 0) + i32 testI32(i32 thing = 0) + u64 testU64(u64 thing = 0) + i64 testI64(i64 thing = 0) + Xtruct testStruct(Xtruct thing = 0) + Xtruct2 testNest(Xtruct2 thing = 0) + map testMap(map thing = 0) + set testSet(set thing = 0) + list testList(list thing = 0) + Numberz testEnum(Numberz thing = 0) + UserId testTypedef(UserId thing = 0) + + map> testMapMap(i32 hello = 0) + + /* So you think you've got this all worked, out eh? */ + map> testInsanity(Insanity argument = 0) +} diff --git a/test/cpp/Makefile b/test/cpp/Makefile new file mode 100644 index 00000000..6afb30be --- /dev/null +++ b/test/cpp/Makefile @@ -0,0 +1,34 @@ +# Makefile for Thrift test project. +# +# Author: +# Mark Slee + +# Default target is everything +target: all + +# Tools +THRIFT = /usr/local/bin/thrift +CC = g++ +LD = g++ + +# Compiler flags +LIBS = ../../lib/cpp/server/TSimpleServer.cc \ + ../../lib/cpp/protocol/TBinaryProtocol.cc \ + ../../lib/cpp/transport/TServerSocket.cc \ + ../../lib/cpp/transport/TSocket.cc +CFL = -Wall -g -I../../lib/cpp $(LIBS) +CFL = -Wall -g -lthrift -I../../lib/cpp + +all: server client + +stubs: ../ThriftTest.thrift + $(THRIFT) ../ThriftTest.thrift + +server: stubs + g++ -o TestServer $(CFL) TestServer.cc gen-cpp/ThriftTest.cc + +client: stubs + g++ -o TestClient $(CFL) TestClient.cc gen-cpp/ThriftTest.cc + +clean: + rm -fr TestServer TestClient gen-cpp diff --git a/test/cpp/TestClient.cc b/test/cpp/TestClient.cc new file mode 100644 index 00000000..6d2a68e1 --- /dev/null +++ b/test/cpp/TestClient.cc @@ -0,0 +1,327 @@ +#include +#include +#include "protocol/TBinaryProtocol.h" +#include "client/TSimpleClient.h" +#include "transport/TSocket.h" +#include "gen-cpp/ThriftTest.h" +using namespace std; + +int main(int argc, char** argv) { + string host = "localhost"; + int port = 9090; + int numTests = 1; + + if (argc > 1) { + host = argv[1]; + } + if (argc > 2) { + port = atoi(argv[2]); + } + if (argc > 3) { + numTests = atoi(argv[3]); + } + + TSocket socket(host, port); + TSimpleClient simpleClient(&socket); + TBinaryProtocol binaryProtocol; + ThriftTestClient testClient(&simpleClient, &binaryProtocol); + + int test = 0; + for (test = 0; test < numTests; ++test) { + printf("Test #%d, connect %s:%d\n", test+1, host.c_str(), port); + bool success = simpleClient.open(); + if (!success) { + printf("Connect failed, server down?\n"); + continue; + } + + /** + * VOID TEST + */ + printf("testVoid()"); + testClient.testVoid(); + printf(" = void\n"); + + /** + * STRING TEST + */ + printf("testString(\"Test\")"); + string s = testClient.testString("Test"); + printf(" = \"%s\"\n", s.c_str()); + + /** + * BYTE TEST + */ + printf("testByte(1)"); + uint8_t u8 = testClient.testByte(1); + printf(" = %d\n", (int)u8); + + /** + * U32 TEST + */ + printf("testU32(1)"); + uint32_t u32 = testClient.testU32(1); + printf(" = %u\n", u32); + + /** + * I32 TEST + */ + printf("testI32(-1)"); + int32_t i32 = testClient.testI32(-1); + printf(" = %d\n", i32); + + /** + * U64 TEST + */ + printf("testU64(34359738368)"); + uint64_t u64 = testClient.testU64(34359738368); + printf(" = %lu\n", u64); + + /** + * I64 TEST + */ + printf("testI64(-34359738368)"); + int64_t i64 = testClient.testI64(-34359738368); + printf(" = %ld\n", i64); + + /** + * STRUCT TEST + */ + printf("testStruct({\"Zero\", 1, 2, -3, 4, -5})"); + Xtruct out; + out.string_thing = "Zero"; + out.byte_thing = 1; + out.u32_thing = 2; + out.i32_thing = -3; + out.u64_thing = 4; + out.i64_thing = -5; + Xtruct in = testClient.testStruct(out); + printf(" = {\"%s\", %d, %u, %d, %lu, %ld}\n", + in.string_thing.c_str(), + (int)in.byte_thing, + in.u32_thing, + in.i32_thing, + in.u64_thing, + in.i64_thing); + + /** + * NESTED STRUCT TEST + */ + printf("testNest({1, {\"Zero\", 1, 2, -3, 4, -5}), 5}"); + Xtruct2 out2; + out2.byte_thing = 1; + out2.struct_thing = out; + out2.i32_thing = 5; + Xtruct2 in2 = testClient.testNest(out2); + in = in2.struct_thing; + printf(" = {%d, {\"%s\", %d, %u, %d, %lu, %ld}, %d}\n", + in2.byte_thing, + in.string_thing.c_str(), + (int)in.byte_thing, + in.u32_thing, + in.i32_thing, + in.u64_thing, + in.i64_thing, + in2.i32_thing); + + /** + * MAP TEST + */ + map mapout; + for (int32_t i = 0; i < 5; ++i) { + mapout.insert(make_pair(i, i-10)); + } + printf("testMap({"); + map::const_iterator m_iter; + bool first = true; + for (m_iter = mapout.begin(); m_iter != mapout.end(); ++m_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d => %d", m_iter->first, m_iter->second); + } + printf("})"); + map mapin = testClient.testMap(mapout); + printf(" = {"); + first = true; + for (m_iter = mapin.begin(); m_iter != mapin.end(); ++m_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d => %d", m_iter->first, m_iter->second); + } + printf("}\n"); + + /** + * SET TEST + */ + set setout; + for (int32_t i = -2; i < 3; ++i) { + setout.insert(i); + } + printf("testSet({"); + set::const_iterator s_iter; + first = true; + for (s_iter = setout.begin(); s_iter != setout.end(); ++s_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d", *s_iter); + } + printf("})"); + set setin = testClient.testSet(setout); + printf(" = {"); + first = true; + for (s_iter = setin.begin(); s_iter != setin.end(); ++s_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d", *s_iter); + } + printf("}\n"); + + /** + * LIST TEST + */ + list listout; + for (int32_t i = -2; i < 3; ++i) { + listout.push_back(i); + } + printf("testList({"); + list::const_iterator l_iter; + first = true; + for (l_iter = listout.begin(); l_iter != listout.end(); ++l_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d", *l_iter); + } + printf("})"); + list listin = testClient.testList(listout); + printf(" = {"); + first = true; + for (l_iter = listin.begin(); l_iter != listin.end(); ++l_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d", *l_iter); + } + printf("}\n"); + + /** + * ENUM TEST + */ + printf("testEnum(ONE)"); + Numberz ret = testClient.testEnum(ONE); + printf(" = %d\n", ret); + + printf("testEnum(TWO)"); + ret = testClient.testEnum(TWO); + printf(" = %d\n", ret); + + printf("testEnum(THREE)"); + ret = testClient.testEnum(THREE); + printf(" = %d\n", ret); + + printf("testEnum(FIVE)"); + ret = testClient.testEnum(FIVE); + printf(" = %d\n", ret); + + printf("testEnum(EIGHT)"); + ret = testClient.testEnum(EIGHT); + printf(" = %d\n", ret); + + /** + * TYPEDEF TEST + */ + printf("testTypedef(309858235082523)"); + UserId uid = testClient.testTypedef(309858235082523); + printf(" = %lu\n", uid); + + /** + * NESTED MAP TEST + */ + printf("testMapMap(1)"); + map > mm = testClient.testMapMap(1); + printf(" = {"); + map >::const_iterator mi; + for (mi = mm.begin(); mi != mm.end(); ++mi) { + printf("%d => {", mi->first); + map::const_iterator mi2; + for (mi2 = mi->second.begin(); mi2 != mi->second.end(); ++mi2) { + printf("%d => %d, ", mi2->first, mi2->second); + } + printf("}, "); + } + printf("}\n"); + + /** + * INSANITY TEST + */ + Insanity insane; + insane.userMap.insert(make_pair(FIVE, 5000)); + Xtruct truck; + truck.string_thing = "Truck"; + truck.byte_thing = 8; + truck.u32_thing = 8; + truck.i32_thing = 8; + truck.u64_thing = 8; + truck.i64_thing = 8; + insane.xtructs.push_back(truck); + printf("testInsanity()"); + map > whoa = testClient.testInsanity(insane); + printf(" = {"); + map >::const_iterator i_iter; + for (i_iter = whoa.begin(); i_iter != whoa.end(); ++i_iter) { + printf("%lu => {", i_iter->first); + map::const_iterator i2_iter; + for (i2_iter = i_iter->second.begin(); + i2_iter != i_iter->second.end(); + ++i2_iter) { + printf("%d => {", i2_iter->first); + map userMap = i2_iter->second.userMap; + map::const_iterator um; + printf("{"); + for (um = userMap.begin(); um != userMap.end(); ++um) { + printf("%d => %lu, ", um->first, um->second); + } + printf("}, "); + + list xtructs = i2_iter->second.xtructs; + list::const_iterator x; + printf("{"); + for (x = xtructs.begin(); x != xtructs.end(); ++x) { + printf("{\"%s\", %d, %u, %d, %lu, %ld}, ", + x->string_thing.c_str(), + (int)x->byte_thing, + x->u32_thing, + x->i32_thing, + x->u64_thing, + x->i64_thing); + } + printf("}"); + + printf("}, "); + } + printf("}, "); + } + printf("}\n"); + + simpleClient.close(); + } + + printf("\nAll tests done.\n"); + return 0; +} diff --git a/test/cpp/TestServer.cc b/test/cpp/TestServer.cc new file mode 100644 index 00000000..8138166e --- /dev/null +++ b/test/cpp/TestServer.cc @@ -0,0 +1,254 @@ +#include +#include "protocol/TBinaryProtocol.h" +#include "server/TSimpleServer.h" +#include "transport/TServerSocket.h" +#include "gen-cpp/ThriftTest.h" +using namespace std; + +class TestServer : public ThriftTestServerIf { + public: + TestServer(TProtocol* protocol) : + ThriftTestServerIf(protocol) {} + + void testVoid() { + printf("testVoid()\n"); + } + + string testString(string thing) { + printf("testString(\"%s\")\n", thing.c_str()); + return thing; + } + + uint8_t testByte(uint8_t thing) { + printf("testByte(%d)\n", (int)thing); + return thing; + } + + uint32_t testU32(uint32_t thing) { + printf("testU32(%u)\n", thing); + return thing; + } + + int32_t testI32(int32_t thing) { + printf("testI32(%d)\n", thing); + return thing; + } + + uint64_t testU64(uint64_t thing) { + printf("testU64(%lu)\n", thing); + return thing; + } + + int64_t testI64(int64_t thing) { + printf("testI64(%ld)\n", thing); + return thing; + } + + Xtruct testStruct(Xtruct thing) { + printf("testStruct({\"%s\", %d, %u, %d, %lu, %ld})\n", + thing.string_thing.c_str(), + (int)thing.byte_thing, + thing.u32_thing, + thing.i32_thing, + thing.u64_thing, + thing.i64_thing); + return thing; + } + + Xtruct2 testNest(Xtruct2 nest) { + Xtruct thing = nest.struct_thing; + printf("testNest({%d, {\"%s\", %d, %u, %d, %lu, %ld}, %d})\n", + (int)nest.byte_thing, + thing.string_thing.c_str(), + (int)thing.byte_thing, + thing.u32_thing, + thing.i32_thing, + thing.u64_thing, + thing.i64_thing, + nest.i32_thing); + return nest; + } + + map testMap(map thing) { + printf("testMap({"); + map::const_iterator m_iter; + bool first = true; + for (m_iter = thing.begin(); m_iter != thing.end(); ++m_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d => %d", m_iter->first, m_iter->second); + } + printf("})\n"); + return thing; + } + + set testSet(set thing) { + printf("testSet({"); + set::const_iterator s_iter; + bool first = true; + for (s_iter = thing.begin(); s_iter != thing.end(); ++s_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d", *s_iter); + } + printf("})\n"); + return thing; + } + + list testList(list thing) { + printf("testList({"); + list::const_iterator l_iter; + bool first = true; + for (l_iter = thing.begin(); l_iter != thing.end(); ++l_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d", *l_iter); + } + printf("})\n"); + return thing; + } + + Numberz testEnum(Numberz thing = 0) { + printf("testEnum(%d)\n", thing); + return thing; + } + + UserId testTypedef(UserId thing = 0) { + printf("testTypedef(%lu)\n", thing); + return thing; + } + + map > testMapMap(int32_t hello) { + printf("testMapMap(%d)\n", hello); + map > mapmap; + + map pos; + map neg; + for (int i = 1; i < 5; i++) { + pos.insert(make_pair(i,i)); + neg.insert(make_pair(-i,-i)); + } + + mapmap.insert(make_pair(4, pos)); + mapmap.insert(make_pair(-4, neg)); + + return mapmap; + } + + map > testInsanity(Insanity argument) { + printf("testInsanity()\n"); + + Xtruct hello; + hello.string_thing = "Hello2"; + hello.byte_thing = 2; + hello.u32_thing = 2; + hello.i32_thing = 2; + hello.u64_thing = 2; + hello.i64_thing = 2; + + Xtruct goodbye; + goodbye.string_thing = "Goodbye4"; + goodbye.byte_thing = 4; + goodbye.u32_thing = 4; + goodbye.i32_thing = 4; + goodbye.u64_thing = 4; + goodbye.i64_thing = 4; + + Insanity crazy; + crazy.userMap.insert(make_pair(EIGHT, 8)); + crazy.xtructs.push_back(goodbye); + + Insanity looney; + crazy.userMap.insert(make_pair(FIVE, 5)); + crazy.xtructs.push_back(hello); + + map first_map; + map second_map; + + first_map.insert(make_pair(TWO, crazy)); + first_map.insert(make_pair(THREE, crazy)); + + second_map.insert(make_pair(SIX, looney)); + + map > insane; + insane.insert(make_pair(1, first_map)); + insane.insert(make_pair(2, second_map)); + + printf("return"); + printf(" = {"); + map >::const_iterator i_iter; + for (i_iter = insane.begin(); i_iter != insane.end(); ++i_iter) { + printf("%lu => {", i_iter->first); + map::const_iterator i2_iter; + for (i2_iter = i_iter->second.begin(); + i2_iter != i_iter->second.end(); + ++i2_iter) { + printf("%d => {", i2_iter->first); + map userMap = i2_iter->second.userMap; + map::const_iterator um; + printf("{"); + for (um = userMap.begin(); um != userMap.end(); ++um) { + printf("%d => %lu, ", um->first, um->second); + } + printf("}, "); + + list xtructs = i2_iter->second.xtructs; + list::const_iterator x; + printf("{"); + for (x = xtructs.begin(); x != xtructs.end(); ++x) { + printf("{\"%s\", %d, %u, %d, %lu, %ld}, ", + x->string_thing.c_str(), + (int)x->byte_thing, + x->u32_thing, + x->i32_thing, + x->u64_thing, + x->i64_thing); + } + printf("}"); + + printf("}, "); + } + printf("}, "); + } + printf("}\n"); + + return insane; + } + +}; + +int main(int argc, char **argv) { + int port = 9090; + if (argc > 1) { + port = atoi(argv[1]); + } + + // Dispatcher + TBinaryProtocol binaryProtocol; + TestServer testServer(&binaryProtocol); + + // Options + TServerOptions serverOptions; + + // Transport + TServerSocket serverSocket(port); + + // Server + TSimpleServer simpleServer(&testServer, + &serverOptions, + &serverSocket); + + printf("Starting the server on port %d...\n", port); + simpleServer.run(); + printf("done.\n"); + return 0; +} -- 2.17.1