Groundwork for exception support:

     Auto generate result structs that combine return type and any thrown exceptions
     Add __isset struct to all user defined and auto defined struct to mark fields that are explicilty read
     Modified client and server generation code to marshal result structs

     Added base facebook::thrift::Exception class 


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664750 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/src/cpp_generator.py b/compiler/src/cpp_generator.py
index f67ca00..2c8efc5 100644
--- a/compiler/src/cpp_generator.py
+++ b/compiler/src/cpp_generator.py
@@ -117,9 +117,9 @@
 }
 
 CPP_CONTAINER_MAP = {
-    Map : "std::map",
-    List: "std::list",
-    Set : "std::set",
+    MapType : "std::map",
+    ListType: "std::list",
+    SetType : "std::set",
 }
 
 def typeToCTypeDeclaration(ttype):
@@ -131,10 +131,10 @@
 
         result = CPP_CONTAINER_MAP[type(ttype)]+"<"
         
-        if isinstance(ttype, Map):
+        if isinstance(ttype, MapType):
             result+= typeToCTypeDeclaration(ttype.keyType)+", "+ typeToCTypeDeclaration(ttype.valueType)
 
-        elif isinstance(ttype, Set) or isinstance(ttype, List):
+        elif isinstance(ttype, SetType) or isinstance(ttype, ListType):
             result+= typeToCTypeDeclaration(ttype.valueType)
 
         else:
@@ -144,17 +144,17 @@
 
         return result
 
-    elif isinstance(ttype, Struct):
+    elif isinstance(ttype, StructType):
         return "struct "+ttype.name
 
-    elif isinstance(ttype, TypeDef):
+    elif isinstance(ttype, TypedefType):
         return ttype.name;
 
-    elif isinstance(ttype, Enum):
+    elif isinstance(ttype, EnumType):
         return ttype.name;
 
     elif isinstance(ttype, Function):
-        return typeToCTypeDeclaration(ttype.resultType)+ " "+ttype.name+"("+string.join([typeToCTypeDeclaration(arg) for arg in ttype.argsStruct.fieldList], ", ")+")"
+        return typeToCTypeDeclaration(ttype.returnType())+ " "+ttype.name+"("+string.join([typeToCTypeDeclaration(arg) for arg in ttype.args()], ", ")+")"
 
     elif isinstance(ttype, Field):
         return typeToCTypeDeclaration(ttype.type)+ " "+ttype.name
@@ -189,16 +189,23 @@
     result = "struct "+struct.name+" {\n"
 
     for field in struct.fieldList:
-        result += "    "+typeToCTypeDeclaration(field)+";\n"
+	if toCanonicalType(field.type) != VOID_TYPE:
+	    result += "    "+typeToCTypeDeclaration(field)+";\n"
+
+    result+= "    struct {\n"
+
+    for field in struct.fieldList:
+	result+= "        bool "+field.name+";\n"
+    result+= "   } __isset;\n"
 
     result+= "};\n"
 
     return result
 
 CPP_DEFINITION_MAP = {
-    TypeDef : toTypeDefDefinition,
-    Enum : toEnumDefinition,
-    Struct : toStructDefinition,
+    TypedefType : toTypeDefDefinition,
+    EnumType : toEnumDefinition,
+    StructType : toStructDefinition,
     Service : None
     }
     
@@ -233,6 +240,8 @@
 
     return CPP_INTERFACE_DECLARATION.substitute(service=service.name, functionDeclarations=functionDeclarations)
 
+CPP_EXCEPTION = CPP_THRIFT_NS+"::Exception"
+
 CPP_SP = Template("boost::shared_ptr<${klass}> ")
 
 CPP_PROCESSOR = CPP_THRIFT_NS+"::TProcessor"
@@ -255,11 +264,19 @@
 
     uint32_t xfer = 0;
 
-${argsStructDeclaration}
-${argsStructReader}    
-${resultDeclaration}
-${functionCall}
-${resultWriter}
+    ${argsStructDeclaration};
+
+    ${argsStructReader};
+
+    ${returnValueDeclaration};
+
+    ${functionCall};
+
+    ${resultStructDeclaration};
+
+    ${returnToResult};
+
+    ${resultStructWriter};
 
     otrans->flush();
 }
