From 2935c77a5f52270e91aba74c845e4edf6ee3c8a8 Mon Sep 17 00:00:00 2001 From: Martin Weismann <30837766+martinweismann@users.noreply.github.com> Date: Tue, 2 Aug 2022 17:54:08 +0200 Subject: [PATCH] Autogenerate Documentation with Sphinx (#181) * First try for Sphinx documentation * Add more documentation * Prepare for more documentation-generation * Fixup build --- Build/build.bat | 2 +- Build/build.sh | 2 +- README.md | 26 +- Source/automaticcomponenttoolkit.go | 9 +- Source/buildbindingccpp.go | 57 ++- Source/buildbindingccppdocumentation.go | 482 ++++++++++++++++++++++++ Source/componentdefinition.go | 2 +- Source/languagec.go | 57 ++- Source/languagecpp.go | 6 +- 9 files changed, 597 insertions(+), 46 deletions(-) mode change 100755 => 100644 Build/build.sh create mode 100644 Source/buildbindingccppdocumentation.go diff --git a/Build/build.bat b/Build/build.bat index a690dae0..49a6a27d 100644 --- a/Build/build.bat +++ b/Build/build.bat @@ -3,7 +3,7 @@ set startingDir="%CD%" set basepath="%~dp0" cd %basepath%\..\Source -set Sources=actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildimplementationcpp.go buildbindingjava.go buildimplementationpascal.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go +set Sources=actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildimplementationcpp.go buildbindingjava.go buildimplementationpascal.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go set GOOS=windows set GOARCH=amd64 diff --git a/Build/build.sh b/Build/build.sh old mode 100755 new mode 100644 index 0a3ae636..e4ba1ad4 --- a/Build/build.sh +++ b/Build/build.sh @@ -9,7 +9,7 @@ startingpath="$(pwd)" basepath="$(cd "$(dirname "$0")" && pwd)" cd "$basepath/../Source" -Sources="actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildbindingjava.go buildimplementationcpp.go buildimplementationpascal.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go" +Sources="actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildbindingjava.go buildimplementationcpp.go buildimplementationpascal.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go" export GOARCH="amd64" echo "Build act.exe" diff --git a/README.md b/README.md index 661795e1..ff2e6af4 100644 --- a/README.md +++ b/README.md @@ -56,19 +56,19 @@ Alternatively to 1) build ACT from source ([master](../../tree/master) for a rel ACT supports generation of bindings or implementation stubs for C++, C, Pascal, Golang, NodeJS and Python3. However, not all features of the IDL are yet supported by the individual binding or implementation language: #### Feature Matrix: Bindings -| Binding | Status | Operating Systems | class | scalar type | struct | enumeration | string | basicarray | structarray | Callbacks | Error Message Propagation | Injection | -|:---------------:|:----------------------------------------------------------:|:-----------------:|:---------:|:-------------:|:-------------:|:-------------:|:-------------:|:----------:|:-----------:|:---------:|:---------:|:---------:| -| C++ | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | -| C++ Dynamic | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | -| C | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | - | -| C Dynamic | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | - | -| Pascal | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | -| Python3 | ![](Documentation/images/Tick.png) complete (but not very pythonic) | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | -| Golang | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | -| NodeJS | ![](Documentation/images/O.png) partial support | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | ? | ? | - | + | - | -| C# | ![](Documentation/images/O.png) experimental | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | - | - | - | + | - | -| Java | ![](Documentation/images/Tick.png) experimental | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | -| PHP | ![](Documentation/images/X.png) not implemented | Win, Linux, MacOS | - | - | - | - | - | - | - | - | - | - | +| Binding | Status | Operating Systems | class | scalar type | struct | enumeration | string | basicarray | structarray | Callbacks | Error Message Propagation | Injection | API Documentation | +|:---------------:|:----------------------------------------------------------:|:-----------------:|:---------:|:-------------:|:-------------:|:-------------:|:-------------:|:----------:|:-----------:|:---------:|:---------:|:---------:|:---------:| +| C++ | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | + | +| C++ Dynamic | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | + | +| C | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | - | - | +| C Dynamic | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | - | - | +| Pascal | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | + | +| Python3 | ![](Documentation/images/Tick.png) complete (but not very pythonic) | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | - | +| Golang | ![](Documentation/images/Tick.png) mature | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | - | +| NodeJS | ![](Documentation/images/O.png) partial support | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | ? | ? | - | + | - | - | +| C# | ![](Documentation/images/O.png) experimental | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | - | - | - | + | - | - | +| Java | ![](Documentation/images/Tick.png) experimental | Win, Linux, MacOS | in,return | in,out,return | in,out,return | in,out,return | in,out,return | in,out | in,out | in | + | + | - | +| PHP | ![](Documentation/images/X.png) not implemented | Win, Linux, MacOS | - | - | - | - | - | - | - | - | - | - | - | #### Feature Matrix: Implementation Stubs | Implementation | Status | Operating Systems | class | scalar type | struct | enumeration | string | basicarray | structarray | Callbacks | Journaling | Error Message Propagation | Injection | diff --git a/Source/automaticcomponenttoolkit.go b/Source/automaticcomponenttoolkit.go index 755d54a4..864d486b 100644 --- a/Source/automaticcomponenttoolkit.go +++ b/Source/automaticcomponenttoolkit.go @@ -64,6 +64,7 @@ func createComponent(component ComponentDefinition, outfolderBase string, bindin outputFolder := path.Join(outfolderBase, component.NameSpace+"_component") outputFolderBindings := path.Join(outputFolder, "Bindings") outputFolderExamples := path.Join(outputFolder, "Examples") + outputFolderDocumentation := path.Join(outputFolder, "Documentations") outputFolderImplementations := path.Join(outputFolder, "Implementations") if bindingsDirectoryOverride != "" { @@ -176,6 +177,12 @@ func createComponent(component ComponentDefinition, outfolderBase string, bindin case "Cpp": { + outputFolderDocumentationCppImplicit := outputFolderDocumentation + "/Cpp" + err = os.MkdirAll(outputFolderDocumentationCppImplicit, os.ModePerm) + if err != nil { + log.Fatal(err) + } + outputFolderBindingCppImplicit := outputFolderBindings + "/Cpp" err = os.MkdirAll(outputFolderBindingCppImplicit, os.ModePerm) if err != nil { @@ -204,7 +211,7 @@ func createComponent(component ComponentDefinition, outfolderBase string, bindin } err = BuildBindingCppImplicit(component, outputFolderBindingCppImplicit, outputFolderExampleCppImplicit, - indentString, binding.ClassIdentifier) + outputFolderDocumentationCppImplicit, indentString, binding.ClassIdentifier) if err != nil { return err } diff --git a/Source/buildbindingccpp.go b/Source/buildbindingccpp.go index e248c588..fa9d36f4 100644 --- a/Source/buildbindingccpp.go +++ b/Source/buildbindingccpp.go @@ -115,7 +115,8 @@ func BuildBindingCExplicit(component ComponentDefinition, outputFolder string, o } // BuildBindingCppImplicit builds dynamic C++-bindings of a library's API in form of implicitly linked functions handles. -func BuildBindingCppImplicit(component ComponentDefinition, outputFolder string, outputFolderExample string, indentString string, ClassIdentifier string) error { +func BuildBindingCppImplicit(component ComponentDefinition, outputFolder string, outputFolderExample string, + outputFolderDocumentation string, indentString string, ClassIdentifier string) error { forceRecreation := false ExplicitLinking := false @@ -168,9 +169,17 @@ func BuildBindingCppImplicit(component ComponentDefinition, outputFolder string, log.Printf("Omitting recreation of C++-example CMakeLists-file \"%s\"", CPPCMake) } } + + err = BuildCCPPDocumentation(component, outputFolderDocumentation, ClassIdentifier) + if err != nil { + return err + } + + return nil } + func buildDynamicCCPPHeader(component ComponentDefinition, w LanguageWriter, NameSpace string, BaseName string, headerOnly bool, useCPPTypes bool) error { @@ -523,7 +532,7 @@ func buildDynamicCImplementation(component ComponentDefinition, w LanguageWriter return nil } -func writeDynamicCPPMethodDeclaration(method ComponentDefinitionMethod, w LanguageWriter, NameSpace string, ClassIdentifier string, ClassName string) error { +func getDynamicCPPMethodParameters(method ComponentDefinitionMethod, NameSpace string, ClassIdentifier string, ClassName string) (string, string, error) { parameters := "" returntype := "void" @@ -560,12 +569,19 @@ func writeDynamicCPPMethodDeclaration(method ComponentDefinitionMethod, w Langua case "return": returntype = getBindingCppParamType(param.ParamType, param.ParamClass, NameSpace, ClassIdentifier, false) default: - return fmt.Errorf("invalid method parameter passing \"%s\" for %s.%s(%s)", param.ParamPass, ClassName, method.MethodName, param.ParamName) + return "", "", fmt.Errorf("invalid method parameter passing \"%s\" for %s.%s(%s)", param.ParamPass, ClassName, method.MethodName, param.ParamName) } } - w.Writeln(" inline %s %s(%s);", returntype, method.MethodName, parameters) + return parameters, returntype, nil +} +func writeDynamicCPPMethodDeclaration(method ComponentDefinitionMethod, w LanguageWriter, NameSpace string, ClassIdentifier string, ClassName string) error { + parameters, returntype, err := getDynamicCPPMethodParameters(method, NameSpace, ClassIdentifier, ClassName) + if (err!= nil) { + return err + } + w.Writeln(" inline %s %s(%s);", returntype, method.MethodName, parameters) return nil } @@ -967,12 +983,12 @@ func writeCPPInputVector(w LanguageWriter, NameSpace string, ClassIdentifier str w.Writeln(" ") w.Writeln("public:") w.Writeln(" ") - w.Writeln(" C%sInputVector( const std::vector& vec)", ClassIdentifier) + w.Writeln(" C%sInputVector(const std::vector& vec)", ClassIdentifier) w.Writeln(" : m_data( vec.data() ), m_size( vec.size() )") w.Writeln(" {") w.Writeln(" }") w.Writeln(" ") - w.Writeln(" C%sInputVector( const T* in_data, size_t in_size)", ClassIdentifier) + w.Writeln(" C%sInputVector(const T* in_data, size_t in_size)", ClassIdentifier) w.Writeln(" : m_data( in_data ), m_size(in_size )") w.Writeln(" {") w.Writeln(" }") @@ -1089,6 +1105,20 @@ func getBindingCppVariableName(param ComponentDefinitionParam) string { } +func getCPPInheritanceSpecifier(component ComponentDefinition, class ComponentDefinitionClass, cppClassPrefix string, ClassIdentifier string) (string, string) { + cppParentClassName := "" + inheritanceSpecifier := "" + if !component.isBaseClass(class) { + if class.ParentClass == "" { + cppParentClassName = cppClassPrefix + ClassIdentifier + component.Global.BaseClassName + } else { + cppParentClassName = cppClassPrefix + ClassIdentifier+ class.ParentClass + } + inheritanceSpecifier = fmt.Sprintf(": public %s ", cppParentClassName) + } + return cppParentClassName, inheritanceSpecifier +} + func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace string, BaseName string, ClassIdentifier string, ExplicitLinking bool) error { useCPPTypes := true @@ -1389,16 +1419,7 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s class := component.Classes[i] cppClassName := cppClassPrefix + ClassIdentifier + class.ClassName - cppParentClassName := "" - inheritanceSpecifier := "" - if !component.isBaseClass(class) { - if class.ParentClass == "" { - cppParentClassName = cppClassPrefix + ClassIdentifier + component.Global.BaseClassName - } else { - cppParentClassName = cppClassPrefix + ClassIdentifier+ class.ParentClass - } - inheritanceSpecifier = fmt.Sprintf(": public %s ", cppParentClassName) - } + cppParentClassName, inheritanceSpecifier := getCPPInheritanceSpecifier(component, class, cppClassPrefix, ClassIdentifier) w.Writeln(" ") w.Writeln("/*************************************************************************************************************************") @@ -1630,7 +1651,7 @@ func BuildBindingCppExplicit(component ComponentDefinition, outputFolder string, } -func buildDynamicCppExample(componentdefinition ComponentDefinition, w LanguageWriter, outputFolder string, ClassIdentifier string, ExplicitLinking bool) error { +func buildDynamicCppExample(componentdefinition ComponentDefinition, w LanguageWriter, outputFolder string, ClassIdentifier string, ExplicitLinking bool) { NameSpace := componentdefinition.NameSpace BaseName := componentdefinition.BaseName @@ -1679,8 +1700,6 @@ func buildDynamicCppExample(componentdefinition ComponentDefinition, w LanguageW w.Writeln(" return 0;") w.Writeln("}") w.Writeln("") - - return nil } func buildCppDynamicExampleCMake(componentdefinition ComponentDefinition, w LanguageWriter, outputFolder string, outputFolderExample string, ExplicitLinking bool) error { diff --git a/Source/buildbindingccppdocumentation.go b/Source/buildbindingccppdocumentation.go new file mode 100644 index 00000000..2e6e4dad --- /dev/null +++ b/Source/buildbindingccppdocumentation.go @@ -0,0 +1,482 @@ +/*++ + +Copyright (C) 2019 Autodesk Inc. (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--*/ + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// buildbindingccppdocumentation.go.go +// functions to generate the Sphinx documentation of a library's C++-bindings +////////////////////////////////////////////////////////////////////////////////////////////////////// + +package main + +import ( + "fmt" + "log" + "path" +) + + +// BuildCCPPDocumentation builds the Sphinx documentation of a library's C++-bindings +func BuildCCPPDocumentation(component ComponentDefinition, outputFolder string, ClassIdentifier string) (error) { + BaseName := component.BaseName + + globalFileName := path.Join(outputFolder, BaseName + ".rst") + log.Printf("Creating \"%s\"", globalFileName) + globalDocFile, err := CreateLanguageFile(globalFileName, "\t") + if err != nil { + return err + } + err = buildCCPPDocumentationGlobal(component, globalDocFile, ClassIdentifier) + if err != nil { + return err + } + + typesFileName := path.Join(outputFolder, BaseName + "-types.rst") + log.Printf("Creating \"%s\"", typesFileName) + typesDocFile, err := CreateLanguageFile(typesFileName, "\t") + if err != nil { + return err + } + err = buildCCPPDocumentationTypes(component, typesDocFile, ClassIdentifier) + if err != nil { + return err + } + + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + classFileName := path.Join(outputFolder, BaseName + "_" + class.ClassName + ".rst") + log.Printf("Creating \"%s\"", classFileName) + classDocFile, err := CreateLanguageFile(classFileName, "\t") + if err != nil { + return err + } + err = buildCCPPDocumentationClass(component, classDocFile, class, ClassIdentifier) + if err != nil { + return err + } + + } + + + err = buildCCPPDocumentationExample(component, outputFolder, ClassIdentifier, true, "_dynamic") + if err != nil { + return err + } + err = buildCCPPDocumentationExample(component, outputFolder, ClassIdentifier, false, "_implicit") + if err != nil { + return err + } + + return nil +} + +func buildCCPPDocumentationExample(component ComponentDefinition, outputFolder string, ClassIdentifier string, ExplicitLinking bool, suffix string) error { + NameSpace := component.NameSpace + + DynamicCPPExample := path.Join(outputFolder, NameSpace +"_example"+suffix+".cpp") + log.Printf("Creating \"%s\"", DynamicCPPExample) + dyncppexamplefile, err := CreateLanguageFile(DynamicCPPExample, " ") + if err != nil { + return err + } + buildDynamicCppExample(component, dyncppexamplefile, outputFolder, ClassIdentifier, ExplicitLinking) + + DynamicCPPCMake := path.Join(outputFolder, "CMakeLists"+suffix+".txt") + log.Printf("Creating \"%s\"", DynamicCPPCMake) + dyncppcmake, err := CreateLanguageFile(DynamicCPPCMake, " ") + if err != nil { + return err + } + buildCppDynamicExampleCMake(component, dyncppcmake, outputFolder, outputFolder, ExplicitLinking) + return nil +} + +func writeCPPDocumentationFunctionPointer(component ComponentDefinition, w LanguageWriter, + functiontype ComponentDefinitionFunctionType) (error) { + + NameSpace := component.NameSpace + returnType := "void" + parameters := "" + + for j := 0; j < len(functiontype.Params); j++ { + param := functiontype.Params[j] + + cParamTypeName, err := getCPPParameterTypeName(param.ParamType, NameSpace, param.ParamClass); + if (err != nil) { + return err; + } + if (parameters != "") { + parameters = parameters + ", " + } + if (param.ParamPass == "in") { + parameters = parameters + cParamTypeName + } else { + parameters = parameters + cParamTypeName + "*" + } + } + w.Writeln(" .. cpp:type:: %s = %s(*)(%s)", functiontype.FunctionName, returnType, parameters) + w.Writeln(" ") + w.Writeln(" %s", functiontype.FunctionDescription) + w.Writeln(" ") + + for j := 0; j < len(functiontype.Params); j++ { + param := functiontype.Params[j] + + cParams, err := generateCCPPParameter(param, "", functiontype.FunctionName, NameSpace, true) + if (err != nil) { + return err; + } + for _, cParam := range cParams { + w.Writeln(" %s", cParam.ParamDocumentationLine); + } + } + w.Writeln(" ") + + return nil +} + + +func buildCCPPDocumentationGlobal(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) (error) { + + NameSpace := component.NameSpace + LibraryName := component.LibraryName + global := component.Global + + wrapperName := "C"+ClassIdentifier+"Wrapper" + + w.Writeln("") + w.Writeln("The wrapper class %s", wrapperName) + w.Writeln("===================================================================================") + w.Writeln("") + w.Writeln("") + w.Writeln(".. cpp:class:: %s::%s", NameSpace, wrapperName) + + w.Writeln("") + w.Writeln(" All types of %s reside in the namespace %s and all", LibraryName, NameSpace) + w.Writeln(" functionality of %s resides in %s::%s.", LibraryName, NameSpace, wrapperName) + w.Writeln("") + w.Writeln(" A suitable way to use %s::%s is as a singleton.", NameSpace, wrapperName) + w.Writeln("") + + + for j := 0; j < len(global.Methods); j++ { + method := global.Methods[j] + + parameters, returntype, err := getDynamicCPPMethodParameters(method, NameSpace, ClassIdentifier, "Wrapper") + if (err != nil) { + return err + } + w.Writeln(" .. cpp:function:: %s %s(%s)", returntype, method.MethodName, parameters) + w.Writeln(" ") + w.Writeln(" %s", method.MethodDescription) + w.Writeln(" ") + writeCPPDocumentationParameters(method, w, NameSpace) + w.Writeln(" ") + } + + w.Writeln(".. cpp:type:: std::shared_ptr<%s> %s::P%s%s", wrapperName, NameSpace, ClassIdentifier, "Wrapper") + w.Writeln(" ") + + // Load library functions + // Check error functions of the base class + + return nil +} + + +func writeCPPDocumentationParameters(method ComponentDefinitionMethod, w LanguageWriter, NameSpace string) { + for k := 0; k < len(method.Params); k++ { + param := method.Params[k] + variableName := getBindingCppVariableName(param) + if (param.ParamPass == "return") { + w.Writeln(" :returns: %s", param.ParamDescription ) + } else { + w.Writeln(" :param %s: %s ", variableName, param.ParamDescription) + } + } + w.Writeln("") +} + +func buildCCPPDocumentationClass(component ComponentDefinition, w LanguageWriter, class ComponentDefinitionClass, ClassIdentifier string) (error) { + + NameSpace := component.NameSpace + className := "C"+ClassIdentifier+class.ClassName + + w.Writeln("") + w.Writeln("%s", className) + w.Writeln("====================================================================================================") + w.Writeln("") + w.Writeln("") + + _, inheritanceSpecifier := getCPPInheritanceSpecifier(component, class, "C", ClassIdentifier) + + w.Writeln(".. cpp:class:: %s::%s %s", NameSpace, className, inheritanceSpecifier) + w.Writeln("") + w.Writeln(" %s", class.ClassDescription) + w.Writeln("") + w.Writeln("") + + w.Writeln("") + w.Writeln("") + for j := 0; j < len(class.Methods); j++ { + method := class.Methods[j] + + parameters, returntype, err := getDynamicCPPMethodParameters(method, NameSpace, ClassIdentifier, class.ClassName) + if (err != nil) { + return err + } + w.Writeln(" .. cpp:function:: %s %s(%s)", returntype, method.MethodName, parameters) + w.Writeln("") + w.Writeln(" %s", method.MethodDescription) + w.Writeln("") + writeCPPDocumentationParameters(method, w, NameSpace) + w.Writeln("") + } + + w.Writeln(".. cpp:type:: std::shared_ptr<%s> %s::P%s%s", className, NameSpace, ClassIdentifier, class.ClassName) + w.Writeln("") + w.Writeln(" Shared pointer to %s to easily allow reference counting.", className) + w.Writeln("") + + return nil +} + +func buildCCPPDocumentationException(component ComponentDefinition, w LanguageWriter) { + LibraryName := component.LibraryName + NameSpace := component.NameSpace + + ExceptionName := "E" + NameSpace + "Exception" + w.Writeln(" ") + w.Writeln("%s: The standard exception class of %s", ExceptionName, LibraryName) + w.Writeln("============================================================================================================================================================================================================") + w.Writeln(" ") + w.Writeln(" Errors in %s are reported as Exceptions. It is recommended to not throw these exceptions in your client code.", LibraryName) + w.Writeln(" ") + w.Writeln(" ") + w.Writeln(" .. cpp:class:: %s::%s", NameSpace, ExceptionName) + w.Writeln(" ") + w.Writeln(" .. cpp:function:: void %s::what() const noexcept", ExceptionName) + w.Writeln(" ") + w.Writeln(" Returns error message") + w.Writeln(" ") + w.Writeln(" :return: the error message of this exception") + w.Writeln(" ") + + + w.Writeln(" ") + w.Writeln(" .. cpp:function:: %sResult %s::getErrorCode() const noexcept", NameSpace, ExceptionName) + w.Writeln(" ") + w.Writeln(" Returns error code") + w.Writeln(" ") + w.Writeln(" :return: the error code of this exception") + w.Writeln(" ") +} + + +func buildCCPPDocumentationInputVector(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) { + LibraryName := component.LibraryName + NameSpace := component.NameSpace + + InputVector := "C" + ClassIdentifier + "InputVector" + w.Writeln(" ") + w.Writeln("%s: Adapter for passing arrays as input for functions", InputVector) + w.Writeln("===============================================================================================================================================================") + w.Writeln(" ") + w.Writeln(" Several functions of %s expect arrays of integral types or structs as input parameters.", LibraryName) + w.Writeln(" To not restrict the interface to, say, std::vector,") + w.Writeln(" and to have a more abstract interface than a location in memory and the number of elements to input to a function") + w.Writeln(" %s provides a templated adapter class to pass arrays as input for functions.", LibraryName) + w.Writeln(" ") + w.Writeln(" Usually, instances of %s are generated anonymously (or even implicitly) in the call to a function that expects an input array.", InputVector) + w.Writeln(" ") + w.Writeln(" ") + + + w.Writeln(" .. cpp:class:: template %s::%s", NameSpace, InputVector) + w.Writeln(" ") + w.Writeln(" .. cpp:function:: %s(const std::vector& vec)", InputVector) + w.Writeln(" ") + w.Writeln(" Constructs of a %s from a std::vector", InputVector) + w.Writeln(" ") + w.Writeln(" .. cpp:function:: %s(const T* in_data, size_t in_size)", InputVector) + w.Writeln(" ") + w.Writeln(" Constructs of a %s from a memory address and a given number of elements", InputVector) + w.Writeln(" ") + + w.Writeln(" .. cpp:function:: const T* %s::data() const", InputVector) + w.Writeln(" ") + w.Writeln(" returns the start address of the data captured by this %s", InputVector) + w.Writeln(" ") + + w.Writeln(" .. cpp:function:: size_t %s::size() const", InputVector) + w.Writeln(" ") + w.Writeln(" returns the number of elements captured by this %s", InputVector) + w.Writeln(" ") + w.Writeln(" ") +} + + +func buildCCPPDocumentationStructs(component ComponentDefinition, w LanguageWriter) (error) { + if len(component.Structs) == 0 { + return nil + } + + NameSpace := component.NameSpace + + w.Writeln("") + w.Writeln("Structs") + w.Writeln("--------------") + w.Writeln("") + w.Writeln(" All structs are defined as `packed`, i.e. with the") + w.Writeln(" ") + w.Writeln(" .. code-block:: c") + w.Writeln(" "); + w.Writeln(" #pragma pack (1)"); + w.Writeln(""); + + for i := 0; i < len(component.Structs); i++ { + structinfo := component.Structs[i]; + w.Writeln(" .. cpp:struct:: s%s", structinfo.Name); + w.Writeln(" "); + // w.Writeln(" %s", structinfo.Description); + // w.Writeln(" "); + for j := 0; j < len(structinfo.Members); j++ { + member := structinfo.Members[j]; + arraysuffix := ""; + if (member.Rows > 0) { + if (member.Columns > 0) { + arraysuffix = fmt.Sprintf ("[%d][%d]", member.Columns, member.Rows) + } else { + arraysuffix = fmt.Sprintf ("[%d]",member.Rows) + } + } + memberLine, err:= getCPPMemberLine(member, NameSpace, arraysuffix, structinfo.Name, "") + if (err!=nil) { + return err + } + w.Writeln(" .. cpp:member:: %s", memberLine) + w.Writeln(" "); + } + w.Writeln(""); + } + + return nil +} + +func buildCCPPDocumentationSimpleTypes(component ComponentDefinition, w LanguageWriter) { + NameSpace := component.NameSpace + + w.Writeln("Simple types") + w.Writeln("--------------") + w.Writeln("") + types := []string{"uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64"} + for _, _type := range types { + w.Writeln(" .. cpp:type:: %s_t %s_%s", _type, NameSpace, _type) + w.Writeln(" ") + } + w.Writeln(" .. cpp:type:: float %s_single", NameSpace) + w.Writeln(" ") + w.Writeln(" .. cpp:type:: double %s_double", NameSpace) + w.Writeln(" ") + w.Writeln(" .. cpp:type:: %s_pvoid = void*", NameSpace) + w.Writeln(" ") + w.Writeln(" .. cpp:type:: %sResult = %s_int32", NameSpace, NameSpace) + w.Writeln(" ") + w.Writeln(" ") +} + +func buildCCPPDocumentationEnums(component ComponentDefinition, w LanguageWriter) { + if len(component.Enums) == 0 { + return + } + + NameSpace := component.NameSpace + + w.Writeln("") + w.Writeln("Enumerations") + w.Writeln("--------------") + w.Writeln("") + for i := 0; i < len(component.Enums); i++ { + enum := component.Enums[i] + w.Writeln(" .. cpp:enum-class:: e%s : %s_int32", enum.Name, NameSpace); + w.Writeln(" ") + // w.Writeln(" %s", enum.Description) + // w.Writeln(" ") + for j := 0; j < len(enum.Options); j++ { + option := enum.Options[j]; + w.Writeln(" .. cpp:enumerator:: %s = %d", option.Name, option.Value); + } + w.Writeln(" "); + } +} + +func buildCCPPDocumentationFunctionTypes(component ComponentDefinition, w LanguageWriter) (error) { + if len(component.Functions) == 0 { + return nil + } + w.Writeln("") + w.Writeln("Function types") + w.Writeln("---------------") + w.Writeln("") + w.Writeln("") + for i := 0; i < len(component.Functions); i++ { + functiontype := component.Functions[i] + err := writeCPPDocumentationFunctionPointer(component, w, functiontype) + if (err!=nil) { + return err + } + } + w.Writeln("") + + return nil +} + +func buildCCPPDocumentationTypes(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) (error) { + LibraryName := component.LibraryName + + w.Writeln("") + w.Writeln("Types used in %s", LibraryName) + w.Writeln("==========================================================================================================") + w.Writeln("") + w.Writeln("") + + buildCCPPDocumentationSimpleTypes(component, w) + buildCCPPDocumentationEnums(component, w) + + err := buildCCPPDocumentationStructs(component, w) + if (err!=nil) { + return err + } + err = buildCCPPDocumentationFunctionTypes(component, w) + if (err!=nil) { + return err + } + buildCCPPDocumentationException(component, w) + buildCCPPDocumentationInputVector(component, w, ClassIdentifier) + + return nil +} diff --git a/Source/componentdefinition.go b/Source/componentdefinition.go index 2d4fc84c..27b0109b 100644 --- a/Source/componentdefinition.go +++ b/Source/componentdefinition.go @@ -1001,7 +1001,7 @@ func (component *ComponentDefinition) CheckComponentDefinition() (error) { // CheckHeaderSpecialFunction checks a special function of the header against their required definitions -func CheckHeaderSpecialFunction (method ComponentDefinitionMethod, global ComponentDefinitionGlobal) (int, error) { +func CheckHeaderSpecialFunction(method ComponentDefinitionMethod, global ComponentDefinitionGlobal) (int, error) { if (global.ReleaseMethod == "") { return eSpecialMethodNone, errors.New ("No release method specified"); diff --git a/Source/languagec.go b/Source/languagec.go index d375cdec..8d1ab5c3 100644 --- a/Source/languagec.go +++ b/Source/languagec.go @@ -483,7 +483,7 @@ func buildCCPPStructs(component ComponentDefinition, w LanguageWriter, NameSpace } var memberLine string if (useCPPTypes) { - memberLine, err= getCPPMemberLine(member, NameSpace, arraysuffix, structinfo.Name) + memberLine, err= getCPPMemberLine(member, NameSpace, arraysuffix, structinfo.Name, ";") } else { memberLine, err= getCMemberLine(member, NameSpace, arraysuffix, structinfo.Name) } @@ -677,6 +677,7 @@ type CParameter struct { ParamType string ParamName string ParamComment string + ParamDocumentationLine string } @@ -700,61 +701,73 @@ func generateCCPPParameter(param ComponentDefinitionParam, className string, met cParams[0].ParamType = cParamTypeName; cParams[0].ParamName = "n" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, param.ParamDescription); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) case "bool": cParams[0].ParamType = cParamTypeName; cParams[0].ParamName = "b" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, param.ParamDescription); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) case "single": cParams[0].ParamType = cParamTypeName; cParams[0].ParamName = "f" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, param.ParamDescription); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) case "double": cParams[0].ParamType = cParamTypeName; cParams[0].ParamName = "d" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, param.ParamDescription); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) case "pointer": cParams[0].ParamType = cParamTypeName; cParams[0].ParamName = "p" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, param.ParamDescription); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) case "string": cParams[0].ParamType = "const " + cParamTypeName; cParams[0].ParamName = "p" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, param.ParamDescription); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) case "enum": cParams[0].ParamType = cParamTypeName; cParams[0].ParamName = "e" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, param.ParamDescription); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) case "struct": cParams[0].ParamType = "const " + cParamTypeName; cParams[0].ParamName = "p" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, param.ParamDescription); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) case "basicarray", "structarray": cParams = make([]CParameter,2) cParams[0].ParamType = fmt.Sprintf ("%s_uint64", NameSpace); cParams[0].ParamName = "n" + param.ParamName + "BufferSize"; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - Number of elements in buffer", cParams[0].ParamName); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) cParams[1].ParamType = "const " + cParamTypeName; cParams[1].ParamName = "p" + param.ParamName + "Buffer"; cParams[1].ParamComment = fmt.Sprintf("* @param[in] %s - %s buffer of %s", cParams[1].ParamName, param.ParamClass, param.ParamDescription); + cParams[1].ParamDocumentationLine = fmt.Sprintf(":param %s: buffer of %s", cParams[0].ParamName, param.ParamDescription) case "class", "optionalclass": cParams[0].ParamType = cParamTypeName; cParams[0].ParamName = "p" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, param.ParamDescription); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) case "functiontype": cParams[0].ParamType = cParamTypeName; cParams[0].ParamName = "p" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, param.ParamDescription); + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) default: return nil, fmt.Errorf ("invalid method parameter type \"%s\" for %s.%s (%s)", param.ParamType, className, methodName, param.ParamName); @@ -768,45 +781,75 @@ func generateCCPPParameter(param ComponentDefinitionParam, className string, met cParams[0].ParamType = cParamTypeName + " *"; cParams[0].ParamName = "p" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[out] %s - %s", cParams[0].ParamName, param.ParamDescription); + if ("out" == param.ParamPass) { + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) + } else { + cParams[0].ParamDocumentationLine = fmt.Sprintf(":return: %s", param.ParamDescription) + } case "struct": cParams[0].ParamType = cParamTypeName; cParams[0].ParamName = "p" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[out] %s - %s", cParams[0].ParamName, param.ParamDescription); + if ("out" == param.ParamPass) { + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) + } else { + cParams[0].ParamDocumentationLine = fmt.Sprintf(":return: %s", param.ParamDescription) + } case "basicarray", "structarray": cParams = make([]CParameter,3) cParams[0].ParamType = fmt.Sprintf("const %s_uint64", NameSpace) cParams[0].ParamName = "n" + param.ParamName + "BufferSize"; - cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - Number of elements in buffer", cParams[0].ParamName); + paramComment0 := "Number of elements in buffer" + cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, paramComment0); cParams[1].ParamType = fmt.Sprintf("%s_uint64*", NameSpace) cParams[1].ParamName = "p" + param.ParamName + "NeededCount"; - cParams[1].ParamComment = fmt.Sprintf("* @param[out] %s - will be filled with the count of the written elements, or needed buffer size.", cParams[1].ParamName); + paramComment1 := "will be filled with the count of the written elements, or needed buffer size." + cParams[1].ParamComment = fmt.Sprintf("* @param[out] %s - %s", cParams[1].ParamName, paramComment1); cParams[2].ParamType = cParamTypeName; cParams[2].ParamName = "p" + param.ParamName + "Buffer"; - cParams[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s buffer of %s", cParams[2].ParamName, param.ParamClass, param.ParamDescription); + paramComment2 := "buffer of " + param.ParamDescription + cParams[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s %s", cParams[2].ParamName, param.ParamClass, paramComment2); + + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, paramComment0) + cParams[1].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[1].ParamName, paramComment1) + cParams[2].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[2].ParamName, paramComment2) case "string": cParams = make([]CParameter,3) cParams[0].ParamType = fmt.Sprintf("const %s_uint32", NameSpace) cParams[0].ParamName = "n" + param.ParamName + "BufferSize"; - cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", cParams[0].ParamName); + paramComment0 := "size of the buffer (including trailing 0)" + cParams[0].ParamComment = fmt.Sprintf("* @param[in] %s - %s", cParams[0].ParamName, paramComment0); cParams[1].ParamType = fmt.Sprintf("%s_uint32*", NameSpace) cParams[1].ParamName = "p" + param.ParamName + "NeededChars"; - cParams[1].ParamComment = fmt.Sprintf("* @param[out] %s - will be filled with the count of the written bytes, or needed buffer size.", cParams[1].ParamName); + paramComment1 := "will be filled with the count of the written bytes, or needed buffer size." + cParams[1].ParamComment = fmt.Sprintf("* @param[out] %s - %s", cParams[1].ParamName, paramComment1); cParams[2].ParamType = cParamTypeName; cParams[2].ParamName = "p" + param.ParamName + "Buffer"; - cParams[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s buffer of %s, may be NULL", cParams[2].ParamName, param.ParamClass, param.ParamDescription); + paramComment2 := fmt.Sprintf("buffer of %s, may be NULL", param.ParamDescription) + cParams[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s %s", cParams[2].ParamName, param.ParamClass, paramComment2); + + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, paramComment0) + cParams[1].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[1].ParamName, paramComment1) + cParams[2].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[2].ParamName, paramComment2) case "class", "optionalclass": cParams[0].ParamType = cParamTypeName + " *"; cParams[0].ParamName = "p" + param.ParamName; cParams[0].ParamComment = fmt.Sprintf("* @param[out] %s - %s", cParams[0].ParamName, param.ParamDescription); + if ("out" == param.ParamPass) { + cParams[0].ParamDocumentationLine = fmt.Sprintf(":param %s: %s", cParams[0].ParamName, param.ParamDescription) + } else { + cParams[0].ParamDocumentationLine = fmt.Sprintf(":return: %s", param.ParamDescription) + } + default: return nil, fmt.Errorf ("invalid method parameter type \"%s\" for %s.%s (%s)", param.ParamType, className, methodName, param.ParamName); } diff --git a/Source/languagecpp.go b/Source/languagecpp.go index b2aa71e8..576e94dd 100644 --- a/Source/languagecpp.go +++ b/Source/languagecpp.go @@ -51,16 +51,16 @@ func CreateCPPTypesHeader(component ComponentDefinition, CTypesHeaderName string return err; } -func getCPPMemberLine(member ComponentDefinitionMember, NameSpace string, arraysuffix string, structName string) (string, error) { +func getCPPMemberLine(member ComponentDefinitionMember, NameSpace string, arraysuffix string, structName string, endCharacter string) (string, error) { switch (member.Type) { case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "bool", "pointer": typeName, err := getCPPParameterTypeName(member.Type, NameSpace, "") if (err != nil) { return "", err } - return fmt.Sprintf("%s m_%s%s;", typeName, member.Name, arraysuffix), nil + return fmt.Sprintf("%s m_%s%s%s", typeName, member.Name, arraysuffix, endCharacter), nil case "enum": - return fmt.Sprintf("e%s m_%s%s;", member.Class, member.Name, arraysuffix), nil + return fmt.Sprintf("e%s m_%s%s%s", member.Class, member.Name, arraysuffix, endCharacter), nil default: return "", fmt.Errorf ("it is not possible for struct %s to contain a %s member", structName, member.Type);