diff --git a/Source/buildimplementationcpp.go b/Source/buildimplementationcpp.go index 06a01a61..9e07f73e 100644 --- a/Source/buildimplementationcpp.go +++ b/Source/buildimplementationcpp.go @@ -115,7 +115,7 @@ func BuildImplementationCPP(component ComponentDefinition, outputFolder string, } if (doJournal) { - IntfJournalHeaderName := path.Join(outputFolder, strings.ToLower(BaseName)+"_interfacejournal.hpp"); + IntfJournalHeaderName := path.Join(outputFolder, BaseName+"_interfacejournal.hpp"); log.Printf("Creating \"%s\"", IntfJournalHeaderName) interfacejournalhppfile, err := CreateLanguageFile (IntfJournalHeaderName, indentString) if err != nil { @@ -125,7 +125,7 @@ func BuildImplementationCPP(component ComponentDefinition, outputFolder string, fmt.Sprintf("This is an autogenerated C++ header file in order to allow easy\ndevelopment of %s. It provides an automatic Journaling mechanism for the library implementation.", LibraryName), true) - IntfJournalImplName := path.Join(outputFolder, strings.ToLower(BaseName)+"_interfacejournal.cpp"); + IntfJournalImplName := path.Join(outputFolder, BaseName+"_interfacejournal.cpp"); log.Printf("Creating \"%s\"", IntfJournalImplName) interfacejournalcppfile, err := CreateLanguageFile(IntfJournalImplName, indentString) if err != nil { @@ -844,7 +844,26 @@ func buildCPPInterfaceWrapper(component ComponentDefinition, w LanguageWriter, N w.Writeln("") if (doJournal) { - w.Writeln("P%sInterfaceJournal m_GlobalJournal;", NameSpace) + w.Writeln("#include ") + w.Writeln("") + w.Writeln("// API calls on other threads read the journal while SetJournal replaces it.") + w.Writeln("static P%sInterfaceJournal m_GlobalJournal;", NameSpace) + w.Writeln("static std::mutex m_GlobalJournalMutex;") + w.Writeln("") + w.Writeln("static P%sInterfaceJournal getGlobalJournal()", NameSpace) + w.Writeln("{") + w.Writeln(" std::lock_guard lock(m_GlobalJournalMutex);") + w.Writeln(" return m_GlobalJournal;") + w.Writeln("}") + w.Writeln("") + w.Writeln("static void setGlobalJournal(P%sInterfaceJournal pJournal)", NameSpace) + w.Writeln("{") + w.Writeln(" {") + w.Writeln(" std::lock_guard lock(m_GlobalJournalMutex);") + w.Writeln(" m_GlobalJournal.swap(pJournal);") + w.Writeln(" }") + w.Writeln(" // The previous journal is released here, outside the lock; it closes when its last entry is written.") + w.Writeln("}") w.Writeln("") } @@ -1018,10 +1037,11 @@ func writeCImplementationMethod(component ComponentDefinition, method ComponentD } if (isSpecialFunction == eSpecialMethodJournal) { - callCPPFunctionCode = append(callCPPFunctionCode, "m_GlobalJournal = nullptr;") + callCPPFunctionCode = append(callCPPFunctionCode, fmt.Sprintf("P%sInterfaceJournal pNewJournal;", NameSpace)) callCPPFunctionCode = append(callCPPFunctionCode, fmt.Sprintf("if (s%s != \"\") {", method.Params[0].ParamName)) - callCPPFunctionCode = append(callCPPFunctionCode, fmt.Sprintf(" m_GlobalJournal = std::make_shared (s%s);", NameSpace, method.Params[0].ParamName)) + callCPPFunctionCode = append(callCPPFunctionCode, fmt.Sprintf(" pNewJournal = std::make_shared (s%s);", NameSpace, method.Params[0].ParamName)) callCPPFunctionCode = append(callCPPFunctionCode, "}") + callCPPFunctionCode = append(callCPPFunctionCode, "setGlobalJournal(pNewJournal);") } else if (isSpecialFunction == eSpecialMethodInjection) { callCPPFunctionCode = append(callCPPFunctionCode, "") callCPPFunctionCode = append(callCPPFunctionCode, "bool bNameSpaceFound = false;") @@ -1900,11 +1920,11 @@ func generateJournalFunctionCode(method ComponentDefinitionMethod, NameSpace str journalSuccessFunctionCode := make([]string,0) - journalInitFunctionCode = append(journalInitFunctionCode, fmt.Sprintf("if (m_GlobalJournal.get() != nullptr) {")) + journalInitFunctionCode = append(journalInitFunctionCode, fmt.Sprintf("if (P%sInterfaceJournal pActiveJournal = getGlobalJournal()) {", NameSpace)) if (isGlobal) { - journalInitFunctionCode = append(journalInitFunctionCode, fmt.Sprintf(" pJournalEntry = m_GlobalJournal->beginStaticFunction(\"%s\");", method.MethodName)) + journalInitFunctionCode = append(journalInitFunctionCode, fmt.Sprintf(" pJournalEntry = pActiveJournal->beginStaticFunction(\"%s\");", method.MethodName)) } else { - journalInitFunctionCode = append(journalInitFunctionCode, fmt.Sprintf(" pJournalEntry = m_GlobalJournal->beginClassMethod(p%s, \"%s\", \"%s\");", ClassName, ClassName, method.MethodName)) + journalInitFunctionCode = append(journalInitFunctionCode, fmt.Sprintf(" pJournalEntry = pActiveJournal->beginClassMethod(p%s, \"%s\", \"%s\");", ClassName, ClassName, method.MethodName)) } for k := 0; k < len(method.Params); k++ { @@ -1961,8 +1981,20 @@ func generateJournalFunctionCode(method ComponentDefinitionMethod, NameSpace str journalCall = "addHandleParameter(\"" + param.ParamName+ "\", " + variableName + ")"; case "struct": + if isJournalableStruct(param) { + journalCall = "addStructParameter(\"" + param.ParamName + "\", \"" + param.ParamClass + "\", p" + param.ParamName + ")"; + } + case "basicarray": + if isJournalableBasicArray(param) { + journalCall = "addBasicArrayParameter(\"" + param.ParamName + "\", \"" + param.ParamClass + "\", p" + param.ParamName + "Buffer, n" + param.ParamName + "BufferSize)"; + } + case "structarray": + if isJournalableStruct(param) { + journalCall = "addStructArrayParameter(\"" + param.ParamName + "\", \"" + param.ParamClass + "\", p" + param.ParamName + "Buffer, n" + param.ParamName + "BufferSize)"; + } + case "functiontype": default: @@ -2031,8 +2063,19 @@ func generateJournalFunctionCode(method ComponentDefinitionMethod, NameSpace str journalCall = "addHandleResult(\"" + param.ParamName+ "\", *p" + param.ParamName + ")"; case "struct": + if isJournalableStruct(param) { + journalCall = "addStructResult(\"" + param.ParamName + "\", \"" + param.ParamClass + "\", p" + param.ParamName + ")"; + } + case "basicarray": + if isJournalableBasicArray(param) { + journalCall = "addBasicArrayResult(\"" + param.ParamName + "\", \"" + param.ParamClass + "\", p" + param.ParamName + "Buffer, n" + param.ParamName + "BufferSize, p" + param.ParamName + "NeededCount)"; + } + case "structarray": + if isJournalableStruct(param) { + journalCall = "addStructArrayResult(\"" + param.ParamName + "\", \"" + param.ParamClass + "\", p" + param.ParamName + "Buffer, n" + param.ParamName + "BufferSize, p" + param.ParamName + "NeededCount)"; + } default: return journalInitFunctionCode, journalSuccessFunctionCode, fmt.Errorf("invalid method parameter passing \"%s\" for %s.%s(%s)", param.ParamPass, ClassName, method.MethodName, param.ParamName) @@ -2051,6 +2094,19 @@ func generateJournalFunctionCode(method ComponentDefinitionMethod, NameSpace str } +// Structs of imported components have no journal serializer in this component. +func isJournalableStruct(param ComponentDefinitionParam) bool { + return !strings.Contains(param.ParamClass, ":") +} + +func isJournalableBasicArray(param ComponentDefinitionParam) bool { + switch param.ParamClass { + case "bool", "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "pointer": + return true + } + return false +} + func buildCMakeForCPPImplementation(component ComponentDefinition, w LanguageWriter, doJournal bool) { NameSpace := component.NameSpace BaseName := component.BaseName @@ -2076,10 +2132,10 @@ func buildCMakeForCPPImplementation(component ComponentDefinition, w LanguageWri w.Writeln(")") w.Writeln("set(%s_SRC ${%s_SRC} ${%s_SRC}", strings.ToUpper(NameSpace), strings.ToUpper(NameSpace), strings.ToUpper(NameSpace)) - w.Writeln(" ${CMAKE_CURRENT_AUTOGENERATED_DIR}/%s_interfaceexception.cpp", strings.ToLower(BaseName)) - w.Writeln(" ${CMAKE_CURRENT_AUTOGENERATED_DIR}/%s_interfacewrapper.cpp", strings.ToLower(BaseName)) + w.Writeln(" ${CMAKE_CURRENT_AUTOGENERATED_DIR}/%s_interfaceexception.cpp", BaseName) + w.Writeln(" ${CMAKE_CURRENT_AUTOGENERATED_DIR}/%s_interfacewrapper.cpp", BaseName) if (doJournal) { - w.Writeln(" ${CMAKE_CURRENT_AUTOGENERATED_DIR}/%s_interfacejournal.cpp", strings.ToLower(BaseName)) + w.Writeln(" ${CMAKE_CURRENT_AUTOGENERATED_DIR}/%s_interfacejournal.cpp", BaseName) } w.Writeln(")") w.Writeln("") @@ -2115,35 +2171,84 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i headerw.Writeln("#include "); headerw.Writeln("#include "); headerw.Writeln("#include "); + headerw.Writeln("#include "); headerw.Writeln("#include \"%s_types.hpp\"", BaseName); headerw.Writeln(""); headerw.Writeln("/*************************************************************************************************************************"); + headerw.Writeln(" Journal value serialization"); + headerw.Writeln("**************************************************************************************************************************/"); + headerw.Writeln(""); + headerw.Writeln("// Arrays longer than this are journaled with their full count but only the first items."); + headerw.Writeln("const %s_uint64 %s_JOURNAL_MAXARRAYITEMS = 1000;", NameSpace, strings.ToUpper(NameSpace)); + headerw.Writeln(""); + headerw.Writeln("std::string %sJournalEscapeXML (const std::string & sValue);", NameSpace); + for _, basicType := range journalBasicTypes { + headerw.Writeln("std::string %sJournalValueToString (const %s Value);", NameSpace, journalCTypeName(basicType, NameSpace)); + } + for _, structinfo := range component.Structs { + headerw.Writeln("std::string %sJournalStructToXML (const s%s%s & Value, const std::string & sIndent);", NameSpace, NameSpace, structinfo.Name); + } + headerw.Writeln(""); + headerw.Writeln("template std::string %sJournalBasicArrayToXML (const T * pBuffer, const %s_uint64 nCount, const std::string & sIndent)", NameSpace, NameSpace); + headerw.Writeln("{"); + headerw.Writeln(" std::stringstream sStream;"); + headerw.Writeln(" for (%s_uint64 nIndex = 0; (nIndex < nCount) && (nIndex < %s_JOURNAL_MAXARRAYITEMS); nIndex++)", NameSpace, strings.ToUpper(NameSpace)); + headerw.Writeln(" sStream << sIndent << \"\\n\";", NameSpace); + headerw.Writeln(" return sStream.str();"); + headerw.Writeln("}"); + headerw.Writeln(""); + headerw.Writeln("template std::string %sJournalStructArrayToXML (const T * pBuffer, const %s_uint64 nCount, const std::string & sIndent)", NameSpace, NameSpace); + headerw.Writeln("{"); + headerw.Writeln(" std::stringstream sStream;"); + headerw.Writeln(" for (%s_uint64 nIndex = 0; (nIndex < nCount) && (nIndex < %s_JOURNAL_MAXARRAYITEMS); nIndex++) {", NameSpace, strings.ToUpper(NameSpace)); + headerw.Writeln(" sStream << sIndent << \"\\n\";"); + headerw.Writeln(" sStream << %sJournalStructToXML (pBuffer[nIndex], sIndent + \" \");", NameSpace); + headerw.Writeln(" sStream << sIndent << \"\\n\";"); + headerw.Writeln(" }"); + headerw.Writeln(" return sStream.str();"); + headerw.Writeln("}"); + headerw.Writeln(""); + headerw.Writeln("/*************************************************************************************************************************"); headerw.Writeln(" Class C%sInterfaceJournal ", NameSpace); headerw.Writeln("**************************************************************************************************************************/"); headerw.Writeln(""); headerw.Writeln("class C%sInterfaceJournal;", NameSpace); + headerw.Writeln("typedef std::shared_ptr P%sInterfaceJournal;", NameSpace, NameSpace); headerw.Writeln(""); headerw.Writeln("class C%sInterfaceJournalEntry {", NameSpace); headerw.Writeln(""); headerw.Writeln(" protected:"); headerw.Writeln(""); - headerw.Writeln(" C%sInterfaceJournal * m_pJournal;", NameSpace); + headerw.Writeln(" struct sJournalValue {"); + headerw.Writeln(" std::string m_sName;"); + headerw.Writeln(" std::string m_sType;"); + headerw.Writeln(" std::string m_sValue;"); + headerw.Writeln(" bool m_bHasValue;"); + headerw.Writeln(" std::string m_sAttributes;"); + headerw.Writeln(" std::string m_sChildXML;"); + headerw.Writeln(" };"); + headerw.Writeln(""); + headerw.Writeln(" P%sInterfaceJournal m_pJournal;", NameSpace); headerw.Writeln(" %sResult m_ErrorCode;", NameSpace); headerw.Writeln(" std::string m_sClassName;"); headerw.Writeln(" std::string m_sMethodName;"); headerw.Writeln(" std::string m_sInstanceHandle;"); headerw.Writeln(" %s_uint64 m_nInitTimeStamp;", NameSpace); headerw.Writeln(" %s_uint64 m_nFinishTimeStamp;", NameSpace); - headerw.Writeln(" std::list, std::string>> m_sParameters;"); - headerw.Writeln(" std::list, std::string>> m_sResultValues;"); + headerw.Writeln(" std::list m_sParameters;"); + headerw.Writeln(" std::list m_sResultValues;"); headerw.Writeln(""); headerw.Writeln(" std::string getXMLString();"); headerw.Writeln(" void addParameter (const std::string & sName, const std::string & sParameterType, const std::string & sParameterValue);"); headerw.Writeln(" void addResult (const std::string & sName, const std::string & sResultType, const std::string & sResultValue);"); + headerw.Writeln(" void addComplexParameter (const std::string & sName, const std::string & sParameterType, const std::string & sAttributes, const std::string & sChildXML);"); + headerw.Writeln(" void addComplexResult (const std::string & sName, const std::string & sResultType, const std::string & sAttributes, const std::string & sChildXML);"); + headerw.Writeln(" static std::string countAttribute (const %s_uint64 nCount);", NameSpace); + headerw.Writeln(" static %s_uint64 writtenArrayCount (const void * pBuffer, const %s_uint64 nBufferSize, const %s_uint64 * pNeededCount);", NameSpace, NameSpace, NameSpace); headerw.Writeln(""); headerw.Writeln(" public:"); - headerw.Writeln(" C%sInterfaceJournalEntry(C%sInterfaceJournal * pJournal, std::string sClassName, std::string sMethodName, %sHandle pInstanceHandle);", NameSpace, NameSpace, NameSpace); + headerw.Writeln(" C%sInterfaceJournalEntry(P%sInterfaceJournal pJournal, std::string sClassName, std::string sMethodName, %sHandle pInstanceHandle);", NameSpace, NameSpace, NameSpace); headerw.Writeln(" ~C%sInterfaceJournalEntry();", NameSpace); headerw.Writeln(""); headerw.Writeln(" void writeSuccess ();"); @@ -2181,6 +2286,50 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i headerw.Writeln(" void addHandleResult(const std::string & sName, const %sHandle pHandle);", NameSpace); headerw.Writeln(" void addEnumResult(const std::string & sName, const std::string & sEnumType, const %s_int32 nValue);", NameSpace); headerw.Writeln(""); + headerw.Writeln(" template void addStructParameter(const std::string & sName, const std::string & sStructType, const T * pValue)"); + headerw.Writeln(" {"); + headerw.Writeln(" if (pValue == nullptr)"); + headerw.Writeln(" addParameter(sName, \"nullstruct\" + sStructType, \"\");"); + headerw.Writeln(" else"); + headerw.Writeln(" addComplexParameter(sName, \"struct\" + sStructType, \"\", %sJournalStructToXML(*pValue, \" \"));", NameSpace); + headerw.Writeln(" }"); + headerw.Writeln(""); + headerw.Writeln(" template void addBasicArrayParameter(const std::string & sName, const std::string & sElementType, const T * pBuffer, const %s_uint64 nBufferSize)", NameSpace); + headerw.Writeln(" {"); + headerw.Writeln(" std::string sChildXML = (pBuffer != nullptr) ? %sJournalBasicArrayToXML(pBuffer, nBufferSize, \" \") : \"\";", NameSpace); + headerw.Writeln(" addComplexParameter(sName, \"basicarray\" + sElementType, countAttribute(nBufferSize), sChildXML);"); + headerw.Writeln(" }"); + headerw.Writeln(""); + headerw.Writeln(" template void addStructArrayParameter(const std::string & sName, const std::string & sStructType, const T * pBuffer, const %s_uint64 nBufferSize)", NameSpace); + headerw.Writeln(" {"); + headerw.Writeln(" std::string sChildXML = (pBuffer != nullptr) ? %sJournalStructArrayToXML(pBuffer, nBufferSize, \" \") : \"\";", NameSpace); + headerw.Writeln(" addComplexParameter(sName, \"structarray\" + sStructType, countAttribute(nBufferSize), sChildXML);"); + headerw.Writeln(" }"); + headerw.Writeln(""); + headerw.Writeln(" template void addStructResult(const std::string & sName, const std::string & sStructType, const T * pValue)"); + headerw.Writeln(" {"); + headerw.Writeln(" if (pValue == nullptr)"); + headerw.Writeln(" addResult(sName, \"nullstruct\" + sStructType, \"\");"); + headerw.Writeln(" else"); + headerw.Writeln(" addComplexResult(sName, \"struct\" + sStructType, \"\", %sJournalStructToXML(*pValue, \" \"));", NameSpace); + headerw.Writeln(" }"); + headerw.Writeln(""); + headerw.Writeln(" template void addBasicArrayResult(const std::string & sName, const std::string & sElementType, const T * pBuffer, const %s_uint64 nBufferSize, const %s_uint64 * pNeededCount)", NameSpace, NameSpace); + headerw.Writeln(" {"); + headerw.Writeln(" %s_uint64 nCount = (pNeededCount != nullptr) ? *pNeededCount : nBufferSize;", NameSpace); + headerw.Writeln(" %s_uint64 nWritten = writtenArrayCount(pBuffer, nBufferSize, pNeededCount);", NameSpace); + headerw.Writeln(" std::string sChildXML = (nWritten > 0) ? %sJournalBasicArrayToXML(pBuffer, nWritten, \" \") : \"\";", NameSpace); + headerw.Writeln(" addComplexResult(sName, \"basicarray\" + sElementType, countAttribute(nCount), sChildXML);"); + headerw.Writeln(" }"); + headerw.Writeln(""); + headerw.Writeln(" template void addStructArrayResult(const std::string & sName, const std::string & sStructType, const T * pBuffer, const %s_uint64 nBufferSize, const %s_uint64 * pNeededCount)", NameSpace, NameSpace); + headerw.Writeln(" {"); + headerw.Writeln(" %s_uint64 nCount = (pNeededCount != nullptr) ? *pNeededCount : nBufferSize;", NameSpace); + headerw.Writeln(" %s_uint64 nWritten = writtenArrayCount(pBuffer, nBufferSize, pNeededCount);", NameSpace); + headerw.Writeln(" std::string sChildXML = (nWritten > 0) ? %sJournalStructArrayToXML(pBuffer, nWritten, \" \") : \"\";", NameSpace); + headerw.Writeln(" addComplexResult(sName, \"structarray\" + sStructType, countAttribute(nCount), sChildXML);"); + headerw.Writeln(" }"); + headerw.Writeln(""); headerw.Writeln("friend class C%sInterfaceJournal;", NameSpace); headerw.Writeln(""); headerw.Writeln("};"); @@ -2191,7 +2340,8 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i headerw.Writeln(""); - headerw.Writeln("class C%sInterfaceJournal {", NameSpace); + headerw.Writeln("// Entries hold a shared reference, so a journal that SetJournal replaces stays open until in-flight calls finish."); + headerw.Writeln("class C%sInterfaceJournal : public std::enable_shared_from_this {", NameSpace, NameSpace); headerw.Writeln(""); headerw.Writeln(" protected:"); headerw.Writeln(""); @@ -2211,8 +2361,6 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i headerw.Writeln(" friend class C%sInterfaceJournalEntry;", NameSpace); headerw.Writeln("};"); headerw.Writeln(""); - headerw.Writeln("typedef std::shared_ptr P%sInterfaceJournal;", NameSpace, NameSpace); - headerw.Writeln(""); headerw.Writeln("#endif // __%s_INTERFACEJOURNAL_HEADER", strings.ToUpper (NameSpace)); headerw.Writeln(""); @@ -2221,9 +2369,10 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i implw.Writeln("#include "); implw.Writeln("#include "); implw.Writeln("#include "); + implw.Writeln("#include "); implw.Writeln(""); - implw.Writeln("#include \"%s_interfacejournal.hpp\"", strings.ToLower(BaseName)); - implw.Writeln("#include \"%s_interfaceexception.hpp\"", strings.ToLower(BaseName)); + implw.Writeln("#include \"%s_interfacejournal.hpp\"", BaseName); + implw.Writeln("#include \"%s_interfaceexception.hpp\"", BaseName); implw.Writeln(""); implw.Writeln(""); @@ -2235,8 +2384,13 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i implw.Writeln(" return stream.str();"); implw.Writeln("}"); implw.Writeln(""); + + err := buildJournalValueSerializersCPP(component, implw) + if err != nil { + return err + } - implw.Writeln("C%sInterfaceJournalEntry::C%sInterfaceJournalEntry(C%sInterfaceJournal * pJournal, std::string sClassName, std::string sMethodName, %sHandle pInstanceHandle)", NameSpace, NameSpace, NameSpace, NameSpace); + implw.Writeln("C%sInterfaceJournalEntry::C%sInterfaceJournalEntry(P%sInterfaceJournal pJournal, std::string sClassName, std::string sMethodName, %sHandle pInstanceHandle)", NameSpace, NameSpace, NameSpace, NameSpace); implw.Writeln(" : m_pJournal(pJournal), m_ErrorCode(%s_SUCCESS), m_sClassName(sClassName), m_sMethodName(sMethodName), m_nInitTimeStamp(0), m_nFinishTimeStamp(0)", strings.ToUpper(NameSpace)) implw.Writeln("{"); implw.Writeln(" if (pJournal == nullptr)"); @@ -2251,12 +2405,37 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addParameter(const std::string & sName, const std::string & sParameterType, const std::string & sParameterValue)", NameSpace); implw.Writeln("{"); - implw.Writeln(" m_sParameters.push_back(std::make_pair(std::make_pair(sName, sParameterType), sParameterValue));"); + implw.Writeln(" m_sParameters.push_back({ sName, sParameterType, sParameterValue, true, \"\", \"\" });"); implw.Writeln("}"); implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addResult(const std::string & sName, const std::string & sResultType, const std::string & sResultValue)", NameSpace); implw.Writeln("{"); - implw.Writeln(" m_sResultValues.push_back(std::make_pair(std::make_pair(sName, sResultType), sResultValue));"); + implw.Writeln(" m_sResultValues.push_back({ sName, sResultType, sResultValue, true, \"\", \"\" });"); + implw.Writeln("}"); + implw.Writeln(""); + implw.Writeln("void C%sInterfaceJournalEntry::addComplexParameter(const std::string & sName, const std::string & sParameterType, const std::string & sAttributes, const std::string & sChildXML)", NameSpace); + implw.Writeln("{"); + implw.Writeln(" m_sParameters.push_back({ sName, sParameterType, \"\", false, sAttributes, sChildXML });"); + implw.Writeln("}"); + implw.Writeln(""); + implw.Writeln("void C%sInterfaceJournalEntry::addComplexResult(const std::string & sName, const std::string & sResultType, const std::string & sAttributes, const std::string & sChildXML)", NameSpace); + implw.Writeln("{"); + implw.Writeln(" m_sResultValues.push_back({ sName, sResultType, \"\", false, sAttributes, sChildXML });"); + implw.Writeln("}"); + implw.Writeln(""); + implw.Writeln("std::string C%sInterfaceJournalEntry::countAttribute(const %s_uint64 nCount)", NameSpace, NameSpace); + implw.Writeln("{"); + implw.Writeln(" return \" count=\\\"\" + std::to_string(nCount) + \"\\\"\";"); + implw.Writeln("}"); + implw.Writeln(""); + implw.Writeln("// Out arrays are called once with a null buffer to query the size; only a filled buffer has items."); + implw.Writeln("%s_uint64 C%sInterfaceJournalEntry::writtenArrayCount(const void * pBuffer, const %s_uint64 nBufferSize, const %s_uint64 * pNeededCount)", NameSpace, NameSpace, NameSpace, NameSpace); + implw.Writeln("{"); + implw.Writeln(" if (pBuffer == nullptr)"); + implw.Writeln(" return 0;"); + implw.Writeln(" if ((pNeededCount != nullptr) && (*pNeededCount < nBufferSize))"); + implw.Writeln(" return *pNeededCount;"); + implw.Writeln(" return nBufferSize;"); implw.Writeln("}"); implw.Writeln(""); implw.Writeln("std::string C%sInterfaceJournalEntry::getXMLString()", NameSpace); @@ -2278,17 +2457,21 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i implw.Writeln(" if (m_sClassName != \"\")"); implw.Writeln(" sStream << \" \\n\";"); implw.Writeln(""); - implw.Writeln(" auto iParamIter = m_sParameters.begin();"); - implw.Writeln(" while (iParamIter != m_sParameters.end()) {"); - implw.Writeln(" sStream << \" first.first << \"\\\" type=\\\"\" << iParamIter->first.second << \"\\\" value=\\\"\" << iParamIter->second <<\"\\\" />\\n\";"); - implw.Writeln(" iParamIter++;"); - implw.Writeln(" }"); + implw.Writeln(" auto writeValues = [&sStream] (const std::string & sTag, const std::list & Values) {"); + implw.Writeln(" for (auto & Value : Values) {"); + implw.Writeln(" sStream << \" <\" << sTag << \" name=\\\"\" << %sJournalEscapeXML(Value.m_sName) << \"\\\" type=\\\"\" << %sJournalEscapeXML(Value.m_sType) << \"\\\"\";", NameSpace, NameSpace); + implw.Writeln(" if (Value.m_bHasValue)"); + implw.Writeln(" sStream << \" value=\\\"\" << %sJournalEscapeXML(Value.m_sValue) << \"\\\"\";", NameSpace); + implw.Writeln(" sStream << Value.m_sAttributes;"); + implw.Writeln(" if (Value.m_sChildXML.empty())"); + implw.Writeln(" sStream << \" />\\n\";"); + implw.Writeln(" else"); + implw.Writeln(" sStream << \">\\n\" << Value.m_sChildXML << \" \\n\";"); + implw.Writeln(" }"); + implw.Writeln(" };"); implw.Writeln(""); - implw.Writeln(" auto iResultIter = m_sResultValues.begin();",); - implw.Writeln(" while (iResultIter != m_sResultValues.end()) {"); - implw.Writeln(" sStream << \" first.first << \"\\\" type=\\\"\" << iResultIter->first.second << \"\\\" value=\\\"\" << iResultIter->second << \"\\\" />\\n\";"); - implw.Writeln(" iResultIter++;"); - implw.Writeln(" }"); + implw.Writeln(" writeValues(\"parameter\", m_sParameters);"); + implw.Writeln(" writeValues(\"result\", m_sResultValues);"); implw.Writeln(""); implw.Writeln(" sStream << \" \\n\";"); implw.Writeln(" return sStream.str ();"); @@ -2343,7 +2526,7 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addInt32Parameter(const std::string & sName, const %s_int32 nValue)", NameSpace, NameSpace); implw.Writeln("{"); - implw.Writeln(" addParameter(sName, \"uint32\", std::to_string(nValue));"); + implw.Writeln(" addParameter(sName, \"int32\", std::to_string(nValue));"); implw.Writeln("}"); implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addInt64Parameter(const std::string & sName, const %s_int64 nValue)", NameSpace, NameSpace); @@ -2353,12 +2536,12 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addSingleParameter(const std::string & sName, const %s_single fValue)", NameSpace, NameSpace); implw.Writeln("{"); - implw.Writeln(" addParameter(sName, \"single\", std::to_string(fValue));"); + implw.Writeln(" addParameter(sName, \"single\", %sJournalValueToString(fValue));", NameSpace); implw.Writeln("}"); implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addDoubleParameter(const std::string & sName, const %s_double dValue)", NameSpace, NameSpace); implw.Writeln("{"); - implw.Writeln(" addParameter(sName, \"double\", std::to_string(dValue));"); + implw.Writeln(" addParameter(sName, \"double\", %sJournalValueToString(dValue));", NameSpace); implw.Writeln("}"); implw.Writeln("void C%sInterfaceJournalEntry::addPointerParameter(const std::string & sName, const %s_pvoid pValue)", NameSpace, NameSpace); implw.Writeln("{"); @@ -2422,7 +2605,7 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addInt32Result(const std::string & sName, const %s_int32 nValue)", NameSpace, NameSpace); implw.Writeln("{"); - implw.Writeln(" addResult(sName, \"uint32\", std::to_string(nValue));"); + implw.Writeln(" addResult(sName, \"int32\", std::to_string(nValue));"); implw.Writeln("}"); implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addInt64Result(const std::string & sName, const %s_int64 nValue)", NameSpace, NameSpace); @@ -2432,12 +2615,12 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addSingleResult(const std::string & sName, const %s_single fValue)", NameSpace, NameSpace); implw.Writeln("{"); - implw.Writeln(" addResult(sName, \"single\", std::to_string(fValue));"); + implw.Writeln(" addResult(sName, \"single\", %sJournalValueToString(fValue));", NameSpace); implw.Writeln("}"); implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addDoubleResult(const std::string & sName, const %s_double dValue)", NameSpace, NameSpace); implw.Writeln("{"); - implw.Writeln(" addResult(sName, \"double\", std::to_string(dValue));"); + implw.Writeln(" addResult(sName, \"double\", %sJournalValueToString(dValue));", NameSpace); implw.Writeln("}"); implw.Writeln(""); implw.Writeln("void C%sInterfaceJournalEntry::addPointerResult(const std::string & sName, const %s_pvoid pValue)", NameSpace, NameSpace); @@ -2471,6 +2654,8 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i implw.Writeln("{"); implw.Writeln(" m_StartTime = std::chrono::high_resolution_clock::now();"); implw.Writeln(" m_Stream.open (sFileName, std::ios::out);"); + implw.Writeln(" if (!m_Stream.is_open())"); + implw.Writeln(" throw E%sInterfaceException (%s_ERROR_INVALIDPARAM);", NameSpace, strings.ToUpper (NameSpace)); implw.Writeln(" m_Stream << \"\\n\";"); implw.Writeln(" m_Stream << \"\\n\";", NameSpace, component.Version, NameSpace, component.Version); @@ -2487,12 +2672,12 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i implw.Writeln(""); implw.Writeln("P%sInterfaceJournalEntry C%sInterfaceJournal::beginClassMethod(const %sHandle pHandle, const std::string & sClassName, const std::string & sMethodName)", NameSpace, NameSpace, NameSpace); implw.Writeln("{"); - implw.Writeln(" return std::make_shared(this, sClassName, sMethodName, pHandle);", NameSpace); + implw.Writeln(" return std::make_shared(shared_from_this(), sClassName, sMethodName, pHandle);", NameSpace); implw.Writeln("}"); implw.Writeln(""); implw.Writeln("P%sInterfaceJournalEntry C%sInterfaceJournal::beginStaticFunction(const std::string & sMethodName)", NameSpace, NameSpace); implw.Writeln("{"); - implw.Writeln(" return std::make_shared(this, \"\", sMethodName, nullptr);", NameSpace); + implw.Writeln(" return std::make_shared(shared_from_this(), \"\", sMethodName, nullptr);", NameSpace); implw.Writeln("}"); implw.Writeln(""); implw.Writeln("void C%sInterfaceJournal::writeEntry (C%sInterfaceJournalEntry * pEntry)", NameSpace, NameSpace); @@ -2532,3 +2717,105 @@ func buildJournalingCPP(component ComponentDefinition, headerw LanguageWriter, i return nil; } +var journalBasicTypes = []string{"bool", "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "pointer"} + +func journalCTypeName(basicType string, NameSpace string) string { + typeName, _ := getCParameterTypeName(basicType, NameSpace, "") + return typeName +} + +// buildJournalValueSerializersCPP generates XML escaping, scalar formatting and one serializer per struct for the journal +func buildJournalValueSerializersCPP(component ComponentDefinition, implw LanguageWriter) error { + NameSpace := component.NameSpace + + implw.Writeln("std::string %sJournalEscapeXML (const std::string & sValue)", NameSpace) + implw.Writeln("{") + implw.Writeln(" std::string sResult;") + implw.Writeln(" sResult.reserve(sValue.size());") + implw.Writeln(" for (char cChar : sValue) {") + implw.Writeln(" switch (cChar) {") + implw.Writeln(" case '&': sResult += \"&\"; break;") + implw.Writeln(" case '<': sResult += \"<\"; break;") + implw.Writeln(" case '>': sResult += \">\"; break;") + implw.Writeln(" case '\"': sResult += \""\"; break;") + implw.Writeln(" case '\\'': sResult += \"'\"; break;") + implw.Writeln(" case '\\n': sResult += \" \"; break;") + implw.Writeln(" case '\\r': sResult += \" \"; break;") + implw.Writeln(" case '\\t': sResult += \" \"; break;") + implw.Writeln(" default: sResult += cChar;") + implw.Writeln(" }") + implw.Writeln(" }") + implw.Writeln(" return sResult;") + implw.Writeln("}") + implw.Writeln("") + + for _, basicType := range journalBasicTypes { + cTypeName := journalCTypeName(basicType, NameSpace) + implw.Writeln("std::string %sJournalValueToString (const %s Value)", NameSpace, cTypeName) + implw.Writeln("{") + switch basicType { + case "bool", "uint8", "int8": + implw.Writeln(" return std::to_string((int)Value);") + case "single", "double": + implw.Writeln(" std::stringstream sStream;") + implw.Writeln(" sStream << std::setprecision(std::numeric_limits<%s>::digits10) << Value;", cTypeName) + implw.Writeln(" return sStream.str();") + case "pointer": + implw.Writeln(" return std::to_string(reinterpret_cast(Value));", NameSpace) + default: + implw.Writeln(" return std::to_string(Value);") + } + implw.Writeln("}") + implw.Writeln("") + } + + for _, structinfo := range component.Structs { + implw.Writeln("std::string %sJournalStructToXML (const s%s%s & Value, const std::string & sIndent)", NameSpace, NameSpace, structinfo.Name) + implw.Writeln("{") + implw.Writeln(" std::stringstream sStream;") + for _, member := range structinfo.Members { + memberType := member.Type + valueExpression := "Value.m_" + member.Name + if member.Type == "enum" { + memberType = "enum" + member.Class + } + + if member.Rows == 0 { + if member.Type == "enum" { + valueExpression = fmt.Sprintf("(%s_int32) %s", NameSpace, valueExpression) + } + implw.Writeln(" sStream << sIndent << \"\\n\";", member.Name, memberType, NameSpace, valueExpression) + continue + } + + // Fixed-size array members are written as one space-separated value list. + implw.Writeln(" sStream << sIndent << \" 0 { + implw.Writeln(" for (int nColumn = 0; nColumn < %d; nColumn++) {", member.Columns) + implw.Writeln(" for (int nRow = 0; nRow < %d; nRow++) {", member.Rows) + valueExpression = valueExpression + "[nColumn][nRow]" + } else { + implw.Writeln(" {") + implw.Writeln(" for (int nRow = 0; nRow < %d; nRow++) {", member.Rows) + valueExpression = valueExpression + "[nRow]" + } + if member.Type == "enum" { + valueExpression = fmt.Sprintf("(%s_int32) %s", NameSpace, valueExpression) + } + implw.Writeln(" sStream << (bFirst ? \"\" : \" \") << %sJournalValueToString(%s);", NameSpace, valueExpression) + implw.Writeln(" bFirst = false;") + implw.Writeln(" }") + implw.Writeln(" }") + implw.Writeln(" }") + implw.Writeln(" sStream << \"\\\" />\\n\";") + } + implw.Writeln(" return sStream.str();") + implw.Writeln("}") + implw.Writeln("") + } + + return nil +} + diff --git a/Source/buildimplementationcpp_test.go b/Source/buildimplementationcpp_test.go new file mode 100644 index 00000000..f6ccf6bc --- /dev/null +++ b/Source/buildimplementationcpp_test.go @@ -0,0 +1,112 @@ +package main + +import ( + "bytes" + "strings" + "testing" +) + +func TestGenerateJournalFunctionCodeStructsAndArrays(t *testing.T) { + method := ComponentDefinitionMethod{ + MethodName: "Update", + Params: []ComponentDefinitionParam{ + {ParamName: "Point", ParamType: "struct", ParamClass: "Coordinate", ParamPass: "in"}, + {ParamName: "Points", ParamType: "structarray", ParamClass: "Coordinate", ParamPass: "in"}, + {ParamName: "Weights", ParamType: "basicarray", ParamClass: "double", ParamPass: "in"}, + {ParamName: "Foreign", ParamType: "struct", ParamClass: "Other:Thing", ParamPass: "in"}, + {ParamName: "Box", ParamType: "struct", ParamClass: "Box", ParamPass: "out"}, + {ParamName: "Corners", ParamType: "structarray", ParamClass: "Coordinate", ParamPass: "out"}, + {ParamName: "Values", ParamType: "basicarray", ParamClass: "uint32", ParamPass: "out"}, + {ParamName: "Center", ParamType: "struct", ParamClass: "Coordinate", ParamPass: "return"}, + }, + } + + initCode, successCode, err := generateJournalFunctionCode(method, "Lib", "Shape", false) + if err != nil { + t.Fatalf("generateJournalFunctionCode failed: %v", err) + } + initText := strings.Join(initCode, "\n") + successText := strings.Join(successCode, "\n") + + expectedInit := []string{ + `pJournalEntry->addStructParameter("Point", "Coordinate", pPoint);`, + `pJournalEntry->addStructArrayParameter("Points", "Coordinate", pPointsBuffer, nPointsBufferSize);`, + `pJournalEntry->addBasicArrayParameter("Weights", "double", pWeightsBuffer, nWeightsBufferSize);`, + } + for _, expected := range expectedInit { + if !strings.Contains(initText, expected) { + t.Errorf("journal init code is missing %q:\n%s", expected, initText) + } + } + if strings.Contains(initText, "Foreign") { + t.Errorf("structs of imported components must not be journaled:\n%s", initText) + } + + expectedSuccess := []string{ + `pJournalEntry->addStructResult("Box", "Box", pBox);`, + `pJournalEntry->addStructArrayResult("Corners", "Coordinate", pCornersBuffer, nCornersBufferSize, pCornersNeededCount);`, + `pJournalEntry->addBasicArrayResult("Values", "uint32", pValuesBuffer, nValuesBufferSize, pValuesNeededCount);`, + `pJournalEntry->addStructResult("Center", "Coordinate", pCenter);`, + } + for _, expected := range expectedSuccess { + if !strings.Contains(successText, expected) { + t.Errorf("journal success code is missing %q:\n%s", expected, successText) + } + } +} + +func TestBuildJournalingCPPSerializesStructsAndEscapesXML(t *testing.T) { + var component ComponentDefinition + component.NameSpace = "Lib" + component.BaseName = "lib" + component.Version = "1.0.0" + component.Structs = []ComponentDefinitionStruct{ + { + Name: "Sample", + Members: []ComponentDefinitionMember{ + {Name: "Value", Type: "double"}, + {Name: "Mode", Type: "enum", Class: "Mode"}, + {Name: "Vector", Type: "single", Rows: 3}, + {Name: "Matrix", Type: "double", Rows: 4, Columns: 4}, + }, + }, + } + + var header, impl bytes.Buffer + headerw := LanguageWriter{IndentString: " ", Writer: &header} + implw := LanguageWriter{IndentString: " ", Writer: &impl} + if err := buildJournalingCPP(component, headerw, implw); err != nil { + t.Fatalf("buildJournalingCPP failed: %v", err) + } + headerText := header.String() + implText := impl.String() + + expectedHeader := []string{ + "std::string LibJournalStructToXML (const sLibSample & Value, const std::string & sIndent);", + "template void addStructParameter(", + "template void addStructArrayResult(", + "template void addBasicArrayResult(", + } + for _, expected := range expectedHeader { + if !strings.Contains(headerText, expected) { + t.Errorf("journal header is missing %q", expected) + } + } + + expectedImpl := []string{ + `#include "lib_interfacejournal.hpp"`, + `