@@ -267,6 +284,9 @@
 
 CPP_PROTOCOL_TSTOP = CPP_PROTOCOL_NS+"::T_STOP"
 CPP_PROTOCOL_TTYPE = CPP_PROTOCOL_NS+"::TType"
+CPP_PROTOCOL_MESSAGE_TYPE = CPP_PROTOCOL_NS+"::TMessageType"
+CPP_PROTOCOL_CALL = CPP_PROTOCOL_NS+"::T_CALL"
+CPP_PROTOCOL_REPLY = CPP_PROTOCOL_NS+"::T_REPLY"
 
 CPP_TTYPE_MAP = {
     STOP_TYPE : CPP_PROTOCOL_NS+"::T_STOP",
@@ -286,10 +306,10 @@
     U32_TYPE : CPP_PROTOCOL_NS+"::T_U32",
     U64_TYPE : CPP_PROTOCOL_NS+"::T_U64",
     FLOAT_TYPE : CPP_PROTOCOL_NS+"::T_FLOAT",
-    Struct : CPP_PROTOCOL_NS+"::T_STRUCT",
-    List : CPP_PROTOCOL_NS+"::T_LIST",
-    Map : CPP_PROTOCOL_NS+"::T_MAP",
-    Set : CPP_PROTOCOL_NS+"::T_SET"
+    StructType : CPP_PROTOCOL_NS+"::T_STRUCT",
+    ListType : CPP_PROTOCOL_NS+"::T_LIST",
+    MapType : CPP_PROTOCOL_NS+"::T_MAP",
+    SetType : CPP_PROTOCOL_NS+"::T_SET"
 }
 
 def toWireType(ttype):
@@ -297,13 +317,13 @@
     if isinstance(ttype, PrimitiveType):
 	return CPP_TTYPE_MAP[ttype]
 
-    elif isinstance(ttype, Enum):
+    elif isinstance(ttype, EnumType):
 	return CPP_TTYPE_MAP[I32_TYPE]
 
-    elif isinstance(ttype, TypeDef):
+    elif isinstance(ttype, TypedefType):
 	return toWireType(toCanonicalType(ttype))
 
-    elif isinstance(ttype, Struct) or isinstance(ttype, CollectionType):
+    elif isinstance(ttype, StructType) or isinstance(ttype, CollectionType):
 	return CPP_TTYPE_MAP[type(ttype)]
 
     else:
@@ -332,6 +352,46 @@
 CPP_CLIENT_FUNCTION_DECLARATION = Template("""    ${functionDeclaration};
 """)
 
+
+CPP_CLIENT_FUNCTION_DEFINITION = Template("""
+${returnDeclaration} ${service}Client::${function}(${argsDeclaration}) {
+
+    uint32_t xfer = 0;
+    """+CPP_PROTOCOL_MESSAGE_TYPE+""" messageType;
+    uint32_t cseqid = 0;
+    uint32_t rseqid = 0;
+
+    _oprot->writeMessageBegin(_otrans, """+CPP_PROTOCOL_CALL+""", cseqid);
+
+    ${argsStructDeclaration};
+
+${argsToStruct};
+
+    ${argsStructWriter};
+
+    _otrans->flush();
+
+    _iprot->readMessageBegin(_itrans, messageType, rseqid);
+
+    if(messageType != """+CPP_PROTOCOL_REPLY+""" || 
+       rseqid != cseqid) {
+        throw """+CPP_EXCEPTION+"""(\"unexpected message type or id\");
+    }
+
+    ${resultStructDeclaration};
+
+    ${resultStructReader};
+
+    _iprot->readMessageEnd(_itrans);
+
+    if(__result.__isset.success) {
+        ${success};
+    } else {
+        throw """+CPP_EXCEPTION+"""(\"${function} failed\");
+    }
+}
+""")
+
 CPP_CLIENT_DECLARATION = Template("""
 class ${service}Client : public ${service}If {
 
@@ -349,36 +409,48 @@
     """+CPP_PROTOCOLP+""" _oprot;
 };""")
 
+def toServerFunctionDefinition(servicePrefix, function, debugp=None):
+    result = ""
+
+    argsStructDeclaration = typeToCTypeDeclaration(function.argsStruct)+" __args"
+
+    argsStructReader = toReaderCall("__args", function.argsStruct, "_iprot")
+
+    resultStructDeclaration = typeToCTypeDeclaration(function.resultStruct)+" __result"
+
+    resultStructWriter = toWriterCall("__result", function.resultStruct, "_oprot")
+
+    if function.returnType() != VOID_TYPE:
+	returnValueDeclaration = typeToCTypeDeclaration(toCanonicalType(function.returnType()))+"  __returnValue"
+	functionCall = "__returnValue = "
+	returnToResult = "__result.success = __returnValue"
+    else:
+	returnValueDeclaration = ""
+	functionCall = ""
+	returnToResult = ""
+
+    functionCall+= function.name+"("+string.join(["__args."+arg.name for arg in function.args()], ", ")+")"
+
+    result+= CPP_SERVER_FUNCTION_DEFINITION.substitute(service=servicePrefix, function=function.name,
+						       argsStructDeclaration=argsStructDeclaration, 
+						       argsStructReader=argsStructReader, 
+						       functionCall=functionCall,
+						       returnToResult=returnToResult,
+						       resultStructDeclaration=resultStructDeclaration,
+						       resultStructWriter=resultStructWriter,
+						       returnValueDeclaration=returnValueDeclaration)
+
+    
+
+    return result
+
 def toServerServiceDefinition(service, debugp=None):
 
     result = ""
 
     for function in service.functionList:
-
-	if len(function.argsStruct.fieldList) > 0:
-	    argsStructDeclaration = "    "+typeToCTypeDeclaration(function.argsStruct)+" __args;\n"
-	    argsStructReader = "    "+toReaderCall("__args", function.argsStruct, "_iprot")+";\n"
-	else:
-	    argsStructDeclaration = ""
-	    argsStructReader = ""
-
-	functionCall = "    "
-	resultDeclaration = ""
-	resultWriter = ""
-
-	if toCanonicalType(function.resultType) != VOID_TYPE:
-	    resultDeclaration = "    "+typeToCTypeDeclaration(function.resultType)+" __result;\n"
-	    functionCall+= "__result = "
-
-	functionCall+= function.name+"("+string.join(["__args."+arg.name for arg in function.argsStruct.fieldList], ", ")+");\n"
-
-	if toCanonicalType(function.resultType) != VOID_TYPE:
-	    resultWriter = "    "+toWriterCall("__result", function.resultType, "_oprot")+";"
-
-	result+= CPP_SERVER_FUNCTION_DEFINITION.substitute(service=service.name, function=function.name,
-							   argsStructDeclaration=argsStructDeclaration, argsStructReader=argsStructReader, 
-							   functionCall=functionCall,
-							   resultDeclaration=resultDeclaration, resultWriter=resultWriter)
+	
+	result+= toServerFunctionDefinition(service.name, function, debugp)
 
     return result
 
@@ -392,6 +464,53 @@
 
     return CPP_CLIENT_DECLARATION.substitute(service=service.name, functionDeclarations=functionDeclarations)+"\n"
 
+def toClientFunctionDefinition(servicePrefix, function, debugp=None):
+
+    returnDeclaration = typeToCTypeDeclaration(function.returnType())
+
+    argsDeclaration = string.join([typeToCTypeDeclaration(function.args()[ix].type)+" __arg"+str(ix) for ix in range(len(function.args()))], ", ")
+
+    argsStructDeclaration = typeToCTypeDeclaration(function.argsStruct)+" __args"
+
+    argsStructWriter = toWriterCall("__args", function.argsStruct, "_oprot", "_otrans")
+
+    argsToStruct= string.join(["    __args."+function.args()[ix].name+" = __arg"+str(ix) for ix in range(len(function.args()))], ";\n")
+    
+    resultStructDeclaration = typeToCTypeDeclaration(function.resultStruct)+" __result"
+
+    resultStructReader = toReaderCall("__result", function.resultStruct, "_iprot", "_itrans")
+
+    if(toCanonicalType(function.returnType()) != VOID_TYPE):
+	
+	success = "return __result.success;"
+    else:
+	success = ""
+	    
+    return CPP_CLIENT_FUNCTION_DEFINITION.substitute(service=servicePrefix,
+						     function=function.name,
+						     returnDeclaration=returnDeclaration,
+						     argsDeclaration=argsDeclaration,
+						     argsStructDeclaration=argsStructDeclaration,
+						     argsStructWriter=argsStructWriter,
+						     argsToStruct=argsToStruct,
+						     resultStructDeclaration=resultStructDeclaration, 
+						     resultStructReader=resultStructReader,
+						     success=success)
+
+def toClientServiceDefinition(service, debugp=None):
+
+    result = ""
+
+    for function in service.functionList:
+
+	result+= toClientFunctionDefinition(service.name, function)
+
+    return result
+
+def toClientDefinition(program, debugp=None):
+
+    return string.join([toClientServiceDefinition(service) for service in program.serviceMap.values()], "\n")
+
 def toServiceDeclaration(service, debugp=None):
     return toServiceInterfaceDeclaration(service, debugp) + toServerDeclaration(service, debugp) + toClientDeclaration(service, debugp)
 
@@ -522,6 +641,7 @@
 """)
 
 CPP_PRIMITIVE_TYPE_IO_METHOD_SUFFIX_MAP = {
+    "void" :"Void",
     "bool" : "Bool",
     "string": "String",
     "utf7": "String",
@@ -539,9 +659,9 @@
 }
 
 CPP_COLLECTION_TYPE_IO_METHOD_SUFFIX_MAP = {
-    Map : "map",
-    List : "list",
-    Set : "set"
+    MapType : "map",
+    ListType : "list",
+    SetType : "set"
 }
 
 def typeToIOMethodSuffix(ttype):
@@ -553,64 +673,70 @@
 
         result = CPP_COLLECTION_TYPE_IO_METHOD_SUFFIX_MAP[type(ttype)]+"_"
 
-        if isinstance(ttype, Map):
+        if isinstance(ttype, MapType):
             result+= "k_"+typeToIOMethodSuffix(ttype.keyType)+"_"
 
         result += "v_"+typeToIOMethodSuffix(ttype.valueType)
 
         return result
 
-    elif isinstance(ttype, Struct):
+    elif isinstance(ttype, StructType):
         return "struct_"+ttype.name
 
-    elif isinstance(ttype, TypeDef):
+    elif isinstance(ttype, TypedefType):
         return ttype.name
 
-    elif isinstance(ttype, Enum):
+    elif isinstance(ttype, EnumType):
         return ttype.name
 
     else:
         raise Exception, "Unknown type "+str(ttype)
 
-def toReaderCall(value, ttype, reader="iprot"):
+def toReaderCall(value, ttype, reader="iprot", transport="itrans"):
 
     suffix = typeToIOMethodSuffix(ttype)
 
     if isinstance(ttype, PrimitiveType):
-        return "xfer += "+reader+"->read"+suffix+"(itrans, "+value+")"
+	if ttype != VOID_TYPE:
+	    return "xfer += "+reader+"->read"+suffix+"("+transport+", "+value+")"
+	else:
+	    return ""
 
     elif isinstance(ttype, CollectionType):
-        return "xfer+= read_"+suffix+"("+reader+", itrans, "+value+")"
+        return "xfer+= read_"+suffix+"("+reader+", "+transport+", "+value+")"
 
-    elif isinstance(ttype, Struct):
-        return "xfer+= read_"+suffix+"("+reader+", itrans, "+value+")"
+    elif isinstance(ttype, StructType):
+        return "xfer+= read_"+suffix+"("+reader+", "+transport+", "+value+")"
 
-    elif isinstance(ttype, TypeDef):
+    elif isinstance(ttype, TypedefType):
         return toReaderCall("reinterpret_cast<"+typeToCTypeDeclaration(ttype.definitionType)+"&>("+value+")", ttype.definitionType, reader)
 
-    elif isinstance(ttype, Enum):
+    elif isinstance(ttype, EnumType):
         return toReaderCall("reinterpret_cast<"+typeToCTypeDeclaration(I32_TYPE)+"&>("+value+")", I32_TYPE, reader)
 
     else:
         raise Exception, "Unknown type "+str(ttype)
 
-def toWriterCall(value, ttype, writer="oprot"):
+def toWriterCall(value, ttype, writer="oprot", transport="otrans"):
 
     suffix = typeToIOMethodSuffix(ttype)
 
     if isinstance(ttype, PrimitiveType):
-        return "xfer+= "+writer+"->write"+suffix+"(otrans, "+value+")"
+	if ttype != VOID_TYPE:
+	    return "xfer+= "+writer+"->write"+suffix+"("+transport+", "+value+")"
+	else:
+	    return ""
 
     elif isinstance(ttype, CollectionType):
-        return "xfer+= write_"+suffix+"("+writer+", otrans, "+value+")"
+        return "xfer+= write_"+suffix+"("+writer+", "+transport+", "+value+")"
 
-    elif isinstance(ttype, Struct):
-        return "xfer+= write_"+suffix+"("+writer+", otrans, "+value+")"
+    elif isinstance(ttype, StructType):
+        return "xfer+= write_"+suffix+"("+writer+", "+transport+", "+value+")"
 
-    elif isinstance(ttype, TypeDef):
+    elif isinstance(ttype, TypedefType):
         return toWriterCall("reinterpret_cast<const "+typeToCTypeDeclaration(ttype.definitionType)+"&>("+value+")", ttype.definitionType, writer)
 
-    elif isinstance(ttype, Enum):
+    elif isinstance(ttype, EnumType):
         return toWriterCall("reinterpret_cast<const "+typeToCTypeDeclaration(I32_TYPE)+"&>("+value+")", I32_TYPE, writer)
 
     else:
@@ -686,12 +812,12 @@
 
     suffix = typeToIOMethodSuffix(ttype)
 
-    if isinstance(ttype, Map):
+    if isinstance(ttype, MapType):
         keyReaderCall = toReaderCall("key", ttype.keyType)
 
     valueReaderCall= toReaderCall("elem", ttype.valueType)
 
-    if isinstance(ttype, Map):
+    if isinstance(ttype, MapType):
         return CPP_READ_MAP_DEFINITION.substitute(suffix=suffix, declaration=typeToCTypeDeclaration(ttype),
                                                   keyType=typeToCTypeDeclaration(ttype.keyType),
                                                   keyReaderCall=keyReaderCall,
@@ -699,7 +825,7 @@
                                                   valueReaderCall=valueReaderCall)
 
     else:
-	if isinstance(ttype, List):
+	if isinstance(ttype, ListType):
 	    insert="push_back"
 	else:
 	    insert="insert"
@@ -714,14 +840,14 @@
 
     suffix = typeToIOMethodSuffix(ttype)
 
-    if isinstance(ttype, Map):
+    if isinstance(ttype, MapType):
         keyWriterCall = toWriterCall("ix->first", ttype.keyType)
         valueWriterCall = toWriterCall("ix->second", ttype.valueType)
 
     else:
 	valueWriterCall= toWriterCall("*ix", ttype.valueType)
 
-    if isinstance(ttype, Map):
+    if isinstance(ttype, MapType):
         return CPP_WRITE_MAP_DEFINITION.substitute(suffix=suffix, declaration=typeToCTypeDeclaration(ttype),
                                                   keyType=typeToCTypeDeclaration(ttype.keyType),
                                                   keyWriterCall=keyWriterCall,
@@ -742,6 +868,8 @@
     int16_t id;
     uint32_t xfer = 0;
 
+    xfer+= iprot->readStructBegin(itrans, name);
+
     while(true) {
 
         xfer+= iprot->readFieldBegin(itrans, name, type, id);
@@ -750,10 +878,14 @@
 
         switch(id) {
 ${fieldSwitch}
-            default: xfer += iprot->skip(itrans, type); break;}
+            default: xfer += iprot->skip(itrans, type); break;
+	}
 
         xfer+= iprot->readFieldEnd(itrans);
     }
+
+    xfer+= iprot->readStructEnd(itrans);
+
     return xfer;
 }
 """)
@@ -792,7 +924,7 @@
 
     for field in fieldList:
         fieldSwitch+= "            case "+str(field.id)+": "
-        fieldSwitch+= toReaderCall("value."+field.name, field.type)+"; break;\n"
+        fieldSwitch+= toReaderCall("value."+field.name, field.type)+"; value.__isset."+field.name+" = true; break;\n"
 
     return CPP_READ_STRUCT_DEFINITION.substitute(suffix=suffix, declaration=typeToCTypeDeclaration(ttype), fieldSwitch=fieldSwitch)
 
@@ -813,13 +945,13 @@
     if isinstance(ttype, CollectionType):
         return toCollectionReaderDefinition(ttype)
 
-    elif isinstance(ttype, Struct):
+    elif isinstance(ttype, StructType):
         return toStructReaderDefinition(ttype)
 
-    elif isinstance(ttype, TypeDef):
+    elif isinstance(ttype, TypedefType):
 	return ""
 
-    elif isinstance(ttype, Enum):
+    elif isinstance(ttype, EnumType):
 	return ""
 
     else:
@@ -829,13 +961,13 @@
     if isinstance(ttype, CollectionType):
         return toCollectionWriterDefinition(ttype)
 
-    elif isinstance(ttype, Struct):
+    elif isinstance(ttype, StructType):
         return toStructWriterDefinition(ttype)
 
-    elif isinstance(ttype, TypeDef):
+    elif isinstance(ttype, TypedefType):
 	return ""
 
-    elif isinstance(ttype, Enum):
+    elif isinstance(ttype, EnumType):
 	return ""
 
     else:
@@ -853,23 +985,23 @@
 
     elif isinstance(ttype, CollectionType):
 
-	if isinstance(ttype, Map):
+	if isinstance(ttype, MapType):
 	    result = toOrderedIOList(ttype.keyType, result)
 
 	result = toOrderedIOList(ttype.valueType, result)
 
 	result.append(ttype)
 
-    elif isinstance(ttype, Struct):
+    elif isinstance(ttype, StructType):
 	for field in ttype.fieldList:
 	    result = toOrderedIOList(field.type, result)
 	result.append(ttype)
 
-    elif isinstance(ttype, TypeDef):
+    elif isinstance(ttype, TypedefType):
 	result.append(ttype)
 	return result
 
-    elif isinstance(ttype, Enum):
+    elif isinstance(ttype, EnumType):
 	result.append(ttype)
 	return result
 
@@ -886,13 +1018,13 @@
 	    result = toOrderedIOList(function, result)
 
     elif isinstance(ttype, Function):
-	result = toOrderedIOList(ttype.resultType, result)
+	result = toOrderedIOList(ttype.returnType(), result)
 
 	# skip the args struct itself and just order the arguments themselves
-	# we don't want the arg struct to be referred to until laters, since we need to
+	# we don't want the arg struct to be referred to until later, since we need to
 	# inline those struct definitions with the implementation, not in the types header
 	
-	for field in ttype.argsStruct.fieldList:
+	for field in ttype.args():
 	    result = toOrderedIOList(field.type, result)
 
     else:
@@ -912,19 +1044,20 @@
 	result+= toReaderDefinition(ttype)
 	result+= toWriterDefinition(ttype)
 
-    # for all function argument lists we need to create both struct definitions
-    # and io methods.  We keep the struct definitions local, since they aren't part of the service
-    # API
+    # For all function argument lists, we need to create both struct definitions
+    # and io methods.  We keep the struct definitions local, since they aren't part of the service API
+    #
     # Note that we don't need to do a depth-first traverse of arg structs since they can only include fields
     # we've already seen
 
     for service in program.serviceMap.values():
 	for function in service.functionList:
-	    if len(function.argsStruct.fieldList) == 0:
-		continue
 	    result+= toStructDefinition(function.argsStruct)
-	    result+=toReaderDefinition(function.argsStruct)
-	    result+=toWriterDefinition(function.argsStruct)
+	    result+= toReaderDefinition(function.argsStruct)
+	    result+= toWriterDefinition(function.argsStruct)
+	    result+= toStructDefinition(function.resultStruct)
+	    result+= toReaderDefinition(function.resultStruct)
+	    result+= toWriterDefinition(function.resultStruct)
 
     return result;
 
@@ -959,6 +1092,8 @@
 
     cfile.write(toServerDefinition(program))
 
+    cfile.write(toClientDefinition(program))
+
     cfile.writeln(CPP_IMPL_FOOTER.substitute(source=basename))
 
     cfile.close()