From 8fc642382f55d3770ff734a6b2faa2f095fc774f Mon Sep 17 00:00:00 2001 From: uck Date: Fri, 25 Apr 2025 16:56:18 +0200 Subject: [PATCH 01/11] Add c++ binding/implementation thread safety options --- Source/automaticcomponenttoolkit.go | 7 +- Source/buildbindingccpp.go | 104 +++++++++++++++++++++++----- Source/buildimplementationcpp.go | 73 +++++++++++++++++-- Source/componentdefinition.go | 97 +++++++++++++++++++++++++- 4 files changed, 256 insertions(+), 25 deletions(-) diff --git a/Source/automaticcomponenttoolkit.go b/Source/automaticcomponenttoolkit.go index 56310ca3..aa9553bf 100644 --- a/Source/automaticcomponenttoolkit.go +++ b/Source/automaticcomponenttoolkit.go @@ -584,7 +584,7 @@ func printUsageInfo() { } func main() { - ACTVersion := "1.8.0-develop" + ACTVersion := "1.8.1-develop" fmt.Fprintln(os.Stdout, "Automatic Component Toolkit v"+ACTVersion) if len(os.Args) < 2 { printUsageInfo() @@ -685,6 +685,11 @@ func main() { log.Fatal(err) } + addExtraBaseClassMethods := component.addExtraBaseClassMethods() + if addExtraBaseClassMethods != nil { + log.Printf("%s", *addExtraBaseClassMethods) + } + if mode == eACTModeDiff { log.Printf("Loading Component Description File to compare to") componentB, err := ReadComponentDefinition(diffFile, ACTVersion) diff --git a/Source/buildbindingccpp.go b/Source/buildbindingccpp.go index c54855f8..ecd5da83 100644 --- a/Source/buildbindingccpp.go +++ b/Source/buildbindingccpp.go @@ -591,7 +591,7 @@ func writeDynamicCPPMethodDeclaration(method ComponentDefinitionMethod, w Langua } func writeDynamicCPPMethod(method ComponentDefinitionMethod, w LanguageWriter, NameSpace string, ClassIdentifier string, ClassName string, - implementationLines []string, isGlobal bool, includeComments bool, doNotThrow bool, useCPPTypes bool, ExplicitLinking bool, forWASM bool) error { + implementationLines []string, isGlobal bool, includeComments bool, doNotThrow bool, useCPPTypes bool, ExplicitLinking bool, forWASM bool, multiThreadedEnv bool, classThreadSafetyOption ThreadSafetyOption) error { WASMPrefix := "" WASMCast := "" @@ -616,6 +616,14 @@ func writeDynamicCPPMethod(method ComponentDefinitionMethod, w LanguageWriter, N checkErrorCodeEnd := ")" makeSharedParameter := "" + if multiThreadedEnv && !isGlobal { + if classThreadSafetyOption == eThreadSafetyStrict { + checkErrorCodeEnd = ", true)" + } else { + checkErrorCodeEnd = ", false)" + } + } + if isGlobal { if ExplicitLinking { CMethodName = fmt.Sprintf("m_WrapperTable.m_%s", method.MethodName) @@ -858,6 +866,12 @@ func writeDynamicCPPMethod(method ComponentDefinitionMethod, w LanguageWriter, N initCallParameters = initCallParameters + initCallParameter } + addThreadSafeCalls := multiThreadedEnv && !isGlobal && ((classThreadSafetyOption == eThreadSafetySoft && requiresInitCall) || classThreadSafetyOption == eThreadSafetyStrict) + + if addThreadSafeCalls { + checkErrorCodeEnd = ", true)" + } + w.Writeln(" ") if includeComments { w.Writeln(" /**") @@ -876,6 +890,11 @@ func writeDynamicCPPMethod(method ComponentDefinitionMethod, w LanguageWriter, N w.Writeln(" {") w.Writelns(" ", definitionCodeLines) + + if addThreadSafeCalls { + w.Writeln(" %s();", getLockInstanceMethodName()) + } + if requiresInitCall { w.Writeln(" %s%s(%s)%s;", checkErrorCodeBegin, CMethodName, initCallParameters, checkErrorCodeEnd) } @@ -888,6 +907,10 @@ func writeDynamicCPPMethod(method ComponentDefinitionMethod, w LanguageWriter, N w.Writelns(" ", implementationLines) } + if addThreadSafeCalls { + w.Writeln(" %s();", getUnlockInstanceMethodName()) + } + if len(returnCodeLines) > 0 { w.Writeln(" ") w.Writelns(" ", returnCodeLines) @@ -905,11 +928,20 @@ func writeDynamicCppBaseClassMethods(component ComponentDefinition, baseClass Co w.Writeln(" /* Handle to Instance in library*/") w.Writeln(" %sHandle m_pHandle;", NameSpace) w.Writeln("") - w.Writeln(" /* Checks for an Error code and raises Exceptions */") - w.Writeln(" void CheckError(%sResult nResult)", NameSpace) + if component.isMultiThreadedEnv() { + w.Writeln(" /* Checks for an Error code, raises Exceptions and unlocks library object*/") + w.Writeln(" void CheckError(%sResult nResult, bool unlockIfError)", NameSpace) + } else { + w.Writeln(" /* Checks for an Error code and raises Exceptions */") + w.Writeln(" void CheckError(%sResult nResult)", NameSpace) + } w.Writeln(" {") w.Writeln(" if (m_pWrapper != nullptr)") - w.Writeln(" m_pWrapper->CheckError(this, nResult);") + if component.isMultiThreadedEnv() { + w.Writeln(" m_pWrapper->CheckError(this, nResult, unlockIfError);") + } else { + w.Writeln(" m_pWrapper->CheckError(this, nResult);") + } w.Writeln(" }") w.Writeln("public:") w.Writeln(" /**") @@ -1390,19 +1422,42 @@ func writeClassDeclarations(w LanguageWriter, component ComponentDefinition, cpp w.Writeln(" {") w.Writeln(" }") w.Writeln(" ") + + for _, method := range class.Methods { + err := writeDynamicCPPMethodDeclaration(method, w, NameSpace, ClassIdentifier, cppClassName) + if err != nil { + return err + } + } + } else { err := writeDynamicCppBaseClassMethods(component, baseClass, w, NameSpace, BaseName, cppClassPrefix, ClassIdentifier) if err != nil { return err } - } - for _, method := range class.Methods { - err := writeDynamicCPPMethodDeclaration(method, w, NameSpace, ClassIdentifier, cppClassName) - if err != nil { - return err + extraBaseClassMethods := []ComponentDefinitionMethod{} + for _, method := range class.Methods { + if method.isExtraBaseClassmethod() { + extraBaseClassMethods = append(extraBaseClassMethods, method) + } else { + err := writeDynamicCPPMethodDeclaration(method, w, NameSpace, ClassIdentifier, cppClassName) + if err != nil { + return err + } + } + } + + w.Writeln(" ") + w.Writeln("protected:") + for _, method := range extraBaseClassMethods { + err := writeDynamicCPPMethodDeclaration(method, w, NameSpace, ClassIdentifier, cppClassName) + if err != nil { + return err + } } } + w.Writeln("};") } return nil @@ -1450,13 +1505,22 @@ func writePolymorphicFactoryImplementation(w LanguageWriter, component Component w.Writeln("}") } -func writeCheckErrorImplementation(w LanguageWriter, ErrorMethodName string, ClassIdentifier string, cppBaseClassName string, NameSpace string) { - w.Writeln(" inline void C%sWrapper::CheckError(%s * pBaseClass, %sResult nResult)", ClassIdentifier, cppBaseClassName, NameSpace) +func writeCheckErrorImplementation(w LanguageWriter, ErrorMethodName string, ClassIdentifier string, cppBaseClassName string, NameSpace string, multiThreadedEnv bool) { + if multiThreadedEnv { + w.Writeln(" inline void C%sWrapper::CheckError(%s * pBaseClass, %sResult nResult, bool unlockIfError)", ClassIdentifier, cppBaseClassName, NameSpace) + } else { + w.Writeln(" inline void C%sWrapper::CheckError(%s * pBaseClass, %sResult nResult)", ClassIdentifier, cppBaseClassName, NameSpace) + } w.Writeln(" {") w.Writeln(" if (nResult != 0) {") w.Writeln(" std::string sErrorMessage;") w.Writeln(" if (pBaseClass != nullptr) {") w.Writeln(" %s(pBaseClass, sErrorMessage);", ErrorMethodName) + if multiThreadedEnv { + w.Writeln(" if (unlockIfError) {") + w.Writeln(" pBaseClass->%s();", getUnlockInstanceMethodName()) + w.Writeln(" }") + } w.Writeln(" }") w.Writeln(" throw E%sException(nResult, sErrorMessage);", NameSpace) w.Writeln(" }") @@ -1539,7 +1603,11 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s writeWrapperLifeTimeHandling(w, cppClassPrefix, ClassIdentifier, ExplicitLinking) w.Writeln(" ") - w.Writeln(" inline void CheckError(%s * pBaseClass, %sResult nResult);", cppBaseClassName, NameSpace) + if component.isMultiThreadedEnv() { + w.Writeln(" inline void CheckError(%s * pBaseClass, %sResult nResult, bool unlockIfError = false);", cppBaseClassName, NameSpace) + } else { + w.Writeln(" inline void CheckError(%s * pBaseClass, %sResult nResult);", cppBaseClassName, NameSpace) + } w.Writeln("") for j := 0; j < len(global.Methods); j++ { @@ -1622,7 +1690,7 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s implementationLines = append(implementationLines, fmt.Sprintf(" throw E%sException(%s_ERROR_COULDNOTLOADLIBRARY, \"Unknown namespace \" + %s);", NameSpace, strings.ToUpper(NameSpace), sParamName)) } - err = writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, "Wrapper", implementationLines, true, true, false, useCPPTypes, ExplicitLinking, false) + err = writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, "Wrapper", implementationLines, true, true, false, useCPPTypes, ExplicitLinking, false, component.isMultiThreadedEnv(), eThreadSafetyNone) if err != nil { return err } @@ -1630,7 +1698,7 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s } w.Writeln("") - writeCheckErrorImplementation(w, component.Global.ErrorMethod, ClassIdentifier, cppBaseClassName, NameSpace) + writeCheckErrorImplementation(w, component.Global.ErrorMethod, ClassIdentifier, cppBaseClassName, NameSpace, component.isMultiThreadedEnv()) w.Writeln("") if ExplicitLinking { @@ -1648,7 +1716,7 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s w.Writeln(" */") for j := 0; j < len(class.Methods); j++ { method := class.Methods[j] - err := writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, class.ClassName, make([]string, 0), false, true, false, useCPPTypes, ExplicitLinking, false) + err := writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, class.ClassName, make([]string, 0), false, true, false, useCPPTypes, ExplicitLinking, false, component.isMultiThreadedEnv(), class.eThreadSafetyOption()) if err != nil { return err } @@ -2217,14 +2285,14 @@ func buildCppwasmGuestHeader(component ComponentDefinition, w LanguageWriter, Na implementationLines = append(implementationLines, fmt.Sprintf(" throw E%sException(%s_ERROR_COULDNOTLOADLIBRARY, \"Unknown namespace \" + %s);", NameSpace, strings.ToUpper(NameSpace), sParamName)) } - err = writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, "Wrapper", implementationLines, true, true, false, useCPPTypes, ExplicitLinking, true) + err = writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, "Wrapper", implementationLines, true, true, false, useCPPTypes, ExplicitLinking, true, false, eThreadSafetyNone) if err != nil { return err } } w.Writeln("") - writeCheckErrorImplementation(w, component.Global.ErrorMethod, ClassIdentifier, cppBaseClassName, NameSpace) + writeCheckErrorImplementation(w, component.Global.ErrorMethod, ClassIdentifier, cppBaseClassName, NameSpace, false) w.Writeln("") for i := 0; i < len(component.Classes); i++ { @@ -2236,7 +2304,7 @@ func buildCppwasmGuestHeader(component ComponentDefinition, w LanguageWriter, Na w.Writeln(" */") for j := 0; j < len(class.Methods); j++ { method := class.Methods[j] - err := writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, class.ClassName, make([]string, 0), false, true, false, useCPPTypes, ExplicitLinking, true) + err := writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, class.ClassName, make([]string, 0), false, true, false, useCPPTypes, ExplicitLinking, true, false, eThreadSafetyNone) if err != nil { return err } diff --git a/Source/buildimplementationcpp.go b/Source/buildimplementationcpp.go index 16acaee5..9b5d8046 100644 --- a/Source/buildimplementationcpp.go +++ b/Source/buildimplementationcpp.go @@ -314,6 +314,39 @@ func writeSharedPtrTemplate(component ComponentDefinition, w LanguageWriter, Cla w.Writeln("") } +func getWithMutexClassName(ClassIdentifier string, ClassName string) (string) { + return ClassIdentifier + ClassName + "WithMutex" +} + +func writeBaseInterfaceWithMutexClass(baseClass ComponentDefinitionClass, w LanguageWriter, ClassIdentifier string) { + baseClassName:= "I" + baseClass.ClassName + className := "I" + getWithMutexClassName(ClassIdentifier, baseClass.ClassName) + lockInstanceMethod := LockInstanceMethod() + unlockInstanceMethod := UnlockInstanceMethod() + + w.Writeln("") + w.Writeln("/**") + w.Writeln(" Definition of a class with mutex for %s", baseClassName) + w.Writeln("*/") + w.Writeln("class %s : public virtual %s", className, baseClassName) + w.Writeln("{") + w.Writeln("private:") + w.Writeln(" std::mutex m_mutex;") + w.Writeln("public:") + w.Writeln("") + w.Writeln(" /**") + w.Writeln(" * %s::%s - %s", className, lockInstanceMethod.MethodName, lockInstanceMethod.MethodDescription) + w.Writeln(" */") + w.Writeln(" virtual void %s() { m_mutex.lock(); }", lockInstanceMethod.MethodName) + w.Writeln("") + w.Writeln(" /**") + w.Writeln(" * %s::%s - %s", className, unlockInstanceMethod.MethodName, unlockInstanceMethod.MethodDescription) + w.Writeln(" */") + w.Writeln(" virtual void %s() { m_mutex.unlock();}", unlockInstanceMethod.MethodName) + w.Writeln("};") + w.Writeln("") +} + func writeCPPClassInterface(component ComponentDefinition, class ComponentDefinitionClass, w LanguageWriter, NameSpace string, NameSpaceImplementation string, ClassIdentifier string, BaseName string) (error) { w.Writeln("") w.Writeln("/*************************************************************************************************************************") @@ -324,7 +357,11 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini if (!component.isBaseClass(class)) { parentClassString = " : public virtual " if (class.ParentClass == "") { - parentClassString += fmt.Sprintf("I%s%s ", ClassIdentifier, component.Global.BaseClassName) + if (class.isThreadSafe()) { + parentClassString += fmt.Sprintf("I%s ", getWithMutexClassName(ClassIdentifier, component.Global.BaseClassName)) + } else { + parentClassString += fmt.Sprintf("I%s%s ", ClassIdentifier, component.Global.BaseClassName) + } } else { parentClassString += fmt.Sprintf("I%s%s ", ClassIdentifier, class.ParentClass) } @@ -334,9 +371,8 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini w.Writeln("class %s%s{", classInterfaceName, parentClassString) if (component.isStringOutBaseClass(class)) { - w.Writeln("private:") - w.Writeln(" std::unique_ptr m_ParameterCache;") - + w.Writeln("protected:") + w.Writeln(" std::unique_ptr m_ParameterCache;") } w.Writeln("public:") @@ -411,7 +447,23 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini w.Writeln(" {") w.Writeln(" return m_ParameterCache.get();") w.Writeln(" }") - w.Writeln("") + w.Writeln("") + + if component.isMultiThreadedEnv() { + lockInstanceMethod := LockInstanceMethod() + w.Writeln(" /**") + w.Writeln(" * %s::%s - %s", classInterfaceName, lockInstanceMethod.MethodName, lockInstanceMethod.MethodDescription) + w.Writeln(" */") + w.Writeln(" virtual void %s() {}", lockInstanceMethod.MethodName) + w.Writeln("") + + unlockInstanceMethod := UnlockInstanceMethod() + w.Writeln(" /**") + w.Writeln(" * %s::%s - %s", classInterfaceName, unlockInstanceMethod.MethodName, unlockInstanceMethod.MethodDescription) + w.Writeln(" */") + w.Writeln(" virtual void %s() {}", unlockInstanceMethod.MethodName) + w.Writeln("") + } } if (component.isBaseClass(class)) { @@ -440,6 +492,11 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini if method.MethodName == component.Global.ClassTypeIdMethod { continue } + + if component.isBaseClass(class) && method.isExtraBaseClassmethod() { + continue + } + methodstring, _, err := buildCPPInterfaceMethodDeclaration(method, class.ClassName, NameSpace, ClassIdentifier, BaseName, w.IndentString, false, true, true) if err != nil { return err @@ -452,6 +509,9 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini if component.isBaseClass(class) { writeSharedPtrTemplate(component, w, ClassIdentifier) + if component.isMultiThreadedEnv() { + writeBaseInterfaceWithMutexClass(class, w, ClassIdentifier) + } } w.Writeln("") @@ -535,6 +595,9 @@ func buildCPPInterfaces(component ComponentDefinition, w LanguageWriter, NameSpa w.Writeln("#include ") w.Writeln("#include ") + if component.isMultiThreadedEnv() { + w.Writeln("#include ") + } w.Writeln("") w.Writeln("#include \"%s_types.hpp\"", BaseName) w.Writeln("") diff --git a/Source/componentdefinition.go b/Source/componentdefinition.go index 24be9903..54561f10 100644 --- a/Source/componentdefinition.go +++ b/Source/componentdefinition.go @@ -62,6 +62,13 @@ const ( eSpecialMethodBuildinfo = 9 ) +type ThreadSafetyOption int +const ( + eThreadSafetyNone = 0 + eThreadSafetySoft = 1 + eThreadSafetyStrict = 2 +) + // ComponentDefinitionParam definition of a method parameter used in the component's API type ComponentDefinitionParam struct { ComponentDiffableElement @@ -90,6 +97,7 @@ type ComponentDefinitionClass struct { ClassName string `xml:"name,attr"` ClassDescription string `xml:"description,attr"` ParentClass string `xml:"parent,attr"` + ThreadSafetyOption string `xml:"threadsafetyoption,attr"` Methods []ComponentDefinitionMethod `xml:"method"` } @@ -345,6 +353,26 @@ func ReadComponentDefinition(FileName string, ACTVersion string) (ComponentDefin return component, nil } +func (component *ComponentDefinition) addExtraBaseClassMethods() *string { + if component.isMultiThreadedEnv() { + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + if component.isBaseClass(class) { + msg := fmt.Sprintf("Mutli threaded implementation detected, adding %s, %s", getLockInstanceMethodName(), getUnlockInstanceMethodName()) + class.Methods = append(class.Methods, LockInstanceMethod(), UnlockInstanceMethod()) + component.Classes[i] = class + return &msg + } + } + } + + return nil +} + +func (method *ComponentDefinitionMethod) isExtraBaseClassmethod() bool { + return method.MethodName == getLockInstanceMethodName() || method.MethodName == getUnlockInstanceMethodName() +} + func getIndentationString(str string) string { if str == "tabs" { return "\t"; @@ -522,6 +550,9 @@ func (component *ComponentDefinition) checkClasses() (error) { for i := 0; i < len(classes); i++ { class := classes[i]; classTypeHash, _ := class.classTypeId(component.NameSpace); + if component.isBaseClass(class) && class.isThreadSafe() { + return fmt.Errorf("thread safety option for base class can't be enabled") + } if !nameIsValidIdentifier(class.ClassName) { return fmt.Errorf ("invalid class name \"%s\"", class.ClassName); } @@ -531,6 +562,9 @@ func (component *ComponentDefinition) checkClasses() (error) { if len(class.ClassDescription) > 0 && !descriptionIsValid(class.ClassDescription) { return fmt.Errorf ("invalid class description \"%s\" in class \"%s\"", class.ClassDescription, class.ClassName); } + if len(class.ThreadSafetyOption) > 0 && !threadSafetyOptionIsValid(class.ThreadSafetyOption) { + return fmt.Errorf("invalid class thread safety option \"%s\" in class \"%s\"", class.ThreadSafetyOption, class.ClassName) + } collision, hashExists := classTypeIdIndex[classTypeHash] if hashExists { return fmt.Errorf ("Classes \"%s\" and \"%s\" have a collision in their Class Type Id. Change class name.", classes[collision].ClassName, class.ClassName); @@ -568,7 +602,7 @@ func (component *ComponentDefinition) checkClasses() (error) { return nil } -func (component *ComponentDefinition) checkFunctionTypes() ( error) { +func (component *ComponentDefinition) checkFunctionTypes() (error) { functions := component.Functions var functionNameList = &component.NameMapsLookup.functionTypeMap @@ -796,6 +830,14 @@ func descriptionIsValid(description string) bool { return false; } +func threadSafetyOptionIsValid(threadSafetyOption string) bool { + switch threadSafetyOption { + case "none", "strict", "soft": + return true + } + return false +} + func isScalarType(typeStr string) bool { switch (typeStr) { case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "bool", "single", "double", "pointer": @@ -1272,6 +1314,32 @@ func (component *ComponentDefinition) isBaseClass(class ComponentDefinitionClass return class.ClassName == component.Global.BaseClassName } +func getLockInstanceMethodName() string { + return "_lockInstance" +} + +// LockInstanceMethod returns the xml definition of the LockInstanceMethod +func LockInstanceMethod() ComponentDefinitionMethod { + var method ComponentDefinitionMethod + source := fmt.Sprintf(` + `, getLockInstanceMethodName()) + xml.Unmarshal([]byte(source), &method) + return method +} + +func getUnlockInstanceMethodName() string { + return "_unlockInstance" +} + +// UnlockInstanceMethod returns the xml definition of the UnlockInstanceMethod +func UnlockInstanceMethod() ComponentDefinitionMethod { + var method ComponentDefinitionMethod + source := fmt.Sprintf(` + `, getUnlockInstanceMethodName()) + xml.Unmarshal([]byte(source), &method) + return method +} + func (component *ComponentDefinition) baseClass() (ComponentDefinitionClass) { for i := 0; i < len(component.Classes); i++ { if (component.isBaseClass(component.Classes[i])) { @@ -1363,6 +1431,33 @@ func (method *ComponentDefinitionMethod) getArrayOutParameters() ([]string) { return outParameters; } +func (class *ComponentDefinitionClass) eThreadSafetyOption() ThreadSafetyOption { + switch class.ThreadSafetyOption { + case "strict": + return eThreadSafetyStrict + case "soft": + return eThreadSafetySoft + case "none": + return eThreadSafetyNone + } + return eThreadSafetyNone +} + +func (class *ComponentDefinitionClass) isThreadSafe() bool { + return class.eThreadSafetyOption() != eThreadSafetyNone +} + +func (component *ComponentDefinition) isMultiThreadedEnv() bool { + classes := component.Classes + for i := 0; i < len(classes); i++ { + class := classes[i] + if class.isThreadSafe() { + return true + } + } + + return false +} func (class *ComponentDefinitionClass) countMaxOutParameters() (uint32) { From e5be6e68ef4a94b87703a0b0dd2eaf97859c2261 Mon Sep 17 00:00:00 2001 From: uck Date: Fri, 25 Apr 2025 16:56:18 +0200 Subject: [PATCH 02/11] Add c++ binding/implementation thread safety options --- Source/automaticcomponenttoolkit.go | 7 +- Source/buildbindingccpp.go | 104 +++++++++++++++++++++++----- Source/buildimplementationcpp.go | 73 +++++++++++++++++-- Source/componentdefinition.go | 97 +++++++++++++++++++++++++- 4 files changed, 256 insertions(+), 25 deletions(-) diff --git a/Source/automaticcomponenttoolkit.go b/Source/automaticcomponenttoolkit.go index b84f469d..4c720689 100644 --- a/Source/automaticcomponenttoolkit.go +++ b/Source/automaticcomponenttoolkit.go @@ -585,7 +585,7 @@ func printUsageInfo() { } func main() { - ACTVersion := "1.8.0-develop" + ACTVersion := "1.8.1-develop" fmt.Fprintln(os.Stdout, "Automatic Component Toolkit v"+ACTVersion) if len(os.Args) < 2 { printUsageInfo() @@ -690,6 +690,11 @@ func main() { log.Fatal(err) } + addExtraBaseClassMethods := component.addExtraBaseClassMethods() + if addExtraBaseClassMethods != nil { + log.Printf("%s", *addExtraBaseClassMethods) + } + if mode == eACTModeDiff { log.Printf("Loading Component Description File to compare to") componentB, err := ReadComponentDefinition(diffFile, ACTVersion) diff --git a/Source/buildbindingccpp.go b/Source/buildbindingccpp.go index c54855f8..ecd5da83 100644 --- a/Source/buildbindingccpp.go +++ b/Source/buildbindingccpp.go @@ -591,7 +591,7 @@ func writeDynamicCPPMethodDeclaration(method ComponentDefinitionMethod, w Langua } func writeDynamicCPPMethod(method ComponentDefinitionMethod, w LanguageWriter, NameSpace string, ClassIdentifier string, ClassName string, - implementationLines []string, isGlobal bool, includeComments bool, doNotThrow bool, useCPPTypes bool, ExplicitLinking bool, forWASM bool) error { + implementationLines []string, isGlobal bool, includeComments bool, doNotThrow bool, useCPPTypes bool, ExplicitLinking bool, forWASM bool, multiThreadedEnv bool, classThreadSafetyOption ThreadSafetyOption) error { WASMPrefix := "" WASMCast := "" @@ -616,6 +616,14 @@ func writeDynamicCPPMethod(method ComponentDefinitionMethod, w LanguageWriter, N checkErrorCodeEnd := ")" makeSharedParameter := "" + if multiThreadedEnv && !isGlobal { + if classThreadSafetyOption == eThreadSafetyStrict { + checkErrorCodeEnd = ", true)" + } else { + checkErrorCodeEnd = ", false)" + } + } + if isGlobal { if ExplicitLinking { CMethodName = fmt.Sprintf("m_WrapperTable.m_%s", method.MethodName) @@ -858,6 +866,12 @@ func writeDynamicCPPMethod(method ComponentDefinitionMethod, w LanguageWriter, N initCallParameters = initCallParameters + initCallParameter } + addThreadSafeCalls := multiThreadedEnv && !isGlobal && ((classThreadSafetyOption == eThreadSafetySoft && requiresInitCall) || classThreadSafetyOption == eThreadSafetyStrict) + + if addThreadSafeCalls { + checkErrorCodeEnd = ", true)" + } + w.Writeln(" ") if includeComments { w.Writeln(" /**") @@ -876,6 +890,11 @@ func writeDynamicCPPMethod(method ComponentDefinitionMethod, w LanguageWriter, N w.Writeln(" {") w.Writelns(" ", definitionCodeLines) + + if addThreadSafeCalls { + w.Writeln(" %s();", getLockInstanceMethodName()) + } + if requiresInitCall { w.Writeln(" %s%s(%s)%s;", checkErrorCodeBegin, CMethodName, initCallParameters, checkErrorCodeEnd) } @@ -888,6 +907,10 @@ func writeDynamicCPPMethod(method ComponentDefinitionMethod, w LanguageWriter, N w.Writelns(" ", implementationLines) } + if addThreadSafeCalls { + w.Writeln(" %s();", getUnlockInstanceMethodName()) + } + if len(returnCodeLines) > 0 { w.Writeln(" ") w.Writelns(" ", returnCodeLines) @@ -905,11 +928,20 @@ func writeDynamicCppBaseClassMethods(component ComponentDefinition, baseClass Co w.Writeln(" /* Handle to Instance in library*/") w.Writeln(" %sHandle m_pHandle;", NameSpace) w.Writeln("") - w.Writeln(" /* Checks for an Error code and raises Exceptions */") - w.Writeln(" void CheckError(%sResult nResult)", NameSpace) + if component.isMultiThreadedEnv() { + w.Writeln(" /* Checks for an Error code, raises Exceptions and unlocks library object*/") + w.Writeln(" void CheckError(%sResult nResult, bool unlockIfError)", NameSpace) + } else { + w.Writeln(" /* Checks for an Error code and raises Exceptions */") + w.Writeln(" void CheckError(%sResult nResult)", NameSpace) + } w.Writeln(" {") w.Writeln(" if (m_pWrapper != nullptr)") - w.Writeln(" m_pWrapper->CheckError(this, nResult);") + if component.isMultiThreadedEnv() { + w.Writeln(" m_pWrapper->CheckError(this, nResult, unlockIfError);") + } else { + w.Writeln(" m_pWrapper->CheckError(this, nResult);") + } w.Writeln(" }") w.Writeln("public:") w.Writeln(" /**") @@ -1390,19 +1422,42 @@ func writeClassDeclarations(w LanguageWriter, component ComponentDefinition, cpp w.Writeln(" {") w.Writeln(" }") w.Writeln(" ") + + for _, method := range class.Methods { + err := writeDynamicCPPMethodDeclaration(method, w, NameSpace, ClassIdentifier, cppClassName) + if err != nil { + return err + } + } + } else { err := writeDynamicCppBaseClassMethods(component, baseClass, w, NameSpace, BaseName, cppClassPrefix, ClassIdentifier) if err != nil { return err } - } - for _, method := range class.Methods { - err := writeDynamicCPPMethodDeclaration(method, w, NameSpace, ClassIdentifier, cppClassName) - if err != nil { - return err + extraBaseClassMethods := []ComponentDefinitionMethod{} + for _, method := range class.Methods { + if method.isExtraBaseClassmethod() { + extraBaseClassMethods = append(extraBaseClassMethods, method) + } else { + err := writeDynamicCPPMethodDeclaration(method, w, NameSpace, ClassIdentifier, cppClassName) + if err != nil { + return err + } + } + } + + w.Writeln(" ") + w.Writeln("protected:") + for _, method := range extraBaseClassMethods { + err := writeDynamicCPPMethodDeclaration(method, w, NameSpace, ClassIdentifier, cppClassName) + if err != nil { + return err + } } } + w.Writeln("};") } return nil @@ -1450,13 +1505,22 @@ func writePolymorphicFactoryImplementation(w LanguageWriter, component Component w.Writeln("}") } -func writeCheckErrorImplementation(w LanguageWriter, ErrorMethodName string, ClassIdentifier string, cppBaseClassName string, NameSpace string) { - w.Writeln(" inline void C%sWrapper::CheckError(%s * pBaseClass, %sResult nResult)", ClassIdentifier, cppBaseClassName, NameSpace) +func writeCheckErrorImplementation(w LanguageWriter, ErrorMethodName string, ClassIdentifier string, cppBaseClassName string, NameSpace string, multiThreadedEnv bool) { + if multiThreadedEnv { + w.Writeln(" inline void C%sWrapper::CheckError(%s * pBaseClass, %sResult nResult, bool unlockIfError)", ClassIdentifier, cppBaseClassName, NameSpace) + } else { + w.Writeln(" inline void C%sWrapper::CheckError(%s * pBaseClass, %sResult nResult)", ClassIdentifier, cppBaseClassName, NameSpace) + } w.Writeln(" {") w.Writeln(" if (nResult != 0) {") w.Writeln(" std::string sErrorMessage;") w.Writeln(" if (pBaseClass != nullptr) {") w.Writeln(" %s(pBaseClass, sErrorMessage);", ErrorMethodName) + if multiThreadedEnv { + w.Writeln(" if (unlockIfError) {") + w.Writeln(" pBaseClass->%s();", getUnlockInstanceMethodName()) + w.Writeln(" }") + } w.Writeln(" }") w.Writeln(" throw E%sException(nResult, sErrorMessage);", NameSpace) w.Writeln(" }") @@ -1539,7 +1603,11 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s writeWrapperLifeTimeHandling(w, cppClassPrefix, ClassIdentifier, ExplicitLinking) w.Writeln(" ") - w.Writeln(" inline void CheckError(%s * pBaseClass, %sResult nResult);", cppBaseClassName, NameSpace) + if component.isMultiThreadedEnv() { + w.Writeln(" inline void CheckError(%s * pBaseClass, %sResult nResult, bool unlockIfError = false);", cppBaseClassName, NameSpace) + } else { + w.Writeln(" inline void CheckError(%s * pBaseClass, %sResult nResult);", cppBaseClassName, NameSpace) + } w.Writeln("") for j := 0; j < len(global.Methods); j++ { @@ -1622,7 +1690,7 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s implementationLines = append(implementationLines, fmt.Sprintf(" throw E%sException(%s_ERROR_COULDNOTLOADLIBRARY, \"Unknown namespace \" + %s);", NameSpace, strings.ToUpper(NameSpace), sParamName)) } - err = writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, "Wrapper", implementationLines, true, true, false, useCPPTypes, ExplicitLinking, false) + err = writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, "Wrapper", implementationLines, true, true, false, useCPPTypes, ExplicitLinking, false, component.isMultiThreadedEnv(), eThreadSafetyNone) if err != nil { return err } @@ -1630,7 +1698,7 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s } w.Writeln("") - writeCheckErrorImplementation(w, component.Global.ErrorMethod, ClassIdentifier, cppBaseClassName, NameSpace) + writeCheckErrorImplementation(w, component.Global.ErrorMethod, ClassIdentifier, cppBaseClassName, NameSpace, component.isMultiThreadedEnv()) w.Writeln("") if ExplicitLinking { @@ -1648,7 +1716,7 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s w.Writeln(" */") for j := 0; j < len(class.Methods); j++ { method := class.Methods[j] - err := writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, class.ClassName, make([]string, 0), false, true, false, useCPPTypes, ExplicitLinking, false) + err := writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, class.ClassName, make([]string, 0), false, true, false, useCPPTypes, ExplicitLinking, false, component.isMultiThreadedEnv(), class.eThreadSafetyOption()) if err != nil { return err } @@ -2217,14 +2285,14 @@ func buildCppwasmGuestHeader(component ComponentDefinition, w LanguageWriter, Na implementationLines = append(implementationLines, fmt.Sprintf(" throw E%sException(%s_ERROR_COULDNOTLOADLIBRARY, \"Unknown namespace \" + %s);", NameSpace, strings.ToUpper(NameSpace), sParamName)) } - err = writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, "Wrapper", implementationLines, true, true, false, useCPPTypes, ExplicitLinking, true) + err = writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, "Wrapper", implementationLines, true, true, false, useCPPTypes, ExplicitLinking, true, false, eThreadSafetyNone) if err != nil { return err } } w.Writeln("") - writeCheckErrorImplementation(w, component.Global.ErrorMethod, ClassIdentifier, cppBaseClassName, NameSpace) + writeCheckErrorImplementation(w, component.Global.ErrorMethod, ClassIdentifier, cppBaseClassName, NameSpace, false) w.Writeln("") for i := 0; i < len(component.Classes); i++ { @@ -2236,7 +2304,7 @@ func buildCppwasmGuestHeader(component ComponentDefinition, w LanguageWriter, Na w.Writeln(" */") for j := 0; j < len(class.Methods); j++ { method := class.Methods[j] - err := writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, class.ClassName, make([]string, 0), false, true, false, useCPPTypes, ExplicitLinking, true) + err := writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, class.ClassName, make([]string, 0), false, true, false, useCPPTypes, ExplicitLinking, true, false, eThreadSafetyNone) if err != nil { return err } diff --git a/Source/buildimplementationcpp.go b/Source/buildimplementationcpp.go index 504b93cf..ae753e74 100644 --- a/Source/buildimplementationcpp.go +++ b/Source/buildimplementationcpp.go @@ -314,6 +314,39 @@ func writeSharedPtrTemplate(component ComponentDefinition, w LanguageWriter, Cla w.Writeln("") } +func getWithMutexClassName(ClassIdentifier string, ClassName string) (string) { + return ClassIdentifier + ClassName + "WithMutex" +} + +func writeBaseInterfaceWithMutexClass(baseClass ComponentDefinitionClass, w LanguageWriter, ClassIdentifier string) { + baseClassName:= "I" + baseClass.ClassName + className := "I" + getWithMutexClassName(ClassIdentifier, baseClass.ClassName) + lockInstanceMethod := LockInstanceMethod() + unlockInstanceMethod := UnlockInstanceMethod() + + w.Writeln("") + w.Writeln("/**") + w.Writeln(" Definition of a class with mutex for %s", baseClassName) + w.Writeln("*/") + w.Writeln("class %s : public virtual %s", className, baseClassName) + w.Writeln("{") + w.Writeln("private:") + w.Writeln(" std::mutex m_mutex;") + w.Writeln("public:") + w.Writeln("") + w.Writeln(" /**") + w.Writeln(" * %s::%s - %s", className, lockInstanceMethod.MethodName, lockInstanceMethod.MethodDescription) + w.Writeln(" */") + w.Writeln(" virtual void %s() { m_mutex.lock(); }", lockInstanceMethod.MethodName) + w.Writeln("") + w.Writeln(" /**") + w.Writeln(" * %s::%s - %s", className, unlockInstanceMethod.MethodName, unlockInstanceMethod.MethodDescription) + w.Writeln(" */") + w.Writeln(" virtual void %s() { m_mutex.unlock();}", unlockInstanceMethod.MethodName) + w.Writeln("};") + w.Writeln("") +} + func writeCPPClassInterface(component ComponentDefinition, class ComponentDefinitionClass, w LanguageWriter, NameSpace string, NameSpaceImplementation string, ClassIdentifier string, BaseName string) (error) { w.Writeln("") w.Writeln("/*************************************************************************************************************************") @@ -324,7 +357,11 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini if (!component.isBaseClass(class)) { parentClassString = " : public virtual " if (class.ParentClass == "") { - parentClassString += fmt.Sprintf("I%s%s ", ClassIdentifier, component.Global.BaseClassName) + if (class.isThreadSafe()) { + parentClassString += fmt.Sprintf("I%s ", getWithMutexClassName(ClassIdentifier, component.Global.BaseClassName)) + } else { + parentClassString += fmt.Sprintf("I%s%s ", ClassIdentifier, component.Global.BaseClassName) + } } else { parentClassString += fmt.Sprintf("I%s%s ", ClassIdentifier, class.ParentClass) } @@ -334,9 +371,8 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini w.Writeln("class %s%s{", classInterfaceName, parentClassString) if (component.isStringOutBaseClass(class)) { - w.Writeln("private:") - w.Writeln(" std::unique_ptr m_ParameterCache;") - + w.Writeln("protected:") + w.Writeln(" std::unique_ptr m_ParameterCache;") } w.Writeln("public:") @@ -411,7 +447,23 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini w.Writeln(" {") w.Writeln(" return m_ParameterCache.get();") w.Writeln(" }") - w.Writeln("") + w.Writeln("") + + if component.isMultiThreadedEnv() { + lockInstanceMethod := LockInstanceMethod() + w.Writeln(" /**") + w.Writeln(" * %s::%s - %s", classInterfaceName, lockInstanceMethod.MethodName, lockInstanceMethod.MethodDescription) + w.Writeln(" */") + w.Writeln(" virtual void %s() {}", lockInstanceMethod.MethodName) + w.Writeln("") + + unlockInstanceMethod := UnlockInstanceMethod() + w.Writeln(" /**") + w.Writeln(" * %s::%s - %s", classInterfaceName, unlockInstanceMethod.MethodName, unlockInstanceMethod.MethodDescription) + w.Writeln(" */") + w.Writeln(" virtual void %s() {}", unlockInstanceMethod.MethodName) + w.Writeln("") + } } if (component.isBaseClass(class)) { @@ -440,6 +492,11 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini if method.MethodName == component.Global.ClassTypeIdMethod { continue } + + if component.isBaseClass(class) && method.isExtraBaseClassmethod() { + continue + } + methodstring, _, err := buildCPPInterfaceMethodDeclaration(method, class.ClassName, NameSpace, ClassIdentifier, BaseName, w.IndentString, false, true, true) if err != nil { return err @@ -452,6 +509,9 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini if component.isBaseClass(class) { writeSharedPtrTemplate(component, w, ClassIdentifier) + if component.isMultiThreadedEnv() { + writeBaseInterfaceWithMutexClass(class, w, ClassIdentifier) + } } w.Writeln("") @@ -535,6 +595,9 @@ func buildCPPInterfaces(component ComponentDefinition, w LanguageWriter, NameSpa w.Writeln("#include ") w.Writeln("#include ") + if component.isMultiThreadedEnv() { + w.Writeln("#include ") + } w.Writeln("") w.Writeln("#include \"%s_types.hpp\"", BaseName) w.Writeln("") diff --git a/Source/componentdefinition.go b/Source/componentdefinition.go index 24be9903..54561f10 100644 --- a/Source/componentdefinition.go +++ b/Source/componentdefinition.go @@ -62,6 +62,13 @@ const ( eSpecialMethodBuildinfo = 9 ) +type ThreadSafetyOption int +const ( + eThreadSafetyNone = 0 + eThreadSafetySoft = 1 + eThreadSafetyStrict = 2 +) + // ComponentDefinitionParam definition of a method parameter used in the component's API type ComponentDefinitionParam struct { ComponentDiffableElement @@ -90,6 +97,7 @@ type ComponentDefinitionClass struct { ClassName string `xml:"name,attr"` ClassDescription string `xml:"description,attr"` ParentClass string `xml:"parent,attr"` + ThreadSafetyOption string `xml:"threadsafetyoption,attr"` Methods []ComponentDefinitionMethod `xml:"method"` } @@ -345,6 +353,26 @@ func ReadComponentDefinition(FileName string, ACTVersion string) (ComponentDefin return component, nil } +func (component *ComponentDefinition) addExtraBaseClassMethods() *string { + if component.isMultiThreadedEnv() { + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + if component.isBaseClass(class) { + msg := fmt.Sprintf("Mutli threaded implementation detected, adding %s, %s", getLockInstanceMethodName(), getUnlockInstanceMethodName()) + class.Methods = append(class.Methods, LockInstanceMethod(), UnlockInstanceMethod()) + component.Classes[i] = class + return &msg + } + } + } + + return nil +} + +func (method *ComponentDefinitionMethod) isExtraBaseClassmethod() bool { + return method.MethodName == getLockInstanceMethodName() || method.MethodName == getUnlockInstanceMethodName() +} + func getIndentationString(str string) string { if str == "tabs" { return "\t"; @@ -522,6 +550,9 @@ func (component *ComponentDefinition) checkClasses() (error) { for i := 0; i < len(classes); i++ { class := classes[i]; classTypeHash, _ := class.classTypeId(component.NameSpace); + if component.isBaseClass(class) && class.isThreadSafe() { + return fmt.Errorf("thread safety option for base class can't be enabled") + } if !nameIsValidIdentifier(class.ClassName) { return fmt.Errorf ("invalid class name \"%s\"", class.ClassName); } @@ -531,6 +562,9 @@ func (component *ComponentDefinition) checkClasses() (error) { if len(class.ClassDescription) > 0 && !descriptionIsValid(class.ClassDescription) { return fmt.Errorf ("invalid class description \"%s\" in class \"%s\"", class.ClassDescription, class.ClassName); } + if len(class.ThreadSafetyOption) > 0 && !threadSafetyOptionIsValid(class.ThreadSafetyOption) { + return fmt.Errorf("invalid class thread safety option \"%s\" in class \"%s\"", class.ThreadSafetyOption, class.ClassName) + } collision, hashExists := classTypeIdIndex[classTypeHash] if hashExists { return fmt.Errorf ("Classes \"%s\" and \"%s\" have a collision in their Class Type Id. Change class name.", classes[collision].ClassName, class.ClassName); @@ -568,7 +602,7 @@ func (component *ComponentDefinition) checkClasses() (error) { return nil } -func (component *ComponentDefinition) checkFunctionTypes() ( error) { +func (component *ComponentDefinition) checkFunctionTypes() (error) { functions := component.Functions var functionNameList = &component.NameMapsLookup.functionTypeMap @@ -796,6 +830,14 @@ func descriptionIsValid(description string) bool { return false; } +func threadSafetyOptionIsValid(threadSafetyOption string) bool { + switch threadSafetyOption { + case "none", "strict", "soft": + return true + } + return false +} + func isScalarType(typeStr string) bool { switch (typeStr) { case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "bool", "single", "double", "pointer": @@ -1272,6 +1314,32 @@ func (component *ComponentDefinition) isBaseClass(class ComponentDefinitionClass return class.ClassName == component.Global.BaseClassName } +func getLockInstanceMethodName() string { + return "_lockInstance" +} + +// LockInstanceMethod returns the xml definition of the LockInstanceMethod +func LockInstanceMethod() ComponentDefinitionMethod { + var method ComponentDefinitionMethod + source := fmt.Sprintf(` + `, getLockInstanceMethodName()) + xml.Unmarshal([]byte(source), &method) + return method +} + +func getUnlockInstanceMethodName() string { + return "_unlockInstance" +} + +// UnlockInstanceMethod returns the xml definition of the UnlockInstanceMethod +func UnlockInstanceMethod() ComponentDefinitionMethod { + var method ComponentDefinitionMethod + source := fmt.Sprintf(` + `, getUnlockInstanceMethodName()) + xml.Unmarshal([]byte(source), &method) + return method +} + func (component *ComponentDefinition) baseClass() (ComponentDefinitionClass) { for i := 0; i < len(component.Classes); i++ { if (component.isBaseClass(component.Classes[i])) { @@ -1363,6 +1431,33 @@ func (method *ComponentDefinitionMethod) getArrayOutParameters() ([]string) { return outParameters; } +func (class *ComponentDefinitionClass) eThreadSafetyOption() ThreadSafetyOption { + switch class.ThreadSafetyOption { + case "strict": + return eThreadSafetyStrict + case "soft": + return eThreadSafetySoft + case "none": + return eThreadSafetyNone + } + return eThreadSafetyNone +} + +func (class *ComponentDefinitionClass) isThreadSafe() bool { + return class.eThreadSafetyOption() != eThreadSafetyNone +} + +func (component *ComponentDefinition) isMultiThreadedEnv() bool { + classes := component.Classes + for i := 0; i < len(classes); i++ { + class := classes[i] + if class.isThreadSafe() { + return true + } + } + + return false +} func (class *ComponentDefinitionClass) countMaxOutParameters() (uint32) { From bcb9f88f7691d9969f1112d244a4bc97b6fdd669 Mon Sep 17 00:00:00 2001 From: uck Date: Tue, 10 Jun 2025 18:29:39 +0200 Subject: [PATCH 03/11] Dont generate lock/unlock instance in cbase class --- Source/buildimplementationcpp.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/buildimplementationcpp.go b/Source/buildimplementationcpp.go index 9b5d8046..d97a37d9 100644 --- a/Source/buildimplementationcpp.go +++ b/Source/buildimplementationcpp.go @@ -1351,6 +1351,11 @@ func buildCPPStubClass(component ComponentDefinition, class ComponentDefinitionC for j := 0; j < len(class.Methods); j++ { method := class.Methods[j] + + if method.isExtraBaseClassmethod() { + continue; + } + methodstring, implementationdeclaration, err := buildCPPInterfaceMethodDeclaration(method, class.ClassName, NameSpace, ClassIdentifier, BaseName, stubimplw.IndentString, false, false, false) if err != nil { return err From db6520b2d4d14ebe1b59e6e297107ba50e939ee0 Mon Sep 17 00:00:00 2001 From: uck Date: Tue, 10 Jun 2025 18:31:57 +0200 Subject: [PATCH 04/11] Add thread safety option example --- Examples/ThreadSafety/.gitignore | 1 + .../CppDynamic/libthreadsafe_dynamic.h | 212 +++ .../CppDynamic/libthreadsafe_dynamic.hpp | 980 ++++++++++++++ .../CppDynamic/libthreadsafe_types.hpp | 123 ++ .../Examples/CppDynamic/CMakeLists.txt | 26 + .../CppDynamic/LibThreadSafe_example.cpp | 56 + .../Implementations/Cpp/CMakeLists.txt | 46 + .../Cpp/Interfaces/libthreadsafe_abi.hpp | 205 +++ .../libthreadsafe_interfaceexception.cpp | 45 + .../libthreadsafe_interfaceexception.hpp | 60 + .../Interfaces/libthreadsafe_interfaces.hpp | 406 ++++++ .../libthreadsafe_interfacewrapper.cpp | 604 +++++++++ .../Cpp/Interfaces/libthreadsafe_types.hpp | 123 ++ .../Cpp/Stub/libthreadsafe.cpp | 68 + .../Cpp/Stub/libthreadsafe_base.cpp | 60 + .../Cpp/Stub/libthreadsafe_base.hpp | 73 + .../Stub/libthreadsafe_softstringreturner.cpp | 33 + .../Stub/libthreadsafe_softstringreturner.hpp | 72 + .../libthreadsafe_strictstringreturner.cpp | 33 + .../libthreadsafe_strictstringreturner.hpp | 72 + .../Cpp/Stub/libthreadsafe_stringreturner.cpp | 33 + .../Cpp/Stub/libthreadsafe_stringreturner.hpp | 72 + .../LibThreadSafe_component/license.txt | 5 + Examples/ThreadSafety/Readme.md | 1185 +++++++++++++++++ Examples/ThreadSafety/threadSafeLibrary.xml | 87 ++ 25 files changed, 4680 insertions(+) create mode 100644 Examples/ThreadSafety/.gitignore create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_dynamic.h create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_dynamic.hpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_types.hpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/CMakeLists.txt create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/CMakeLists.txt create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_abi.hpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaceexception.cpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaceexception.hpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaces.hpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfacewrapper.cpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_types.hpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe.cpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_base.cpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_base.hpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.cpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.hpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.cpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.hpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.cpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.hpp create mode 100644 Examples/ThreadSafety/LibThreadSafe_component/license.txt create mode 100644 Examples/ThreadSafety/Readme.md create mode 100644 Examples/ThreadSafety/threadSafeLibrary.xml diff --git a/Examples/ThreadSafety/.gitignore b/Examples/ThreadSafety/.gitignore new file mode 100644 index 00000000..2241d4af --- /dev/null +++ b/Examples/ThreadSafety/.gitignore @@ -0,0 +1 @@ +LibPrimes_component diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_dynamic.h b/Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_dynamic.h new file mode 100644 index 00000000..bd577fd8 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_dynamic.h @@ -0,0 +1,212 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++-Header file in order to allow an easy + use of Thread-safe library + +Interface version: 1.0.0 + +*/ + +#ifndef __LIBTHREADSAFE_DYNAMICHEADER_CPPTYPES +#define __LIBTHREADSAFE_DYNAMICHEADER_CPPTYPES + +#include "libthreadsafe_types.hpp" + + + +/************************************************************************************************************************* + Class definition for Base +**************************************************************************************************************************/ + +/** +* Get Class Type Id +* +* @param[in] pBase - Base instance. +* @param[out] pClassTypeId - Class type as a 64 bits integer +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeBase_ClassTypeIdPtr) (LibThreadSafe_Base pBase, LibThreadSafe_uint64 * pClassTypeId); + +/** +* If thread safety for class in library is enabled it should lock object for calling thread +* +* @param[in] pBase - Base instance. +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeBase__lockInstancePtr) (LibThreadSafe_Base pBase); + +/** +* If thread safety for class in library is enabled it should unlock object for other threads +* +* @param[in] pBase - Base instance. +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeBase__unlockInstancePtr) (LibThreadSafe_Base pBase); + +/************************************************************************************************************************* + Class definition for StringReturner +**************************************************************************************************************************/ + +/** +* Returns a string +* +* @param[in] pStringReturner - StringReturner instance. +* @param[in] nValueBufferSize - size of the buffer (including trailing 0) +* @param[out] pValueNeededChars - will be filled with the count of the written bytes, or needed buffer size. +* @param[out] pValueBuffer - buffer of , may be NULL +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeStringReturner_GetStringPtr) (LibThreadSafe_StringReturner pStringReturner, const LibThreadSafe_uint32 nValueBufferSize, LibThreadSafe_uint32* pValueNeededChars, char * pValueBuffer); + +/** +* Function that may crash when called from different threads at the same time +* +* @param[in] pStringReturner - StringReturner instance. +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeStringReturner_ThreadSafetyCheckPtr) (LibThreadSafe_StringReturner pStringReturner); + +/************************************************************************************************************************* + Class definition for SoftStringReturner +**************************************************************************************************************************/ + +/** +* Returns a string +* +* @param[in] pSoftStringReturner - SoftStringReturner instance. +* @param[in] nValueBufferSize - size of the buffer (including trailing 0) +* @param[out] pValueNeededChars - will be filled with the count of the written bytes, or needed buffer size. +* @param[out] pValueBuffer - buffer of , may be NULL +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeSoftStringReturner_GetStringPtr) (LibThreadSafe_SoftStringReturner pSoftStringReturner, const LibThreadSafe_uint32 nValueBufferSize, LibThreadSafe_uint32* pValueNeededChars, char * pValueBuffer); + +/** +* Function that may crash when called from different threads at the same time +* +* @param[in] pSoftStringReturner - SoftStringReturner instance. +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeSoftStringReturner_ThreadSafetyCheckPtr) (LibThreadSafe_SoftStringReturner pSoftStringReturner); + +/************************************************************************************************************************* + Class definition for StrictStringReturner +**************************************************************************************************************************/ + +/** +* Returns a string +* +* @param[in] pStrictStringReturner - StrictStringReturner instance. +* @param[in] nValueBufferSize - size of the buffer (including trailing 0) +* @param[out] pValueNeededChars - will be filled with the count of the written bytes, or needed buffer size. +* @param[out] pValueBuffer - buffer of , may be NULL +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeStrictStringReturner_GetStringPtr) (LibThreadSafe_StrictStringReturner pStrictStringReturner, const LibThreadSafe_uint32 nValueBufferSize, LibThreadSafe_uint32* pValueNeededChars, char * pValueBuffer); + +/** +* Function that shouldn't crash when called from different threads at the same time +* +* @param[in] pStrictStringReturner - StrictStringReturner instance. +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeStrictStringReturner_ThreadSafetyCheckPtr) (LibThreadSafe_StrictStringReturner pStrictStringReturner); + +/************************************************************************************************************************* + Global functions +**************************************************************************************************************************/ + +/** +* retrieves the binary version of this library. +* +* @param[out] pMajor - returns the major version of this library +* @param[out] pMinor - returns the minor version of this library +* @param[out] pMicro - returns the micro version of this library +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeGetVersionPtr) (LibThreadSafe_uint32 * pMajor, LibThreadSafe_uint32 * pMinor, LibThreadSafe_uint32 * pMicro); + +/** +* Returns the last error recorded on this object +* +* @param[in] pInstance - Instance Handle +* @param[in] nErrorMessageBufferSize - size of the buffer (including trailing 0) +* @param[out] pErrorMessageNeededChars - will be filled with the count of the written bytes, or needed buffer size. +* @param[out] pErrorMessageBuffer - buffer of Message of the last error, may be NULL +* @param[out] pHasError - Is there a last error to query +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeGetLastErrorPtr) (LibThreadSafe_Base pInstance, const LibThreadSafe_uint32 nErrorMessageBufferSize, LibThreadSafe_uint32* pErrorMessageNeededChars, char * pErrorMessageBuffer, bool * pHasError); + +/** +* Acquire shared ownership of an Instance +* +* @param[in] pInstance - Instance Handle +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeAcquireInstancePtr) (LibThreadSafe_Base pInstance); + +/** +* Releases shared ownership of an Instance +* +* @param[in] pInstance - Instance Handle +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeReleaseInstancePtr) (LibThreadSafe_Base pInstance); + +/** +* Creates a new StringReturner instance +* +* @param[out] pInstance - New StringReturner instance +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeCreateStringReturnerPtr) (LibThreadSafe_StringReturner * pInstance); + +/** +* Creates a new SoftStringReturner instance +* +* @param[out] pInstance - New SoftStringReturner instance +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeCreateSoftStringReturnerPtr) (LibThreadSafe_SoftStringReturner * pInstance); + +/** +* Creates a new StrictStringReturner instance +* +* @param[out] pInstance - New StrictStringReturner instance +* @return error code or 0 (success) +*/ +typedef LibThreadSafeResult (*PLibThreadSafeCreateStrictStringReturnerPtr) (LibThreadSafe_StrictStringReturner * pInstance); + +/************************************************************************************************************************* + Function Table Structure +**************************************************************************************************************************/ + +typedef struct { + void * m_LibraryHandle; + PLibThreadSafeBase_ClassTypeIdPtr m_Base_ClassTypeId; + PLibThreadSafeBase__lockInstancePtr m_Base__lockInstance; + PLibThreadSafeBase__unlockInstancePtr m_Base__unlockInstance; + PLibThreadSafeStringReturner_GetStringPtr m_StringReturner_GetString; + PLibThreadSafeStringReturner_ThreadSafetyCheckPtr m_StringReturner_ThreadSafetyCheck; + PLibThreadSafeSoftStringReturner_GetStringPtr m_SoftStringReturner_GetString; + PLibThreadSafeSoftStringReturner_ThreadSafetyCheckPtr m_SoftStringReturner_ThreadSafetyCheck; + PLibThreadSafeStrictStringReturner_GetStringPtr m_StrictStringReturner_GetString; + PLibThreadSafeStrictStringReturner_ThreadSafetyCheckPtr m_StrictStringReturner_ThreadSafetyCheck; + PLibThreadSafeGetVersionPtr m_GetVersion; + PLibThreadSafeGetLastErrorPtr m_GetLastError; + PLibThreadSafeAcquireInstancePtr m_AcquireInstance; + PLibThreadSafeReleaseInstancePtr m_ReleaseInstance; + PLibThreadSafeCreateStringReturnerPtr m_CreateStringReturner; + PLibThreadSafeCreateSoftStringReturnerPtr m_CreateSoftStringReturner; + PLibThreadSafeCreateStrictStringReturnerPtr m_CreateStrictStringReturner; +} sLibThreadSafeDynamicWrapperTable; + +#endif // __LIBTHREADSAFE_DYNAMICHEADER_CPPTYPES + diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_dynamic.hpp b/Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_dynamic.hpp new file mode 100644 index 00000000..4981ebd2 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_dynamic.hpp @@ -0,0 +1,980 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++-Header file in order to allow an easy + use of Thread-safe library + +Interface version: 1.0.0 + +*/ + +#ifndef __LIBTHREADSAFE_CPPHEADER_DYNAMIC_CPP +#define __LIBTHREADSAFE_CPPHEADER_DYNAMIC_CPP + +#include "libthreadsafe_types.hpp" +#include "libthreadsafe_dynamic.h" + + +#ifdef _WIN32 +#include +#else // _WIN32 +#include +#endif // _WIN32 +#include +#include +#include +#include +#include + +namespace LibThreadSafe { + +/************************************************************************************************************************* + Forward Declaration of all classes +**************************************************************************************************************************/ +class CWrapper; +class CBase; +class CStringReturner; +class CSoftStringReturner; +class CStrictStringReturner; + +/************************************************************************************************************************* + Declaration of deprecated class types +**************************************************************************************************************************/ +typedef CWrapper CLibThreadSafeWrapper; +typedef CBase CLibThreadSafeBase; +typedef CStringReturner CLibThreadSafeStringReturner; +typedef CSoftStringReturner CLibThreadSafeSoftStringReturner; +typedef CStrictStringReturner CLibThreadSafeStrictStringReturner; + +/************************************************************************************************************************* + Declaration of shared pointer types +**************************************************************************************************************************/ +typedef std::shared_ptr PWrapper; +typedef std::shared_ptr PBase; +typedef std::shared_ptr PStringReturner; +typedef std::shared_ptr PSoftStringReturner; +typedef std::shared_ptr PStrictStringReturner; + +/************************************************************************************************************************* + Declaration of deprecated shared pointer types +**************************************************************************************************************************/ +typedef PWrapper PLibThreadSafeWrapper; +typedef PBase PLibThreadSafeBase; +typedef PStringReturner PLibThreadSafeStringReturner; +typedef PSoftStringReturner PLibThreadSafeSoftStringReturner; +typedef PStrictStringReturner PLibThreadSafeStrictStringReturner; + + +/************************************************************************************************************************* + classParam Definition +**************************************************************************************************************************/ + +template class classParam { +private: + std::shared_ptr m_sharedPtr; + const T* m_ptr; + +public: + classParam(const T* ptr) + : m_ptr(ptr) + { + } + + classParam(std::shared_ptr sharedPtr) + : m_sharedPtr(sharedPtr), m_ptr(sharedPtr.get()) + { + } + + LibThreadSafeHandle GetHandle() + { + if (m_ptr != nullptr) + return m_ptr->handle(); + return (LibThreadSafeHandle)nullptr; + } +}; + + +/************************************************************************************************************************* + Class ELibThreadSafeException +**************************************************************************************************************************/ +class ELibThreadSafeException : public std::exception { +protected: + /** + * Error code for the Exception. + */ + LibThreadSafeResult m_errorCode; + /** + * Error message for the Exception. + */ + std::string m_errorMessage; + std::string m_originalErrorMessage; + +public: + /** + * Exception Constructor. + */ + ELibThreadSafeException(LibThreadSafeResult errorCode, const std::string & sErrorMessage) + : m_errorCode(errorCode), m_originalErrorMessage(sErrorMessage) + { + m_errorMessage = buildErrorMessage(); + } + + /** + * Returns error code + */ + LibThreadSafeResult getErrorCode() const noexcept + { + return m_errorCode; + } + + /** + * Returns error message + */ + const char* what() const noexcept + { + return m_errorMessage.c_str(); + } + + const char* getErrorMessage() const noexcept + { + return m_originalErrorMessage.c_str(); + } + + const char* getErrorName() const noexcept + { + switch(getErrorCode()) { + case LIBTHREADSAFE_SUCCESS: return "SUCCESS"; + case LIBTHREADSAFE_ERROR_NOTIMPLEMENTED: return "NOTIMPLEMENTED"; + case LIBTHREADSAFE_ERROR_INVALIDPARAM: return "INVALIDPARAM"; + case LIBTHREADSAFE_ERROR_INVALIDCAST: return "INVALIDCAST"; + case LIBTHREADSAFE_ERROR_BUFFERTOOSMALL: return "BUFFERTOOSMALL"; + case LIBTHREADSAFE_ERROR_GENERICEXCEPTION: return "GENERICEXCEPTION"; + case LIBTHREADSAFE_ERROR_COULDNOTLOADLIBRARY: return "COULDNOTLOADLIBRARY"; + case LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT: return "COULDNOTFINDLIBRARYEXPORT"; + case LIBTHREADSAFE_ERROR_INCOMPATIBLEBINARYVERSION: return "INCOMPATIBLEBINARYVERSION"; + case LIBTHREADSAFE_ERROR_NORESULTAVAILABLE: return "NORESULTAVAILABLE"; + case LIBTHREADSAFE_ERROR_CALCULATIONABORTED: return "CALCULATIONABORTED"; + } + return "UNKNOWN"; + } + + const char* getErrorDescription() const noexcept + { + switch(getErrorCode()) { + case LIBTHREADSAFE_SUCCESS: return "success"; + case LIBTHREADSAFE_ERROR_NOTIMPLEMENTED: return "functionality not implemented"; + case LIBTHREADSAFE_ERROR_INVALIDPARAM: return "an invalid parameter was passed"; + case LIBTHREADSAFE_ERROR_INVALIDCAST: return "a type cast failed"; + case LIBTHREADSAFE_ERROR_BUFFERTOOSMALL: return "a provided buffer is too small"; + case LIBTHREADSAFE_ERROR_GENERICEXCEPTION: return "a generic exception occurred"; + case LIBTHREADSAFE_ERROR_COULDNOTLOADLIBRARY: return "the library could not be loaded"; + case LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT: return "a required exported symbol could not be found in the library"; + case LIBTHREADSAFE_ERROR_INCOMPATIBLEBINARYVERSION: return "the version of the binary interface does not match the bindings interface"; + case LIBTHREADSAFE_ERROR_NORESULTAVAILABLE: return "no result is available"; + case LIBTHREADSAFE_ERROR_CALCULATIONABORTED: return "a calculation has been aborted"; + } + return "unknown error"; + } + +private: + + std::string buildErrorMessage() const noexcept + { + std::string msg = m_originalErrorMessage; + if (msg.empty()) { + msg = getErrorDescription(); + } + return std::string("Error: ") + getErrorName() + ": " + msg; + } +}; + +/************************************************************************************************************************* + Class CInputVector +**************************************************************************************************************************/ +template +class CInputVector { +private: + + const T* m_data; + size_t m_size; + +public: + + CInputVector(const std::vector& vec) + : m_data( vec.data() ), m_size( vec.size() ) + { + } + + CInputVector(const T* in_data, size_t in_size) + : m_data( in_data ), m_size(in_size ) + { + } + + const T* data() const + { + return m_data; + } + + size_t size() const + { + return m_size; + } + +}; + +// declare deprecated class name +template +using CLibThreadSafeInputVector = CInputVector; + +/************************************************************************************************************************* + Class CWrapper +**************************************************************************************************************************/ +class CWrapper { +public: + + explicit CWrapper(void* pSymbolLookupMethod) + { + CheckError(nullptr, initWrapperTable(&m_WrapperTable)); + CheckError(nullptr, loadWrapperTableFromSymbolLookupMethod(&m_WrapperTable, pSymbolLookupMethod)); + + CheckError(nullptr, checkBinaryVersion()); + } + + explicit CWrapper(const std::string &sFileName) + { + CheckError(nullptr, initWrapperTable(&m_WrapperTable)); + CheckError(nullptr, loadWrapperTable(&m_WrapperTable, sFileName.c_str())); + + CheckError(nullptr, checkBinaryVersion()); + } + + static PWrapper loadLibrary(const std::string &sFileName) + { + return std::make_shared(sFileName); + } + + static PWrapper loadLibraryFromSymbolLookupMethod(void* pSymbolLookupMethod) + { + return std::make_shared(pSymbolLookupMethod); + } + + ~CWrapper() + { + releaseWrapperTable(&m_WrapperTable); + } + + inline void CheckError(CBase * pBaseClass, LibThreadSafeResult nResult, bool unlockIfError = false); + + inline void GetVersion(LibThreadSafe_uint32 & nMajor, LibThreadSafe_uint32 & nMinor, LibThreadSafe_uint32 & nMicro); + inline bool GetLastError(classParam pInstance, std::string & sErrorMessage); + inline void AcquireInstance(classParam pInstance); + inline void ReleaseInstance(classParam pInstance); + inline PStringReturner CreateStringReturner(); + inline PSoftStringReturner CreateSoftStringReturner(); + inline PStrictStringReturner CreateStrictStringReturner(); + + inline CBase* polymorphicFactory(LibThreadSafeHandle); + +private: + sLibThreadSafeDynamicWrapperTable m_WrapperTable; + + LibThreadSafeResult checkBinaryVersion() + { + LibThreadSafe_uint32 nMajor, nMinor, nMicro; + GetVersion(nMajor, nMinor, nMicro); + if (nMajor != LIBTHREADSAFE_VERSION_MAJOR) { + return LIBTHREADSAFE_ERROR_INCOMPATIBLEBINARYVERSION; + } + return LIBTHREADSAFE_SUCCESS; + } + LibThreadSafeResult initWrapperTable(sLibThreadSafeDynamicWrapperTable * pWrapperTable); + LibThreadSafeResult releaseWrapperTable(sLibThreadSafeDynamicWrapperTable * pWrapperTable); + LibThreadSafeResult loadWrapperTable(sLibThreadSafeDynamicWrapperTable * pWrapperTable, const char * pLibraryFileName); + LibThreadSafeResult loadWrapperTableFromSymbolLookupMethod(sLibThreadSafeDynamicWrapperTable * pWrapperTable, void* pSymbolLookupMethod); + + friend class CBase; + friend class CStringReturner; + friend class CSoftStringReturner; + friend class CStrictStringReturner; + +}; + + +/************************************************************************************************************************* + Class CBase +**************************************************************************************************************************/ +class CBase { +public: + +protected: + /* Wrapper Object that created the class. */ + CWrapper * m_pWrapper; + /* Handle to Instance in library*/ + LibThreadSafeHandle m_pHandle; + + /* Checks for an Error code, raises Exceptions and unlocks library object*/ + void CheckError(LibThreadSafeResult nResult, bool unlockIfError) + { + if (m_pWrapper != nullptr) + m_pWrapper->CheckError(this, nResult, unlockIfError); + } +public: + /** + * CBase::CBase - Constructor for Base class. + */ + CBase(CWrapper * pWrapper, LibThreadSafeHandle pHandle) + : m_pWrapper(pWrapper), m_pHandle(pHandle) + { + } + + /** + * CBase::~CBase - Destructor for Base class. + */ + virtual ~CBase() + { + if (m_pWrapper != nullptr) + m_pWrapper->ReleaseInstance(this); + m_pWrapper = nullptr; + } + + /** + * CBase::handle - Returns handle to instance. + */ + LibThreadSafeHandle handle() const + { + return m_pHandle; + } + + /** + * CBase::wrapper - Returns wrapper instance. + */ + CWrapper * wrapper() const + { + return m_pWrapper; + } + + friend class CWrapper; + inline LibThreadSafe_uint64 ClassTypeId(); + +protected: + inline void _lockInstance(); + inline void _unlockInstance(); +}; + +/************************************************************************************************************************* + Class CStringReturner +**************************************************************************************************************************/ +class CStringReturner : public CBase { +public: + + /** + * CStringReturner::CStringReturner - Constructor for StringReturner class. + */ + CStringReturner(CWrapper* pWrapper, LibThreadSafeHandle pHandle) + : CBase(pWrapper, pHandle) + { + } + + inline std::string GetString(); + inline void ThreadSafetyCheck(); +}; + +/************************************************************************************************************************* + Class CSoftStringReturner +**************************************************************************************************************************/ +class CSoftStringReturner : public CBase { +public: + + /** + * CSoftStringReturner::CSoftStringReturner - Constructor for SoftStringReturner class. + */ + CSoftStringReturner(CWrapper* pWrapper, LibThreadSafeHandle pHandle) + : CBase(pWrapper, pHandle) + { + } + + inline std::string GetString(); + inline void ThreadSafetyCheck(); +}; + +/************************************************************************************************************************* + Class CStrictStringReturner +**************************************************************************************************************************/ +class CStrictStringReturner : public CBase { +public: + + /** + * CStrictStringReturner::CStrictStringReturner - Constructor for StrictStringReturner class. + */ + CStrictStringReturner(CWrapper* pWrapper, LibThreadSafeHandle pHandle) + : CBase(pWrapper, pHandle) + { + } + + inline std::string GetString(); + inline void ThreadSafetyCheck(); +}; + +/************************************************************************************************************************* + RTTI: Polymorphic Factory implementation +**************************************************************************************************************************/ + +/** +* IMPORTANT: PolymorphicFactory method should not be used by application directly. +* It's designed to be used on LibThreadSafeHandle object only once. +* If it's used on any existing object as a form of dynamic cast then +* CWrapper::AcquireInstance(CBase object) must be called after instantiating new object. +* This is important to keep reference count matching between application and library sides. +*/ +inline CBase* CWrapper::polymorphicFactory(LibThreadSafeHandle pHandle) +{ + LibThreadSafe_uint64 resultClassTypeId = 0; + CheckError(nullptr, m_WrapperTable.m_Base_ClassTypeId(pHandle, &resultClassTypeId)); + switch(resultClassTypeId) { + case 0x82FEB223BA4B6B0EUL: return new CBase(this, pHandle); break; // First 64 bits of SHA1 of a string: "LibThreadSafe::Base" + case 0x01755067BCC5ED81UL: return new CStringReturner(this, pHandle); break; // First 64 bits of SHA1 of a string: "LibThreadSafe::StringReturner" + case 0x6C0744711291F198UL: return new CSoftStringReturner(this, pHandle); break; // First 64 bits of SHA1 of a string: "LibThreadSafe::SoftStringReturner" + case 0xC8D8EB62D68DF429UL: return new CStrictStringReturner(this, pHandle); break; // First 64 bits of SHA1 of a string: "LibThreadSafe::StrictStringReturner" + } + return new CBase(this, pHandle); +} + + /** + * CWrapper::GetVersion - retrieves the binary version of this library. + * @param[out] nMajor - returns the major version of this library + * @param[out] nMinor - returns the minor version of this library + * @param[out] nMicro - returns the micro version of this library + */ + inline void CWrapper::GetVersion(LibThreadSafe_uint32 & nMajor, LibThreadSafe_uint32 & nMinor, LibThreadSafe_uint32 & nMicro) + { + CheckError(nullptr,m_WrapperTable.m_GetVersion(&nMajor, &nMinor, &nMicro)); + } + + /** + * CWrapper::GetLastError - Returns the last error recorded on this object + * @param[in] pInstance - Instance Handle + * @param[out] sErrorMessage - Message of the last error + * @return Is there a last error to query + */ + inline bool CWrapper::GetLastError(classParam pInstance, std::string & sErrorMessage) + { + LibThreadSafeHandle hInstance = pInstance.GetHandle(); + LibThreadSafe_uint32 bytesNeededErrorMessage = 0; + LibThreadSafe_uint32 bytesWrittenErrorMessage = 0; + bool resultHasError = 0; + CheckError(nullptr,m_WrapperTable.m_GetLastError(hInstance, 0, &bytesNeededErrorMessage, nullptr, &resultHasError)); + std::vector bufferErrorMessage(bytesNeededErrorMessage); + CheckError(nullptr,m_WrapperTable.m_GetLastError(hInstance, bytesNeededErrorMessage, &bytesWrittenErrorMessage, &bufferErrorMessage[0], &resultHasError)); + sErrorMessage = std::string(&bufferErrorMessage[0]); + + return resultHasError; + } + + /** + * CWrapper::AcquireInstance - Acquire shared ownership of an Instance + * @param[in] pInstance - Instance Handle + */ + inline void CWrapper::AcquireInstance(classParam pInstance) + { + LibThreadSafeHandle hInstance = pInstance.GetHandle(); + CheckError(nullptr,m_WrapperTable.m_AcquireInstance(hInstance)); + } + + /** + * CWrapper::ReleaseInstance - Releases shared ownership of an Instance + * @param[in] pInstance - Instance Handle + */ + inline void CWrapper::ReleaseInstance(classParam pInstance) + { + LibThreadSafeHandle hInstance = pInstance.GetHandle(); + CheckError(nullptr,m_WrapperTable.m_ReleaseInstance(hInstance)); + } + + /** + * CWrapper::CreateStringReturner - Creates a new StringReturner instance + * @return New StringReturner instance + */ + inline PStringReturner CWrapper::CreateStringReturner() + { + LibThreadSafeHandle hInstance = (LibThreadSafeHandle)nullptr; + CheckError(nullptr,m_WrapperTable.m_CreateStringReturner(&hInstance)); + + if (!hInstance) { + CheckError(nullptr,LIBTHREADSAFE_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(this->polymorphicFactory(hInstance))); + } + + /** + * CWrapper::CreateSoftStringReturner - Creates a new SoftStringReturner instance + * @return New SoftStringReturner instance + */ + inline PSoftStringReturner CWrapper::CreateSoftStringReturner() + { + LibThreadSafeHandle hInstance = (LibThreadSafeHandle)nullptr; + CheckError(nullptr,m_WrapperTable.m_CreateSoftStringReturner(&hInstance)); + + if (!hInstance) { + CheckError(nullptr,LIBTHREADSAFE_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(this->polymorphicFactory(hInstance))); + } + + /** + * CWrapper::CreateStrictStringReturner - Creates a new StrictStringReturner instance + * @return New StrictStringReturner instance + */ + inline PStrictStringReturner CWrapper::CreateStrictStringReturner() + { + LibThreadSafeHandle hInstance = (LibThreadSafeHandle)nullptr; + CheckError(nullptr,m_WrapperTable.m_CreateStrictStringReturner(&hInstance)); + + if (!hInstance) { + CheckError(nullptr,LIBTHREADSAFE_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(this->polymorphicFactory(hInstance))); + } + + inline void CWrapper::CheckError(CBase * pBaseClass, LibThreadSafeResult nResult, bool unlockIfError) + { + if (nResult != 0) { + std::string sErrorMessage; + if (pBaseClass != nullptr) { + GetLastError(pBaseClass, sErrorMessage); + if (unlockIfError) { + pBaseClass->_unlockInstance(); + } + } + throw ELibThreadSafeException(nResult, sErrorMessage); + } + } + + + inline LibThreadSafeResult CWrapper::initWrapperTable(sLibThreadSafeDynamicWrapperTable * pWrapperTable) + { + if (pWrapperTable == nullptr) + return LIBTHREADSAFE_ERROR_INVALIDPARAM; + + pWrapperTable->m_LibraryHandle = nullptr; + pWrapperTable->m_Base_ClassTypeId = nullptr; + pWrapperTable->m_Base__lockInstance = nullptr; + pWrapperTable->m_Base__unlockInstance = nullptr; + pWrapperTable->m_StringReturner_GetString = nullptr; + pWrapperTable->m_StringReturner_ThreadSafetyCheck = nullptr; + pWrapperTable->m_SoftStringReturner_GetString = nullptr; + pWrapperTable->m_SoftStringReturner_ThreadSafetyCheck = nullptr; + pWrapperTable->m_StrictStringReturner_GetString = nullptr; + pWrapperTable->m_StrictStringReturner_ThreadSafetyCheck = nullptr; + pWrapperTable->m_GetVersion = nullptr; + pWrapperTable->m_GetLastError = nullptr; + pWrapperTable->m_AcquireInstance = nullptr; + pWrapperTable->m_ReleaseInstance = nullptr; + pWrapperTable->m_CreateStringReturner = nullptr; + pWrapperTable->m_CreateSoftStringReturner = nullptr; + pWrapperTable->m_CreateStrictStringReturner = nullptr; + + return LIBTHREADSAFE_SUCCESS; + } + + inline LibThreadSafeResult CWrapper::releaseWrapperTable(sLibThreadSafeDynamicWrapperTable * pWrapperTable) + { + if (pWrapperTable == nullptr) + return LIBTHREADSAFE_ERROR_INVALIDPARAM; + + if (pWrapperTable->m_LibraryHandle != nullptr) { + #ifdef _WIN32 + HMODULE hModule = (HMODULE) pWrapperTable->m_LibraryHandle; + FreeLibrary(hModule); + #else // _WIN32 + dlclose(pWrapperTable->m_LibraryHandle); + #endif // _WIN32 + return initWrapperTable(pWrapperTable); + } + + return LIBTHREADSAFE_SUCCESS; + } + + inline LibThreadSafeResult CWrapper::loadWrapperTable(sLibThreadSafeDynamicWrapperTable * pWrapperTable, const char * pLibraryFileName) + { + if (pWrapperTable == nullptr) + return LIBTHREADSAFE_ERROR_INVALIDPARAM; + if (pLibraryFileName == nullptr) + return LIBTHREADSAFE_ERROR_INVALIDPARAM; + + #ifdef _WIN32 + // Convert filename to UTF16-string + int nLength = 0; + while ((pLibraryFileName[nLength] != 0) && (nLength < MAX_PATH)) + nLength++; + int nBufferSize = nLength * 2 + 2; + std::vector wsLibraryFileName(nBufferSize); + int nResult = MultiByteToWideChar(CP_UTF8, 0, pLibraryFileName, nLength, &wsLibraryFileName[0], nBufferSize); + if (nResult == 0) + return LIBTHREADSAFE_ERROR_COULDNOTLOADLIBRARY; + + HMODULE hLibrary = LoadLibraryW(wsLibraryFileName.data()); + if (hLibrary == 0) + return LIBTHREADSAFE_ERROR_COULDNOTLOADLIBRARY; + #else // _WIN32 + void* hLibrary = dlopen(pLibraryFileName, RTLD_LAZY); + if (hLibrary == 0) + return LIBTHREADSAFE_ERROR_COULDNOTLOADLIBRARY; + dlerror(); + #endif // _WIN32 + + #ifdef _WIN32 + pWrapperTable->m_Base_ClassTypeId = (PLibThreadSafeBase_ClassTypeIdPtr) GetProcAddress(hLibrary, "libthreadsafe_base_classtypeid"); + #else // _WIN32 + pWrapperTable->m_Base_ClassTypeId = (PLibThreadSafeBase_ClassTypeIdPtr) dlsym(hLibrary, "libthreadsafe_base_classtypeid"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Base_ClassTypeId == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_Base__lockInstance = (PLibThreadSafeBase__lockInstancePtr) GetProcAddress(hLibrary, "libthreadsafe_base__lockinstance"); + #else // _WIN32 + pWrapperTable->m_Base__lockInstance = (PLibThreadSafeBase__lockInstancePtr) dlsym(hLibrary, "libthreadsafe_base__lockinstance"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Base__lockInstance == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_Base__unlockInstance = (PLibThreadSafeBase__unlockInstancePtr) GetProcAddress(hLibrary, "libthreadsafe_base__unlockinstance"); + #else // _WIN32 + pWrapperTable->m_Base__unlockInstance = (PLibThreadSafeBase__unlockInstancePtr) dlsym(hLibrary, "libthreadsafe_base__unlockinstance"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Base__unlockInstance == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_StringReturner_GetString = (PLibThreadSafeStringReturner_GetStringPtr) GetProcAddress(hLibrary, "libthreadsafe_stringreturner_getstring"); + #else // _WIN32 + pWrapperTable->m_StringReturner_GetString = (PLibThreadSafeStringReturner_GetStringPtr) dlsym(hLibrary, "libthreadsafe_stringreturner_getstring"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_StringReturner_GetString == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_StringReturner_ThreadSafetyCheck = (PLibThreadSafeStringReturner_ThreadSafetyCheckPtr) GetProcAddress(hLibrary, "libthreadsafe_stringreturner_threadsafetycheck"); + #else // _WIN32 + pWrapperTable->m_StringReturner_ThreadSafetyCheck = (PLibThreadSafeStringReturner_ThreadSafetyCheckPtr) dlsym(hLibrary, "libthreadsafe_stringreturner_threadsafetycheck"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_StringReturner_ThreadSafetyCheck == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_SoftStringReturner_GetString = (PLibThreadSafeSoftStringReturner_GetStringPtr) GetProcAddress(hLibrary, "libthreadsafe_softstringreturner_getstring"); + #else // _WIN32 + pWrapperTable->m_SoftStringReturner_GetString = (PLibThreadSafeSoftStringReturner_GetStringPtr) dlsym(hLibrary, "libthreadsafe_softstringreturner_getstring"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_SoftStringReturner_GetString == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_SoftStringReturner_ThreadSafetyCheck = (PLibThreadSafeSoftStringReturner_ThreadSafetyCheckPtr) GetProcAddress(hLibrary, "libthreadsafe_softstringreturner_threadsafetycheck"); + #else // _WIN32 + pWrapperTable->m_SoftStringReturner_ThreadSafetyCheck = (PLibThreadSafeSoftStringReturner_ThreadSafetyCheckPtr) dlsym(hLibrary, "libthreadsafe_softstringreturner_threadsafetycheck"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_SoftStringReturner_ThreadSafetyCheck == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_StrictStringReturner_GetString = (PLibThreadSafeStrictStringReturner_GetStringPtr) GetProcAddress(hLibrary, "libthreadsafe_strictstringreturner_getstring"); + #else // _WIN32 + pWrapperTable->m_StrictStringReturner_GetString = (PLibThreadSafeStrictStringReturner_GetStringPtr) dlsym(hLibrary, "libthreadsafe_strictstringreturner_getstring"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_StrictStringReturner_GetString == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_StrictStringReturner_ThreadSafetyCheck = (PLibThreadSafeStrictStringReturner_ThreadSafetyCheckPtr) GetProcAddress(hLibrary, "libthreadsafe_strictstringreturner_threadsafetycheck"); + #else // _WIN32 + pWrapperTable->m_StrictStringReturner_ThreadSafetyCheck = (PLibThreadSafeStrictStringReturner_ThreadSafetyCheckPtr) dlsym(hLibrary, "libthreadsafe_strictstringreturner_threadsafetycheck"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_StrictStringReturner_ThreadSafetyCheck == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_GetVersion = (PLibThreadSafeGetVersionPtr) GetProcAddress(hLibrary, "libthreadsafe_getversion"); + #else // _WIN32 + pWrapperTable->m_GetVersion = (PLibThreadSafeGetVersionPtr) dlsym(hLibrary, "libthreadsafe_getversion"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_GetVersion == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_GetLastError = (PLibThreadSafeGetLastErrorPtr) GetProcAddress(hLibrary, "libthreadsafe_getlasterror"); + #else // _WIN32 + pWrapperTable->m_GetLastError = (PLibThreadSafeGetLastErrorPtr) dlsym(hLibrary, "libthreadsafe_getlasterror"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_GetLastError == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_AcquireInstance = (PLibThreadSafeAcquireInstancePtr) GetProcAddress(hLibrary, "libthreadsafe_acquireinstance"); + #else // _WIN32 + pWrapperTable->m_AcquireInstance = (PLibThreadSafeAcquireInstancePtr) dlsym(hLibrary, "libthreadsafe_acquireinstance"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_AcquireInstance == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_ReleaseInstance = (PLibThreadSafeReleaseInstancePtr) GetProcAddress(hLibrary, "libthreadsafe_releaseinstance"); + #else // _WIN32 + pWrapperTable->m_ReleaseInstance = (PLibThreadSafeReleaseInstancePtr) dlsym(hLibrary, "libthreadsafe_releaseinstance"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_ReleaseInstance == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_CreateStringReturner = (PLibThreadSafeCreateStringReturnerPtr) GetProcAddress(hLibrary, "libthreadsafe_createstringreturner"); + #else // _WIN32 + pWrapperTable->m_CreateStringReturner = (PLibThreadSafeCreateStringReturnerPtr) dlsym(hLibrary, "libthreadsafe_createstringreturner"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_CreateStringReturner == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_CreateSoftStringReturner = (PLibThreadSafeCreateSoftStringReturnerPtr) GetProcAddress(hLibrary, "libthreadsafe_createsoftstringreturner"); + #else // _WIN32 + pWrapperTable->m_CreateSoftStringReturner = (PLibThreadSafeCreateSoftStringReturnerPtr) dlsym(hLibrary, "libthreadsafe_createsoftstringreturner"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_CreateSoftStringReturner == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_CreateStrictStringReturner = (PLibThreadSafeCreateStrictStringReturnerPtr) GetProcAddress(hLibrary, "libthreadsafe_createstrictstringreturner"); + #else // _WIN32 + pWrapperTable->m_CreateStrictStringReturner = (PLibThreadSafeCreateStrictStringReturnerPtr) dlsym(hLibrary, "libthreadsafe_createstrictstringreturner"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_CreateStrictStringReturner == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + pWrapperTable->m_LibraryHandle = hLibrary; + return LIBTHREADSAFE_SUCCESS; + } + + inline LibThreadSafeResult CWrapper::loadWrapperTableFromSymbolLookupMethod(sLibThreadSafeDynamicWrapperTable * pWrapperTable, void* pSymbolLookupMethod) + { + if (pWrapperTable == nullptr) + return LIBTHREADSAFE_ERROR_INVALIDPARAM; + if (pSymbolLookupMethod == nullptr) + return LIBTHREADSAFE_ERROR_INVALIDPARAM; + + typedef LibThreadSafeResult(*SymbolLookupType)(const char*, void**); + + SymbolLookupType pLookup = (SymbolLookupType)pSymbolLookupMethod; + + LibThreadSafeResult eLookupError = LIBTHREADSAFE_SUCCESS; + eLookupError = (*pLookup)("libthreadsafe_base_classtypeid", (void**)&(pWrapperTable->m_Base_ClassTypeId)); + if ( (eLookupError != 0) || (pWrapperTable->m_Base_ClassTypeId == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_base__lockinstance", (void**)&(pWrapperTable->m_Base__lockInstance)); + if ( (eLookupError != 0) || (pWrapperTable->m_Base__lockInstance == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_base__unlockinstance", (void**)&(pWrapperTable->m_Base__unlockInstance)); + if ( (eLookupError != 0) || (pWrapperTable->m_Base__unlockInstance == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_stringreturner_getstring", (void**)&(pWrapperTable->m_StringReturner_GetString)); + if ( (eLookupError != 0) || (pWrapperTable->m_StringReturner_GetString == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_stringreturner_threadsafetycheck", (void**)&(pWrapperTable->m_StringReturner_ThreadSafetyCheck)); + if ( (eLookupError != 0) || (pWrapperTable->m_StringReturner_ThreadSafetyCheck == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_softstringreturner_getstring", (void**)&(pWrapperTable->m_SoftStringReturner_GetString)); + if ( (eLookupError != 0) || (pWrapperTable->m_SoftStringReturner_GetString == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_softstringreturner_threadsafetycheck", (void**)&(pWrapperTable->m_SoftStringReturner_ThreadSafetyCheck)); + if ( (eLookupError != 0) || (pWrapperTable->m_SoftStringReturner_ThreadSafetyCheck == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_strictstringreturner_getstring", (void**)&(pWrapperTable->m_StrictStringReturner_GetString)); + if ( (eLookupError != 0) || (pWrapperTable->m_StrictStringReturner_GetString == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_strictstringreturner_threadsafetycheck", (void**)&(pWrapperTable->m_StrictStringReturner_ThreadSafetyCheck)); + if ( (eLookupError != 0) || (pWrapperTable->m_StrictStringReturner_ThreadSafetyCheck == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_getversion", (void**)&(pWrapperTable->m_GetVersion)); + if ( (eLookupError != 0) || (pWrapperTable->m_GetVersion == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_getlasterror", (void**)&(pWrapperTable->m_GetLastError)); + if ( (eLookupError != 0) || (pWrapperTable->m_GetLastError == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_acquireinstance", (void**)&(pWrapperTable->m_AcquireInstance)); + if ( (eLookupError != 0) || (pWrapperTable->m_AcquireInstance == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_releaseinstance", (void**)&(pWrapperTable->m_ReleaseInstance)); + if ( (eLookupError != 0) || (pWrapperTable->m_ReleaseInstance == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_createstringreturner", (void**)&(pWrapperTable->m_CreateStringReturner)); + if ( (eLookupError != 0) || (pWrapperTable->m_CreateStringReturner == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_createsoftstringreturner", (void**)&(pWrapperTable->m_CreateSoftStringReturner)); + if ( (eLookupError != 0) || (pWrapperTable->m_CreateSoftStringReturner == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libthreadsafe_createstrictstringreturner", (void**)&(pWrapperTable->m_CreateStrictStringReturner)); + if ( (eLookupError != 0) || (pWrapperTable->m_CreateStrictStringReturner == nullptr) ) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + + return LIBTHREADSAFE_SUCCESS; + } + + + /** + * Method definitions for class CBase + */ + + /** + * CBase::ClassTypeId - Get Class Type Id + * @return Class type as a 64 bits integer + */ + LibThreadSafe_uint64 CBase::ClassTypeId() + { + LibThreadSafe_uint64 resultClassTypeId = 0; + CheckError(m_pWrapper->m_WrapperTable.m_Base_ClassTypeId(m_pHandle, &resultClassTypeId), false); + + return resultClassTypeId; + } + + /** + * CBase::_lockInstance - If thread safety for class in library is enabled it should lock object for calling thread + */ + void CBase::_lockInstance() + { + CheckError(m_pWrapper->m_WrapperTable.m_Base__lockInstance(m_pHandle), false); + } + + /** + * CBase::_unlockInstance - If thread safety for class in library is enabled it should unlock object for other threads + */ + void CBase::_unlockInstance() + { + CheckError(m_pWrapper->m_WrapperTable.m_Base__unlockInstance(m_pHandle), false); + } + + /** + * Method definitions for class CStringReturner + */ + + /** + * CStringReturner::GetString - Returns a string + * @return + */ + std::string CStringReturner::GetString() + { + LibThreadSafe_uint32 bytesNeededValue = 0; + LibThreadSafe_uint32 bytesWrittenValue = 0; + CheckError(m_pWrapper->m_WrapperTable.m_StringReturner_GetString(m_pHandle, 0, &bytesNeededValue, nullptr), false); + std::vector bufferValue(bytesNeededValue); + CheckError(m_pWrapper->m_WrapperTable.m_StringReturner_GetString(m_pHandle, bytesNeededValue, &bytesWrittenValue, &bufferValue[0]), false); + + return std::string(&bufferValue[0]); + } + + /** + * CStringReturner::ThreadSafetyCheck - Function that may crash when called from different threads at the same time + */ + void CStringReturner::ThreadSafetyCheck() + { + CheckError(m_pWrapper->m_WrapperTable.m_StringReturner_ThreadSafetyCheck(m_pHandle), false); + } + + /** + * Method definitions for class CSoftStringReturner + */ + + /** + * CSoftStringReturner::GetString - Returns a string + * @return + */ + std::string CSoftStringReturner::GetString() + { + LibThreadSafe_uint32 bytesNeededValue = 0; + LibThreadSafe_uint32 bytesWrittenValue = 0; + _lockInstance(); + CheckError(m_pWrapper->m_WrapperTable.m_SoftStringReturner_GetString(m_pHandle, 0, &bytesNeededValue, nullptr), true); + std::vector bufferValue(bytesNeededValue); + CheckError(m_pWrapper->m_WrapperTable.m_SoftStringReturner_GetString(m_pHandle, bytesNeededValue, &bytesWrittenValue, &bufferValue[0]), true); + _unlockInstance(); + + return std::string(&bufferValue[0]); + } + + /** + * CSoftStringReturner::ThreadSafetyCheck - Function that may crash when called from different threads at the same time + */ + void CSoftStringReturner::ThreadSafetyCheck() + { + CheckError(m_pWrapper->m_WrapperTable.m_SoftStringReturner_ThreadSafetyCheck(m_pHandle), false); + } + + /** + * Method definitions for class CStrictStringReturner + */ + + /** + * CStrictStringReturner::GetString - Returns a string + * @return + */ + std::string CStrictStringReturner::GetString() + { + LibThreadSafe_uint32 bytesNeededValue = 0; + LibThreadSafe_uint32 bytesWrittenValue = 0; + _lockInstance(); + CheckError(m_pWrapper->m_WrapperTable.m_StrictStringReturner_GetString(m_pHandle, 0, &bytesNeededValue, nullptr), true); + std::vector bufferValue(bytesNeededValue); + CheckError(m_pWrapper->m_WrapperTable.m_StrictStringReturner_GetString(m_pHandle, bytesNeededValue, &bytesWrittenValue, &bufferValue[0]), true); + _unlockInstance(); + + return std::string(&bufferValue[0]); + } + + /** + * CStrictStringReturner::ThreadSafetyCheck - Function that shouldn't crash when called from different threads at the same time + */ + void CStrictStringReturner::ThreadSafetyCheck() + { + _lockInstance(); + CheckError(m_pWrapper->m_WrapperTable.m_StrictStringReturner_ThreadSafetyCheck(m_pHandle), true); + _unlockInstance(); + } + +} // namespace LibThreadSafe + +#endif // __LIBTHREADSAFE_CPPHEADER_DYNAMIC_CPP + diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_types.hpp b/Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_types.hpp new file mode 100644 index 00000000..cbc3acd4 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Bindings/CppDynamic/libthreadsafe_types.hpp @@ -0,0 +1,123 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++-Header file with basic types in +order to allow an easy use of Thread-safe library + +Interface version: 1.0.0 + +*/ + +#ifndef __LIBTHREADSAFE_TYPES_HEADER_CPP +#define __LIBTHREADSAFE_TYPES_HEADER_CPP + + +/************************************************************************************************************************* + Scalar types definition +**************************************************************************************************************************/ + +#ifdef LIBTHREADSAFE_USELEGACYINTEGERTYPES + +typedef unsigned char LibThreadSafe_uint8; +typedef unsigned short LibThreadSafe_uint16 ; +typedef unsigned int LibThreadSafe_uint32; +typedef unsigned long long LibThreadSafe_uint64; +typedef char LibThreadSafe_int8; +typedef short LibThreadSafe_int16; +typedef int LibThreadSafe_int32; +typedef long long LibThreadSafe_int64; + +#else // LIBTHREADSAFE_USELEGACYINTEGERTYPES + +#include + +typedef uint8_t LibThreadSafe_uint8; +typedef uint16_t LibThreadSafe_uint16; +typedef uint32_t LibThreadSafe_uint32; +typedef uint64_t LibThreadSafe_uint64; +typedef int8_t LibThreadSafe_int8; +typedef int16_t LibThreadSafe_int16; +typedef int32_t LibThreadSafe_int32; +typedef int64_t LibThreadSafe_int64 ; + +#endif // LIBTHREADSAFE_USELEGACYINTEGERTYPES + +typedef float LibThreadSafe_single; +typedef double LibThreadSafe_double; + +/************************************************************************************************************************* + General type definitions +**************************************************************************************************************************/ + +typedef LibThreadSafe_int32 LibThreadSafeResult; +typedef void * LibThreadSafeHandle; +typedef void * LibThreadSafe_pvoid; + +/************************************************************************************************************************* + Version for LibThreadSafe +**************************************************************************************************************************/ + +#define LIBTHREADSAFE_VERSION_MAJOR 1 +#define LIBTHREADSAFE_VERSION_MINOR 0 +#define LIBTHREADSAFE_VERSION_MICRO 0 +#define LIBTHREADSAFE_VERSION_PRERELEASEINFO "" +#define LIBTHREADSAFE_VERSION_BUILDINFO "" + +/************************************************************************************************************************* + Error constants for LibThreadSafe +**************************************************************************************************************************/ + +#define LIBTHREADSAFE_SUCCESS 0 +#define LIBTHREADSAFE_ERROR_NOTIMPLEMENTED 1 /** functionality not implemented */ +#define LIBTHREADSAFE_ERROR_INVALIDPARAM 2 /** an invalid parameter was passed */ +#define LIBTHREADSAFE_ERROR_INVALIDCAST 3 /** a type cast failed */ +#define LIBTHREADSAFE_ERROR_BUFFERTOOSMALL 4 /** a provided buffer is too small */ +#define LIBTHREADSAFE_ERROR_GENERICEXCEPTION 5 /** a generic exception occurred */ +#define LIBTHREADSAFE_ERROR_COULDNOTLOADLIBRARY 6 /** the library could not be loaded */ +#define LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT 7 /** a required exported symbol could not be found in the library */ +#define LIBTHREADSAFE_ERROR_INCOMPATIBLEBINARYVERSION 8 /** the version of the binary interface does not match the bindings interface */ +#define LIBTHREADSAFE_ERROR_NORESULTAVAILABLE 9 /** no result is available */ +#define LIBTHREADSAFE_ERROR_CALCULATIONABORTED 10 /** a calculation has been aborted */ + +/************************************************************************************************************************* + Error strings for LibThreadSafe +**************************************************************************************************************************/ + +inline const char * LIBTHREADSAFE_GETERRORSTRING (LibThreadSafeResult nErrorCode) { + switch (nErrorCode) { + case LIBTHREADSAFE_SUCCESS: return "no error"; + case LIBTHREADSAFE_ERROR_NOTIMPLEMENTED: return "functionality not implemented"; + case LIBTHREADSAFE_ERROR_INVALIDPARAM: return "an invalid parameter was passed"; + case LIBTHREADSAFE_ERROR_INVALIDCAST: return "a type cast failed"; + case LIBTHREADSAFE_ERROR_BUFFERTOOSMALL: return "a provided buffer is too small"; + case LIBTHREADSAFE_ERROR_GENERICEXCEPTION: return "a generic exception occurred"; + case LIBTHREADSAFE_ERROR_COULDNOTLOADLIBRARY: return "the library could not be loaded"; + case LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT: return "a required exported symbol could not be found in the library"; + case LIBTHREADSAFE_ERROR_INCOMPATIBLEBINARYVERSION: return "the version of the binary interface does not match the bindings interface"; + case LIBTHREADSAFE_ERROR_NORESULTAVAILABLE: return "no result is available"; + case LIBTHREADSAFE_ERROR_CALCULATIONABORTED: return "a calculation has been aborted"; + default: return "unknown error"; + } +} + +/************************************************************************************************************************* + Declaration of handle classes +**************************************************************************************************************************/ + +typedef LibThreadSafeHandle LibThreadSafe_Base; +typedef LibThreadSafeHandle LibThreadSafe_StringReturner; +typedef LibThreadSafeHandle LibThreadSafe_SoftStringReturner; +typedef LibThreadSafeHandle LibThreadSafe_StrictStringReturner; + +namespace LibThreadSafe { + +} // namespace LibThreadSafe; + +// define legacy C-names for enums, structs and function types + +#endif // __LIBTHREADSAFE_TYPES_HEADER_CPP diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/CMakeLists.txt b/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/CMakeLists.txt new file mode 100644 index 00000000..71931859 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/CMakeLists.txt @@ -0,0 +1,26 @@ +#[[++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated CMake Project that demonstrates the + usage of the Dynamic C++ bindings of Thread-safe library + +Interface version: 1.0.0 + + +]] + +cmake_minimum_required(VERSION 3.5) + +set(CMAKE_CURRENT_BINDING_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../Bindings/CppDynamic) +project(LibThreadSafeExample_CPPDynamic) +set(CMAKE_CXX_STANDARD 20) +add_executable(LibThreadSafeExample_CPPDynamic "${CMAKE_CURRENT_SOURCE_DIR}/LibThreadSafe_example.cpp") +if (UNIX) + target_link_libraries(LibThreadSafeExample_CPPDynamic ${CMAKE_DL_LIBS}) +endif (UNIX) +target_include_directories(LibThreadSafeExample_CPPDynamic PRIVATE "${CMAKE_CURRENT_BINDING_DIR}") diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp new file mode 100644 index 00000000..2e3f0eb3 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp @@ -0,0 +1,56 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++ application that demonstrates the + usage of the Dynamic C++ bindings of Thread-safe library + +Interface version: 1.0.0 + +*/ + +#include +#include +#include +#include "libthreadsafe_dynamic.hpp" + +template +void testStringReturn(ArrayReturner& returner) +{ + std::vector threads; + for (int i = 0; i < 5; ++i) { + threads.emplace_back([&returner]() { + for (int j = 0; j < 10000; ++j) { + returner->GetString(); + } + }); + } +} + +template +void testThreadSafetyCheck(ArrayReturner& returner) +{ + std::vector threads; + for (int i = 0; i < 5; ++i) { + threads.emplace_back([&returner]() { + for (int j = 0; j < 10000; ++j) { + returner->ThreadSafetyCheck(); + } + }); + } +} + +int main() +{ + std::string libpath = ("C:/Users/uck/Dev/Repos/ActTask/MyFork/AutomaticComponentToolkit/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/out/build/x64-Debug"); // TODO: put the location of the LibThreadSafe-library file here. + auto wrapper = LibThreadSafe::CWrapper::loadLibrary(libpath + "/libthreadsafe.dll"); // TODO: add correct suffix of the library + + LibThreadSafe::PStringReturner stringReturner = wrapper->CreateStringReturner(); + LibThreadSafe::PSoftStringReturner softStringReturner = wrapper->CreateSoftStringReturner(); + LibThreadSafe::PStrictStringReturner strictStringReturner = wrapper->CreateStrictStringReturner(); + testThreadSafetyCheck(strictStringReturner); +} diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/CMakeLists.txt b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/CMakeLists.txt new file mode 100644 index 00000000..8170408d --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/CMakeLists.txt @@ -0,0 +1,46 @@ +#[[++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated CMakeLists file for the development of Thread-safe library. + +Interface version: 1.0.0 + + +]] + +cmake_minimum_required(VERSION 3.5) + +### The implementation of the Thread-safe library component +project(LibThreadSafe) + +set (CMAKE_CXX_STANDARD 11) + +# The location of autogenerated interfaces +set(CMAKE_CURRENT_AUTOGENERATED_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Interfaces) + +file(GLOB LIBTHREADSAFE_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/Stub/*.cpp +) +file(GLOB LIBTHREADSAFE_HDR + ${CMAKE_CURRENT_SOURCE_DIR}/Stub/*.hpp +) +set(LIBTHREADSAFE_SRC ${LIBTHREADSAFE_SRC} ${LIBTHREADSAFE_SRC} + ${CMAKE_CURRENT_AUTOGENERATED_DIR}/libthreadsafe_interfaceexception.cpp + ${CMAKE_CURRENT_AUTOGENERATED_DIR}/libthreadsafe_interfacewrapper.cpp +) + +add_library(libthreadsafe SHARED ${LIBTHREADSAFE_SRC}) +# Do not prefix the binary's name with "lib" on Unix systems: +set_target_properties(libthreadsafe PROPERTIES PREFIX "" IMPORT_PREFIX "" ) +# The following two properties are crucial to reduce the number of undesirably exported symbols +set_target_properties(libthreadsafe PROPERTIES CXX_VISIBILITY_PRESET hidden) +set_target_properties(libthreadsafe PROPERTIES VISIBILITY_INLINES_HIDDEN ON) +# This makes sure symbols are exported +target_compile_options(libthreadsafe PRIVATE "-D__LIBTHREADSAFE_EXPORTS") +target_include_directories(libthreadsafe PRIVATE ${CMAKE_CURRENT_AUTOGENERATED_DIR}) +target_include_directories(libthreadsafe PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Stub) diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_abi.hpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_abi.hpp new file mode 100644 index 00000000..28c57473 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_abi.hpp @@ -0,0 +1,205 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++-Header file in order to allow an easy + use of Thread-safe library + +Interface version: 1.0.0 + +*/ + +#ifndef __LIBTHREADSAFE_HEADER_CPP +#define __LIBTHREADSAFE_HEADER_CPP + +#ifdef __LIBTHREADSAFE_EXPORTS +#ifdef _WIN32 +#define LIBTHREADSAFE_DECLSPEC __declspec (dllexport) +#else // _WIN32 +#define LIBTHREADSAFE_DECLSPEC __attribute__((visibility("default"))) +#endif // _WIN32 +#else // __LIBTHREADSAFE_EXPORTS +#define LIBTHREADSAFE_DECLSPEC +#endif // __LIBTHREADSAFE_EXPORTS + +#include "libthreadsafe_types.hpp" + + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************************************************************* + Class definition for Base +**************************************************************************************************************************/ + +/** +* Get Class Type Id +* +* @param[in] pBase - Base instance. +* @param[out] pClassTypeId - Class type as a 64 bits integer +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_base_classtypeid(LibThreadSafe_Base pBase, LibThreadSafe_uint64 * pClassTypeId); + +/** +* If thread safety for class in library is enabled it should lock object for calling thread +* +* @param[in] pBase - Base instance. +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_base__lockinstance(LibThreadSafe_Base pBase); + +/** +* If thread safety for class in library is enabled it should unlock object for other threads +* +* @param[in] pBase - Base instance. +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_base__unlockinstance(LibThreadSafe_Base pBase); + +/************************************************************************************************************************* + Class definition for StringReturner +**************************************************************************************************************************/ + +/** +* Returns a string +* +* @param[in] pStringReturner - StringReturner instance. +* @param[in] nValueBufferSize - size of the buffer (including trailing 0) +* @param[out] pValueNeededChars - will be filled with the count of the written bytes, or needed buffer size. +* @param[out] pValueBuffer - buffer of , may be NULL +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_stringreturner_getstring(LibThreadSafe_StringReturner pStringReturner, const LibThreadSafe_uint32 nValueBufferSize, LibThreadSafe_uint32* pValueNeededChars, char * pValueBuffer); + +/** +* Function that may crash when called from different threads at the same time +* +* @param[in] pStringReturner - StringReturner instance. +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_stringreturner_threadsafetycheck(LibThreadSafe_StringReturner pStringReturner); + +/************************************************************************************************************************* + Class definition for SoftStringReturner +**************************************************************************************************************************/ + +/** +* Returns a string +* +* @param[in] pSoftStringReturner - SoftStringReturner instance. +* @param[in] nValueBufferSize - size of the buffer (including trailing 0) +* @param[out] pValueNeededChars - will be filled with the count of the written bytes, or needed buffer size. +* @param[out] pValueBuffer - buffer of , may be NULL +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_softstringreturner_getstring(LibThreadSafe_SoftStringReturner pSoftStringReturner, const LibThreadSafe_uint32 nValueBufferSize, LibThreadSafe_uint32* pValueNeededChars, char * pValueBuffer); + +/** +* Function that may crash when called from different threads at the same time +* +* @param[in] pSoftStringReturner - SoftStringReturner instance. +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_softstringreturner_threadsafetycheck(LibThreadSafe_SoftStringReturner pSoftStringReturner); + +/************************************************************************************************************************* + Class definition for StrictStringReturner +**************************************************************************************************************************/ + +/** +* Returns a string +* +* @param[in] pStrictStringReturner - StrictStringReturner instance. +* @param[in] nValueBufferSize - size of the buffer (including trailing 0) +* @param[out] pValueNeededChars - will be filled with the count of the written bytes, or needed buffer size. +* @param[out] pValueBuffer - buffer of , may be NULL +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_strictstringreturner_getstring(LibThreadSafe_StrictStringReturner pStrictStringReturner, const LibThreadSafe_uint32 nValueBufferSize, LibThreadSafe_uint32* pValueNeededChars, char * pValueBuffer); + +/** +* Function that shouldn't crash when called from different threads at the same time +* +* @param[in] pStrictStringReturner - StrictStringReturner instance. +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_strictstringreturner_threadsafetycheck(LibThreadSafe_StrictStringReturner pStrictStringReturner); + +/************************************************************************************************************************* + Global functions +**************************************************************************************************************************/ + +/** +* retrieves the binary version of this library. +* +* @param[out] pMajor - returns the major version of this library +* @param[out] pMinor - returns the minor version of this library +* @param[out] pMicro - returns the micro version of this library +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_getversion(LibThreadSafe_uint32 * pMajor, LibThreadSafe_uint32 * pMinor, LibThreadSafe_uint32 * pMicro); + +/** +* Returns the last error recorded on this object +* +* @param[in] pInstance - Instance Handle +* @param[in] nErrorMessageBufferSize - size of the buffer (including trailing 0) +* @param[out] pErrorMessageNeededChars - will be filled with the count of the written bytes, or needed buffer size. +* @param[out] pErrorMessageBuffer - buffer of Message of the last error, may be NULL +* @param[out] pHasError - Is there a last error to query +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_getlasterror(LibThreadSafe_Base pInstance, const LibThreadSafe_uint32 nErrorMessageBufferSize, LibThreadSafe_uint32* pErrorMessageNeededChars, char * pErrorMessageBuffer, bool * pHasError); + +/** +* Acquire shared ownership of an Instance +* +* @param[in] pInstance - Instance Handle +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_acquireinstance(LibThreadSafe_Base pInstance); + +/** +* Releases shared ownership of an Instance +* +* @param[in] pInstance - Instance Handle +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_releaseinstance(LibThreadSafe_Base pInstance); + +/** +* Creates a new StringReturner instance +* +* @param[out] pInstance - New StringReturner instance +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_createstringreturner(LibThreadSafe_StringReturner * pInstance); + +/** +* Creates a new SoftStringReturner instance +* +* @param[out] pInstance - New SoftStringReturner instance +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_createsoftstringreturner(LibThreadSafe_SoftStringReturner * pInstance); + +/** +* Creates a new StrictStringReturner instance +* +* @param[out] pInstance - New StrictStringReturner instance +* @return error code or 0 (success) +*/ +LIBTHREADSAFE_DECLSPEC LibThreadSafeResult libthreadsafe_createstrictstringreturner(LibThreadSafe_StrictStringReturner * pInstance); + +#ifdef __cplusplus +} +#endif + +#endif // __LIBTHREADSAFE_HEADER_CPP + diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaceexception.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaceexception.cpp new file mode 100644 index 00000000..2bc5aa60 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaceexception.cpp @@ -0,0 +1,45 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++ Implementation file with the basic internal + exception type in order to allow an easy use of Thread-safe library + +Interface version: 1.0.0 + +*/ + + +#include + +#include "libthreadsafe_interfaceexception.hpp" + +/************************************************************************************************************************* + Class ELibThreadSafeInterfaceException +**************************************************************************************************************************/ +ELibThreadSafeInterfaceException::ELibThreadSafeInterfaceException(LibThreadSafeResult errorCode) + : m_errorMessage(LIBTHREADSAFE_GETERRORSTRING (errorCode)) +{ + m_errorCode = errorCode; +} + +ELibThreadSafeInterfaceException::ELibThreadSafeInterfaceException(LibThreadSafeResult errorCode, std::string errorMessage) + : m_errorMessage(errorMessage + " (" + std::to_string (errorCode) + ")") +{ + m_errorCode = errorCode; +} + +LibThreadSafeResult ELibThreadSafeInterfaceException::getErrorCode () +{ + return m_errorCode; +} + +const char * ELibThreadSafeInterfaceException::what () const noexcept +{ + return m_errorMessage.c_str(); +} + diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaceexception.hpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaceexception.hpp new file mode 100644 index 00000000..1c9ac9bf --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaceexception.hpp @@ -0,0 +1,60 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++ Header file with the basic internal + exception type in order to allow an easy use of Thread-safe library + +Interface version: 1.0.0 + +*/ + +#ifndef __LIBTHREADSAFE_INTERFACEEXCEPTION_HEADER +#define __LIBTHREADSAFE_INTERFACEEXCEPTION_HEADER + +#include +#include +#include "libthreadsafe_types.hpp" + +/************************************************************************************************************************* + Class ELibThreadSafeInterfaceException +**************************************************************************************************************************/ + + +class ELibThreadSafeInterfaceException : public std::exception { +protected: + /** + * Error code for the Exception. + */ + LibThreadSafeResult m_errorCode; + /** + * Error message for the Exception. + */ + std::string m_errorMessage; + +public: + /** + * Exception Constructor. + */ + ELibThreadSafeInterfaceException(LibThreadSafeResult errorCode); + + /** + * Custom Exception Constructor. + */ + ELibThreadSafeInterfaceException(LibThreadSafeResult errorCode, std::string errorMessage); + + /** + * Returns error code + */ + LibThreadSafeResult getErrorCode(); + /** + * Returns error message + */ + const char* what() const noexcept override; +}; + +#endif // __LIBTHREADSAFE_INTERFACEEXCEPTION_HEADER diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaces.hpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaces.hpp new file mode 100644 index 00000000..4f6ff875 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfaces.hpp @@ -0,0 +1,406 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++ header file in order to allow easy +development of Thread-safe library. The implementer of Thread-safe library needs to +derive concrete classes from the abstract classes in this header. + +Interface version: 1.0.0 + +*/ + + +#ifndef __LIBTHREADSAFE_CPPINTERFACES +#define __LIBTHREADSAFE_CPPINTERFACES + +#include +#include +#include + +#include "libthreadsafe_types.hpp" + + + +namespace LibThreadSafe { +namespace Impl { + +/** + Forward declarations of class interfaces +*/ +class IBase; +class IStringReturner; +class ISoftStringReturner; +class IStrictStringReturner; + + + +/************************************************************************************************************************* + Parameter Cache definitions +**************************************************************************************************************************/ + +class ParameterCache { + public: + virtual ~ParameterCache() {} +}; + +template class ParameterCache_1 : public ParameterCache { + private: + T1 m_param1; + public: + ParameterCache_1 (const T1 & param1) + : m_param1 (param1) + { + } + + void retrieveData (T1 & param1) + { + param1 = m_param1; + } +}; + +template class ParameterCache_2 : public ParameterCache { + private: + T1 m_param1; + T2 m_param2; + public: + ParameterCache_2 (const T1 & param1, const T2 & param2) + : m_param1 (param1), m_param2 (param2) + { + } + + void retrieveData (T1 & param1, T2 & param2) + { + param1 = m_param1; + param2 = m_param2; + } +}; + +template class ParameterCache_3 : public ParameterCache { + private: + T1 m_param1; + T2 m_param2; + T3 m_param3; + public: + ParameterCache_3 (const T1 & param1, const T2 & param2, const T3 & param3) + : m_param1 (param1), m_param2 (param2), m_param3 (param3) + { + } + + void retrieveData (T1 & param1, T2 & param2, T3 & param3) + { + param1 = m_param1; + param2 = m_param2; + param3 = m_param3; + } +}; + + +/************************************************************************************************************************* + Class interface for Base +**************************************************************************************************************************/ + +class IBase { +protected: + std::unique_ptr m_ParameterCache; +public: + /** + * IBase::~IBase - virtual destructor of IBase + */ + virtual ~IBase() {}; + + /** + * IBase::ReleaseBaseClassInterface - Releases ownership of a base class interface. Deletes the reference, if necessary. + * @param[in] pIBase - The base class instance to release + */ + static void ReleaseBaseClassInterface(IBase* pIBase) + { + if (pIBase) { + pIBase->DecRefCount(); + } + }; + + /** + * IBase::AcquireBaseClassInterface - Acquires shared ownership of a base class interface. + * @param[in] pIBase - The base class instance to acquire + */ + static void AcquireBaseClassInterface(IBase* pIBase) + { + if (pIBase) { + pIBase->IncRefCount(); + } + }; + + + /** + * IBase::GetLastErrorMessage - Returns the last error registered of this class instance + * @param[out] sErrorMessage - Message of the last error registered + * @return Has an error been registered already + */ + virtual bool GetLastErrorMessage(std::string & sErrorMessage) = 0; + + /** + * IBase::ClearErrorMessages - Clears all registered messages of this class instance + */ + virtual void ClearErrorMessages() = 0; + + /** + * IBase::RegisterErrorMessage - Registers an error message with this class instance + * @param[in] sErrorMessage - Error message to register + */ + virtual void RegisterErrorMessage(const std::string & sErrorMessage) = 0; + + /** + * IBase::IncRefCount - Increases the reference count of a class instance + */ + virtual void IncRefCount() = 0; + + /** + * IBase::DecRefCount - Decreases the reference count of a class instance and free releases it, if the last reference has been removed + * @return Has the object been released + */ + virtual bool DecRefCount() = 0; + + /** + * IBase::_setCache - set parameter cache of object + */ + void _setCache(ParameterCache * pCache) + { + m_ParameterCache.reset(pCache); + } + + /** + * IBase::_getCache - returns parameter cache of object + */ + ParameterCache* _getCache() + { + return m_ParameterCache.get(); + } + + /** + * IBase::_lockInstance - If thread safety for class in library is enabled it should lock object for calling thread + */ + virtual void _lockInstance() {} + + /** + * IBase::_unlockInstance - If thread safety for class in library is enabled it should unlock object for other threads + */ + virtual void _unlockInstance() {} + + /** + * IBase::ClassTypeId - Get Class Type Id + * @return Class type as a 64 bits integer + */ + virtual LibThreadSafe_uint64 ClassTypeId() = 0; +}; + + +/** + Definition of a shared pointer class for IBase +*/ +template +class IBaseSharedPtr : public std::shared_ptr +{ +public: + explicit IBaseSharedPtr(T* t = nullptr) + : std::shared_ptr(t, IBase::ReleaseBaseClassInterface) + { + t->IncRefCount(); + } + + // Reset function, as it also needs to properly set the deleter. + void reset(T* t = nullptr) + { + std::shared_ptr::reset(t, IBase::ReleaseBaseClassInterface); + } + + // Get-function that increases the Base class's reference count + T* getCoOwningPtr() + { + T* t = this->get(); + t->IncRefCount(); + return t; + } +}; + + +/** + Definition of a class with mutex for IBase +*/ +class IBaseWithMutex : public virtual IBase +{ +private: + std::mutex m_mutex; +public: + + /** + * IBaseWithMutex::_lockInstance - If thread safety for class in library is enabled it should lock object for calling thread + */ + virtual void _lockInstance() { m_mutex.lock(); } + + /** + * IBaseWithMutex::_unlockInstance - If thread safety for class in library is enabled it should unlock object for other threads + */ + virtual void _unlockInstance() { m_mutex.unlock();} +}; + + +typedef IBaseSharedPtr PIBase; + + +/************************************************************************************************************************* + Class interface for StringReturner +**************************************************************************************************************************/ + +class IStringReturner : public virtual IBase { +public: + /** + * IStringReturner::ClassTypeId - Get Class Type Id + * @return Class type as a 64 bits integer + */ + LibThreadSafe_uint64 ClassTypeId() override + { + return 0x1755067BCC5ED81UL; // First 64 bits of SHA1 of a string: "LibThreadSafe::StringReturner" + } + + /** + * IStringReturner::GetString - Returns a string + * @return + */ + virtual std::string GetString() = 0; + + /** + * IStringReturner::ThreadSafetyCheck - Function that may crash when called from different threads at the same time + */ + virtual void ThreadSafetyCheck() = 0; + +}; + +typedef IBaseSharedPtr PIStringReturner; + + +/************************************************************************************************************************* + Class interface for SoftStringReturner +**************************************************************************************************************************/ + +class ISoftStringReturner : public virtual IBaseWithMutex { +public: + /** + * ISoftStringReturner::ClassTypeId - Get Class Type Id + * @return Class type as a 64 bits integer + */ + LibThreadSafe_uint64 ClassTypeId() override + { + return 0x6C0744711291F198UL; // First 64 bits of SHA1 of a string: "LibThreadSafe::SoftStringReturner" + } + + /** + * ISoftStringReturner::GetString - Returns a string + * @return + */ + virtual std::string GetString() = 0; + + /** + * ISoftStringReturner::ThreadSafetyCheck - Function that may crash when called from different threads at the same time + */ + virtual void ThreadSafetyCheck() = 0; + +}; + +typedef IBaseSharedPtr PISoftStringReturner; + + +/************************************************************************************************************************* + Class interface for StrictStringReturner +**************************************************************************************************************************/ + +class IStrictStringReturner : public virtual IBaseWithMutex { +public: + /** + * IStrictStringReturner::ClassTypeId - Get Class Type Id + * @return Class type as a 64 bits integer + */ + LibThreadSafe_uint64 ClassTypeId() override + { + return 0xC8D8EB62D68DF429UL; // First 64 bits of SHA1 of a string: "LibThreadSafe::StrictStringReturner" + } + + /** + * IStrictStringReturner::GetString - Returns a string + * @return + */ + virtual std::string GetString() = 0; + + /** + * IStrictStringReturner::ThreadSafetyCheck - Function that shouldn't crash when called from different threads at the same time + */ + virtual void ThreadSafetyCheck() = 0; + +}; + +typedef IBaseSharedPtr PIStrictStringReturner; + + +/************************************************************************************************************************* + Global functions declarations +**************************************************************************************************************************/ +class CWrapper { +public: + /** + * Ilibthreadsafe::GetVersion - retrieves the binary version of this library. + * @param[out] nMajor - returns the major version of this library + * @param[out] nMinor - returns the minor version of this library + * @param[out] nMicro - returns the micro version of this library + */ + static void GetVersion(LibThreadSafe_uint32 & nMajor, LibThreadSafe_uint32 & nMinor, LibThreadSafe_uint32 & nMicro); + + /** + * Ilibthreadsafe::GetLastError - Returns the last error recorded on this object + * @param[in] pInstance - Instance Handle + * @param[out] sErrorMessage - Message of the last error + * @return Is there a last error to query + */ + static bool GetLastError(IBase* pInstance, std::string & sErrorMessage); + + /** + * Ilibthreadsafe::AcquireInstance - Acquire shared ownership of an Instance + * @param[in] pInstance - Instance Handle + */ + static void AcquireInstance(IBase* pInstance); + + /** + * Ilibthreadsafe::ReleaseInstance - Releases shared ownership of an Instance + * @param[in] pInstance - Instance Handle + */ + static void ReleaseInstance(IBase* pInstance); + + /** + * Ilibthreadsafe::CreateStringReturner - Creates a new StringReturner instance + * @return New StringReturner instance + */ + static IStringReturner * CreateStringReturner(); + + /** + * Ilibthreadsafe::CreateSoftStringReturner - Creates a new SoftStringReturner instance + * @return New SoftStringReturner instance + */ + static ISoftStringReturner * CreateSoftStringReturner(); + + /** + * Ilibthreadsafe::CreateStrictStringReturner - Creates a new StrictStringReturner instance + * @return New StrictStringReturner instance + */ + static IStrictStringReturner * CreateStrictStringReturner(); + +}; + +LibThreadSafeResult LibThreadSafe_GetProcAddress (const char * pProcName, void ** ppProcAddress); + +} // namespace Impl +} // namespace LibThreadSafe + +#endif // __LIBTHREADSAFE_CPPINTERFACES diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfacewrapper.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfacewrapper.cpp new file mode 100644 index 00000000..57a8c8e1 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_interfacewrapper.cpp @@ -0,0 +1,604 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++ implementation file in order to allow easy +development of Thread-safe library. The functions in this file need to be implemented. It needs to be generated only once. + +Interface version: 1.0.0 + +*/ + +#include "libthreadsafe_abi.hpp" +#include "libthreadsafe_interfaces.hpp" +#include "libthreadsafe_interfaceexception.hpp" + +#include + +using namespace LibThreadSafe::Impl; + +LibThreadSafeResult handleLibThreadSafeException(IBase * pIBaseClass, ELibThreadSafeInterfaceException & Exception) +{ + LibThreadSafeResult errorCode = Exception.getErrorCode(); + + if (pIBaseClass != nullptr) + pIBaseClass->RegisterErrorMessage(Exception.what()); + + return errorCode; +} + +LibThreadSafeResult handleStdException(IBase * pIBaseClass, std::exception & Exception) +{ + LibThreadSafeResult errorCode = LIBTHREADSAFE_ERROR_GENERICEXCEPTION; + + if (pIBaseClass != nullptr) + pIBaseClass->RegisterErrorMessage(Exception.what()); + + return errorCode; +} + +LibThreadSafeResult handleUnhandledException(IBase * pIBaseClass) +{ + LibThreadSafeResult errorCode = LIBTHREADSAFE_ERROR_GENERICEXCEPTION; + + if (pIBaseClass != nullptr) + pIBaseClass->RegisterErrorMessage("Unhandled Exception"); + + return errorCode; +} + + + +/************************************************************************************************************************* + Class implementation for Base +**************************************************************************************************************************/ +LibThreadSafeResult libthreadsafe_base_classtypeid(LibThreadSafe_Base pBase, LibThreadSafe_uint64 * pClassTypeId) +{ + IBase* pIBaseClass = (IBase *)pBase; + + try { + if (pClassTypeId == nullptr) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + IBase* pIBase = dynamic_cast(pIBaseClass); + if (!pIBase) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + + *pClassTypeId = pIBase->ClassTypeId(); + + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_base__lockinstance(LibThreadSafe_Base pBase) +{ + IBase* pIBaseClass = (IBase *)pBase; + + try { + IBase* pIBase = dynamic_cast(pIBaseClass); + if (!pIBase) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + + pIBase->_lockInstance(); + + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_base__unlockinstance(LibThreadSafe_Base pBase) +{ + IBase* pIBaseClass = (IBase *)pBase; + + try { + IBase* pIBase = dynamic_cast(pIBaseClass); + if (!pIBase) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + + pIBase->_unlockInstance(); + + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + + +/************************************************************************************************************************* + Class implementation for StringReturner +**************************************************************************************************************************/ +LibThreadSafeResult libthreadsafe_stringreturner_getstring(LibThreadSafe_StringReturner pStringReturner, const LibThreadSafe_uint32 nValueBufferSize, LibThreadSafe_uint32* pValueNeededChars, char * pValueBuffer) +{ + IBase* pIBaseClass = (IBase *)pStringReturner; + + try { + if ( (!pValueBuffer) && !(pValueNeededChars) ) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + std::string sValue(""); + IStringReturner* pIStringReturner = dynamic_cast(pIBaseClass); + if (!pIStringReturner) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + + bool isCacheCall = (pValueBuffer == nullptr); + if (isCacheCall) { + sValue = pIStringReturner->GetString(); + + pIStringReturner->_setCache (new ParameterCache_1 (sValue)); + } + else { + auto cache = dynamic_cast*> (pIStringReturner->_getCache ()); + if (cache == nullptr) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + cache->retrieveData (sValue); + pIStringReturner->_setCache (nullptr); + } + + if (pValueNeededChars) + *pValueNeededChars = (LibThreadSafe_uint32) (sValue.size()+1); + if (pValueBuffer) { + if (sValue.size() >= nValueBufferSize) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_BUFFERTOOSMALL); + for (size_t iValue = 0; iValue < sValue.size(); iValue++) + pValueBuffer[iValue] = sValue[iValue]; + pValueBuffer[sValue.size()] = 0; + } + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_stringreturner_threadsafetycheck(LibThreadSafe_StringReturner pStringReturner) +{ + IBase* pIBaseClass = (IBase *)pStringReturner; + + try { + IStringReturner* pIStringReturner = dynamic_cast(pIBaseClass); + if (!pIStringReturner) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + + pIStringReturner->ThreadSafetyCheck(); + + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + + +/************************************************************************************************************************* + Class implementation for SoftStringReturner +**************************************************************************************************************************/ +LibThreadSafeResult libthreadsafe_softstringreturner_getstring(LibThreadSafe_SoftStringReturner pSoftStringReturner, const LibThreadSafe_uint32 nValueBufferSize, LibThreadSafe_uint32* pValueNeededChars, char * pValueBuffer) +{ + IBase* pIBaseClass = (IBase *)pSoftStringReturner; + + try { + if ( (!pValueBuffer) && !(pValueNeededChars) ) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + std::string sValue(""); + ISoftStringReturner* pISoftStringReturner = dynamic_cast(pIBaseClass); + if (!pISoftStringReturner) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + + bool isCacheCall = (pValueBuffer == nullptr); + if (isCacheCall) { + sValue = pISoftStringReturner->GetString(); + + pISoftStringReturner->_setCache (new ParameterCache_1 (sValue)); + } + else { + auto cache = dynamic_cast*> (pISoftStringReturner->_getCache ()); + if (cache == nullptr) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + cache->retrieveData (sValue); + pISoftStringReturner->_setCache (nullptr); + } + + if (pValueNeededChars) + *pValueNeededChars = (LibThreadSafe_uint32) (sValue.size()+1); + if (pValueBuffer) { + if (sValue.size() >= nValueBufferSize) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_BUFFERTOOSMALL); + for (size_t iValue = 0; iValue < sValue.size(); iValue++) + pValueBuffer[iValue] = sValue[iValue]; + pValueBuffer[sValue.size()] = 0; + } + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_softstringreturner_threadsafetycheck(LibThreadSafe_SoftStringReturner pSoftStringReturner) +{ + IBase* pIBaseClass = (IBase *)pSoftStringReturner; + + try { + ISoftStringReturner* pISoftStringReturner = dynamic_cast(pIBaseClass); + if (!pISoftStringReturner) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + + pISoftStringReturner->ThreadSafetyCheck(); + + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + + +/************************************************************************************************************************* + Class implementation for StrictStringReturner +**************************************************************************************************************************/ +LibThreadSafeResult libthreadsafe_strictstringreturner_getstring(LibThreadSafe_StrictStringReturner pStrictStringReturner, const LibThreadSafe_uint32 nValueBufferSize, LibThreadSafe_uint32* pValueNeededChars, char * pValueBuffer) +{ + IBase* pIBaseClass = (IBase *)pStrictStringReturner; + + try { + if ( (!pValueBuffer) && !(pValueNeededChars) ) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + std::string sValue(""); + IStrictStringReturner* pIStrictStringReturner = dynamic_cast(pIBaseClass); + if (!pIStrictStringReturner) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + + bool isCacheCall = (pValueBuffer == nullptr); + if (isCacheCall) { + sValue = pIStrictStringReturner->GetString(); + + pIStrictStringReturner->_setCache (new ParameterCache_1 (sValue)); + } + else { + auto cache = dynamic_cast*> (pIStrictStringReturner->_getCache ()); + if (cache == nullptr) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + cache->retrieveData (sValue); + pIStrictStringReturner->_setCache (nullptr); + } + + if (pValueNeededChars) + *pValueNeededChars = (LibThreadSafe_uint32) (sValue.size()+1); + if (pValueBuffer) { + if (sValue.size() >= nValueBufferSize) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_BUFFERTOOSMALL); + for (size_t iValue = 0; iValue < sValue.size(); iValue++) + pValueBuffer[iValue] = sValue[iValue]; + pValueBuffer[sValue.size()] = 0; + } + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_strictstringreturner_threadsafetycheck(LibThreadSafe_StrictStringReturner pStrictStringReturner) +{ + IBase* pIBaseClass = (IBase *)pStrictStringReturner; + + try { + IStrictStringReturner* pIStrictStringReturner = dynamic_cast(pIBaseClass); + if (!pIStrictStringReturner) + throw ELibThreadSafeInterfaceException(LIBTHREADSAFE_ERROR_INVALIDCAST); + + pIStrictStringReturner->ThreadSafetyCheck(); + + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + + + +/************************************************************************************************************************* + Function table lookup implementation +**************************************************************************************************************************/ + +LibThreadSafeResult LibThreadSafe::Impl::LibThreadSafe_GetProcAddress (const char * pProcName, void ** ppProcAddress) +{ + if (pProcName == nullptr) + return LIBTHREADSAFE_ERROR_INVALIDPARAM; + if (ppProcAddress == nullptr) + return LIBTHREADSAFE_ERROR_INVALIDPARAM; + *ppProcAddress = nullptr; + std::string sProcName (pProcName); + + if (sProcName == "libthreadsafe_base_classtypeid") + *ppProcAddress = (void*) &libthreadsafe_base_classtypeid; + if (sProcName == "libthreadsafe_base__lockinstance") + *ppProcAddress = (void*) &libthreadsafe_base__lockinstance; + if (sProcName == "libthreadsafe_base__unlockinstance") + *ppProcAddress = (void*) &libthreadsafe_base__unlockinstance; + if (sProcName == "libthreadsafe_stringreturner_getstring") + *ppProcAddress = (void*) &libthreadsafe_stringreturner_getstring; + if (sProcName == "libthreadsafe_stringreturner_threadsafetycheck") + *ppProcAddress = (void*) &libthreadsafe_stringreturner_threadsafetycheck; + if (sProcName == "libthreadsafe_softstringreturner_getstring") + *ppProcAddress = (void*) &libthreadsafe_softstringreturner_getstring; + if (sProcName == "libthreadsafe_softstringreturner_threadsafetycheck") + *ppProcAddress = (void*) &libthreadsafe_softstringreturner_threadsafetycheck; + if (sProcName == "libthreadsafe_strictstringreturner_getstring") + *ppProcAddress = (void*) &libthreadsafe_strictstringreturner_getstring; + if (sProcName == "libthreadsafe_strictstringreturner_threadsafetycheck") + *ppProcAddress = (void*) &libthreadsafe_strictstringreturner_threadsafetycheck; + if (sProcName == "libthreadsafe_getversion") + *ppProcAddress = (void*) &libthreadsafe_getversion; + if (sProcName == "libthreadsafe_getlasterror") + *ppProcAddress = (void*) &libthreadsafe_getlasterror; + if (sProcName == "libthreadsafe_acquireinstance") + *ppProcAddress = (void*) &libthreadsafe_acquireinstance; + if (sProcName == "libthreadsafe_releaseinstance") + *ppProcAddress = (void*) &libthreadsafe_releaseinstance; + if (sProcName == "libthreadsafe_createstringreturner") + *ppProcAddress = (void*) &libthreadsafe_createstringreturner; + if (sProcName == "libthreadsafe_createsoftstringreturner") + *ppProcAddress = (void*) &libthreadsafe_createsoftstringreturner; + if (sProcName == "libthreadsafe_createstrictstringreturner") + *ppProcAddress = (void*) &libthreadsafe_createstrictstringreturner; + + if (*ppProcAddress == nullptr) + return LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT; + return LIBTHREADSAFE_SUCCESS; +} + +/************************************************************************************************************************* + Global functions implementation +**************************************************************************************************************************/ +LibThreadSafeResult libthreadsafe_getversion(LibThreadSafe_uint32 * pMajor, LibThreadSafe_uint32 * pMinor, LibThreadSafe_uint32 * pMicro) +{ + IBase* pIBaseClass = nullptr; + + try { + if (!pMajor) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + if (!pMinor) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + if (!pMicro) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + CWrapper::GetVersion(*pMajor, *pMinor, *pMicro); + + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_getlasterror(LibThreadSafe_Base pInstance, const LibThreadSafe_uint32 nErrorMessageBufferSize, LibThreadSafe_uint32* pErrorMessageNeededChars, char * pErrorMessageBuffer, bool * pHasError) +{ + IBase* pIBaseClass = nullptr; + + try { + if ( (!pErrorMessageBuffer) && !(pErrorMessageNeededChars) ) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + if (pHasError == nullptr) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + IBase* pIBaseClassInstance = (IBase *)pInstance; + IBase* pIInstance = dynamic_cast(pIBaseClassInstance); + if (!pIInstance) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDCAST); + + std::string sErrorMessage(""); + *pHasError = CWrapper::GetLastError(pIInstance, sErrorMessage); + + if (pErrorMessageNeededChars) + *pErrorMessageNeededChars = (LibThreadSafe_uint32) (sErrorMessage.size()+1); + if (pErrorMessageBuffer) { + if (sErrorMessage.size() >= nErrorMessageBufferSize) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_BUFFERTOOSMALL); + for (size_t iErrorMessage = 0; iErrorMessage < sErrorMessage.size(); iErrorMessage++) + pErrorMessageBuffer[iErrorMessage] = sErrorMessage[iErrorMessage]; + pErrorMessageBuffer[sErrorMessage.size()] = 0; + } + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_acquireinstance(LibThreadSafe_Base pInstance) +{ + IBase* pIBaseClass = nullptr; + + try { + IBase* pIBaseClassInstance = (IBase *)pInstance; + IBase* pIInstance = dynamic_cast(pIBaseClassInstance); + if (!pIInstance) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDCAST); + + CWrapper::AcquireInstance(pIInstance); + + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_releaseinstance(LibThreadSafe_Base pInstance) +{ + IBase* pIBaseClass = nullptr; + + try { + IBase* pIBaseClassInstance = (IBase *)pInstance; + IBase* pIInstance = dynamic_cast(pIBaseClassInstance); + if (!pIInstance) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDCAST); + + CWrapper::ReleaseInstance(pIInstance); + + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_createstringreturner(LibThreadSafe_StringReturner * pInstance) +{ + IBase* pIBaseClass = nullptr; + + try { + if (pInstance == nullptr) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + IBase* pBaseInstance(nullptr); + pBaseInstance = CWrapper::CreateStringReturner(); + + *pInstance = (IBase*)(pBaseInstance); + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_createsoftstringreturner(LibThreadSafe_SoftStringReturner * pInstance) +{ + IBase* pIBaseClass = nullptr; + + try { + if (pInstance == nullptr) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + IBase* pBaseInstance(nullptr); + pBaseInstance = CWrapper::CreateSoftStringReturner(); + + *pInstance = (IBase*)(pBaseInstance); + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibThreadSafeResult libthreadsafe_createstrictstringreturner(LibThreadSafe_StrictStringReturner * pInstance) +{ + IBase* pIBaseClass = nullptr; + + try { + if (pInstance == nullptr) + throw ELibThreadSafeInterfaceException (LIBTHREADSAFE_ERROR_INVALIDPARAM); + IBase* pBaseInstance(nullptr); + pBaseInstance = CWrapper::CreateStrictStringReturner(); + + *pInstance = (IBase*)(pBaseInstance); + return LIBTHREADSAFE_SUCCESS; + } + catch (ELibThreadSafeInterfaceException & Exception) { + return handleLibThreadSafeException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + + diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_types.hpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_types.hpp new file mode 100644 index 00000000..cbc3acd4 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Interfaces/libthreadsafe_types.hpp @@ -0,0 +1,123 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++-Header file with basic types in +order to allow an easy use of Thread-safe library + +Interface version: 1.0.0 + +*/ + +#ifndef __LIBTHREADSAFE_TYPES_HEADER_CPP +#define __LIBTHREADSAFE_TYPES_HEADER_CPP + + +/************************************************************************************************************************* + Scalar types definition +**************************************************************************************************************************/ + +#ifdef LIBTHREADSAFE_USELEGACYINTEGERTYPES + +typedef unsigned char LibThreadSafe_uint8; +typedef unsigned short LibThreadSafe_uint16 ; +typedef unsigned int LibThreadSafe_uint32; +typedef unsigned long long LibThreadSafe_uint64; +typedef char LibThreadSafe_int8; +typedef short LibThreadSafe_int16; +typedef int LibThreadSafe_int32; +typedef long long LibThreadSafe_int64; + +#else // LIBTHREADSAFE_USELEGACYINTEGERTYPES + +#include + +typedef uint8_t LibThreadSafe_uint8; +typedef uint16_t LibThreadSafe_uint16; +typedef uint32_t LibThreadSafe_uint32; +typedef uint64_t LibThreadSafe_uint64; +typedef int8_t LibThreadSafe_int8; +typedef int16_t LibThreadSafe_int16; +typedef int32_t LibThreadSafe_int32; +typedef int64_t LibThreadSafe_int64 ; + +#endif // LIBTHREADSAFE_USELEGACYINTEGERTYPES + +typedef float LibThreadSafe_single; +typedef double LibThreadSafe_double; + +/************************************************************************************************************************* + General type definitions +**************************************************************************************************************************/ + +typedef LibThreadSafe_int32 LibThreadSafeResult; +typedef void * LibThreadSafeHandle; +typedef void * LibThreadSafe_pvoid; + +/************************************************************************************************************************* + Version for LibThreadSafe +**************************************************************************************************************************/ + +#define LIBTHREADSAFE_VERSION_MAJOR 1 +#define LIBTHREADSAFE_VERSION_MINOR 0 +#define LIBTHREADSAFE_VERSION_MICRO 0 +#define LIBTHREADSAFE_VERSION_PRERELEASEINFO "" +#define LIBTHREADSAFE_VERSION_BUILDINFO "" + +/************************************************************************************************************************* + Error constants for LibThreadSafe +**************************************************************************************************************************/ + +#define LIBTHREADSAFE_SUCCESS 0 +#define LIBTHREADSAFE_ERROR_NOTIMPLEMENTED 1 /** functionality not implemented */ +#define LIBTHREADSAFE_ERROR_INVALIDPARAM 2 /** an invalid parameter was passed */ +#define LIBTHREADSAFE_ERROR_INVALIDCAST 3 /** a type cast failed */ +#define LIBTHREADSAFE_ERROR_BUFFERTOOSMALL 4 /** a provided buffer is too small */ +#define LIBTHREADSAFE_ERROR_GENERICEXCEPTION 5 /** a generic exception occurred */ +#define LIBTHREADSAFE_ERROR_COULDNOTLOADLIBRARY 6 /** the library could not be loaded */ +#define LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT 7 /** a required exported symbol could not be found in the library */ +#define LIBTHREADSAFE_ERROR_INCOMPATIBLEBINARYVERSION 8 /** the version of the binary interface does not match the bindings interface */ +#define LIBTHREADSAFE_ERROR_NORESULTAVAILABLE 9 /** no result is available */ +#define LIBTHREADSAFE_ERROR_CALCULATIONABORTED 10 /** a calculation has been aborted */ + +/************************************************************************************************************************* + Error strings for LibThreadSafe +**************************************************************************************************************************/ + +inline const char * LIBTHREADSAFE_GETERRORSTRING (LibThreadSafeResult nErrorCode) { + switch (nErrorCode) { + case LIBTHREADSAFE_SUCCESS: return "no error"; + case LIBTHREADSAFE_ERROR_NOTIMPLEMENTED: return "functionality not implemented"; + case LIBTHREADSAFE_ERROR_INVALIDPARAM: return "an invalid parameter was passed"; + case LIBTHREADSAFE_ERROR_INVALIDCAST: return "a type cast failed"; + case LIBTHREADSAFE_ERROR_BUFFERTOOSMALL: return "a provided buffer is too small"; + case LIBTHREADSAFE_ERROR_GENERICEXCEPTION: return "a generic exception occurred"; + case LIBTHREADSAFE_ERROR_COULDNOTLOADLIBRARY: return "the library could not be loaded"; + case LIBTHREADSAFE_ERROR_COULDNOTFINDLIBRARYEXPORT: return "a required exported symbol could not be found in the library"; + case LIBTHREADSAFE_ERROR_INCOMPATIBLEBINARYVERSION: return "the version of the binary interface does not match the bindings interface"; + case LIBTHREADSAFE_ERROR_NORESULTAVAILABLE: return "no result is available"; + case LIBTHREADSAFE_ERROR_CALCULATIONABORTED: return "a calculation has been aborted"; + default: return "unknown error"; + } +} + +/************************************************************************************************************************* + Declaration of handle classes +**************************************************************************************************************************/ + +typedef LibThreadSafeHandle LibThreadSafe_Base; +typedef LibThreadSafeHandle LibThreadSafe_StringReturner; +typedef LibThreadSafeHandle LibThreadSafe_SoftStringReturner; +typedef LibThreadSafeHandle LibThreadSafe_StrictStringReturner; + +namespace LibThreadSafe { + +} // namespace LibThreadSafe; + +// define legacy C-names for enums, structs and function types + +#endif // __LIBTHREADSAFE_TYPES_HEADER_CPP diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe.cpp new file mode 100644 index 00000000..ebbaf60f --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe.cpp @@ -0,0 +1,68 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated C++ implementation file in order to allow easy +development of Thread-safe library. It needs to be generated only once. + +Interface version: 1.0.0 + +*/ + +#include "libthreadsafe_abi.hpp" +#include "libthreadsafe_interfaces.hpp" +#include "libthreadsafe_interfaceexception.hpp" + +#include "libthreadsafe_stringreturner.hpp" +#include "libthreadsafe_softstringreturner.hpp" +#include "libthreadsafe_strictstringreturner.hpp" + +using namespace LibThreadSafe; +using namespace LibThreadSafe::Impl; + +void CWrapper::GetVersion(LibThreadSafe_uint32 & nMajor, LibThreadSafe_uint32 & nMinor, LibThreadSafe_uint32 & nMicro) +{ + nMajor = LIBTHREADSAFE_VERSION_MAJOR; + nMinor = LIBTHREADSAFE_VERSION_MINOR; + nMicro = LIBTHREADSAFE_VERSION_MICRO; +} + +bool CWrapper::GetLastError(IBase* pInstance, std::string & sErrorMessage) +{ + if (pInstance) { + return pInstance->GetLastErrorMessage (sErrorMessage); + } else { + return false; + } +} + +void CWrapper::AcquireInstance(IBase* pInstance) +{ + IBase::AcquireBaseClassInterface(pInstance); +} + +void CWrapper::ReleaseInstance(IBase* pInstance) +{ + IBase::ReleaseBaseClassInterface(pInstance); +} + +IStringReturner * CWrapper::CreateStringReturner() +{ + return new CStringReturner(); +} + +ISoftStringReturner * CWrapper::CreateSoftStringReturner() +{ + return new CSoftStringReturner(); +} + +IStrictStringReturner * CWrapper::CreateStrictStringReturner() +{ + return new CStrictStringReturner(); +} + + diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_base.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_base.cpp new file mode 100644 index 00000000..9dbb77c9 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_base.cpp @@ -0,0 +1,60 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +Abstract: This is a stub class definition of CBase + +*/ + +#include "libthreadsafe_base.hpp" +#include "libthreadsafe_interfaceexception.hpp" + +// Include custom headers here. + + +using namespace LibThreadSafe::Impl; + +/************************************************************************************************************************* + Class definition of CBase +**************************************************************************************************************************/ + +bool CBase::GetLastErrorMessage(std::string & sErrorMessage) +{ + if (m_pLastError.get() != nullptr) { + sErrorMessage = *m_pLastError; + return true; + } else { + sErrorMessage = ""; + return false; + } +} + +void CBase::ClearErrorMessages() +{ + m_pLastError.reset(); +} + +void CBase::RegisterErrorMessage(const std::string & sErrorMessage) +{ + if (m_pLastError.get() == nullptr) { + m_pLastError.reset(new std::string()); + } + *m_pLastError = sErrorMessage; +} + +void CBase::IncRefCount() +{ + ++m_nReferenceCount; +} + +bool CBase::DecRefCount() +{ + m_nReferenceCount--; + if (!m_nReferenceCount) { + delete this; + return true; + } + return false; +} diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_base.hpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_base.hpp new file mode 100644 index 00000000..beae5e19 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_base.hpp @@ -0,0 +1,73 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +Abstract: This is the class declaration of CBase + +*/ + + +#ifndef __LIBTHREADSAFE_BASE +#define __LIBTHREADSAFE_BASE + +#include "libthreadsafe_interfaces.hpp" +#include +#include +#include + + +// Include custom headers here. + + +namespace LibThreadSafe { +namespace Impl { + + +/************************************************************************************************************************* + Class declaration of CBase +**************************************************************************************************************************/ + +class CBase : public virtual IBase { +private: + + std::unique_ptr m_pLastError; + uint32_t m_nReferenceCount = 1; + + /** + * Put private members here. + */ + +protected: + + /** + * Put protected members here. + */ + +public: + + /** + * Put additional public members here. They will not be visible in the external API. + */ + + bool GetLastErrorMessage(std::string & sErrorMessage) override; + + void ClearErrorMessages() override; + + void RegisterErrorMessage(const std::string & sErrorMessage) override; + + void IncRefCount() override; + + bool DecRefCount() override; + + + /** + * Public member functions to implement. + */ +}; + +} // namespace Impl +} // namespace LibThreadSafe + +#endif // __LIBTHREADSAFE_BASE diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.cpp new file mode 100644 index 00000000..ea8640d1 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.cpp @@ -0,0 +1,33 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +Abstract: This is a stub class definition of CSoftStringReturner + +*/ + +#include "libthreadsafe_softstringreturner.hpp" +#include "libthreadsafe_interfaceexception.hpp" + +// Include custom headers here. + + +using namespace LibThreadSafe::Impl; + +/************************************************************************************************************************* + Class definition of CSoftStringReturner +**************************************************************************************************************************/ + +std::string CSoftStringReturner::GetString() +{ + throw std::string("Get random string"); +} + +void CSoftStringReturner::ThreadSafetyCheck() +{ + values.emplace_back(values.size()); + std::reverse(values.begin(), values.end()); +} + diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.hpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.hpp new file mode 100644 index 00000000..19a59451 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.hpp @@ -0,0 +1,72 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +Abstract: This is the class declaration of CSoftStringReturner + +*/ + + +#ifndef __LIBTHREADSAFE_SOFTSTRINGRETURNER +#define __LIBTHREADSAFE_SOFTSTRINGRETURNER + +#include "libthreadsafe_interfaces.hpp" + +// Parent classes +#include "libthreadsafe_base.hpp" +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4250) +#endif + +// Include custom headers here. +#include + +namespace LibThreadSafe { +namespace Impl { + + +/************************************************************************************************************************* + Class declaration of CSoftStringReturner +**************************************************************************************************************************/ + +class CSoftStringReturner : public virtual ISoftStringReturner, public virtual CBase { +private: + + /** + * Put private members here. + */ + std::vector values; + +protected: + + /** + * Put protected members here. + */ + +public: + + /** + * Put additional public members here. They will not be visible in the external API. + */ + + + /** + * Public member functions to implement. + */ + + std::string GetString() override; + + void ThreadSafetyCheck() override; + +}; + +} // namespace Impl +} // namespace LibThreadSafe + +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif // __LIBTHREADSAFE_SOFTSTRINGRETURNER diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.cpp new file mode 100644 index 00000000..c7d484cb --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.cpp @@ -0,0 +1,33 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +Abstract: This is a stub class definition of CStrictStringReturner + +*/ + +#include "libthreadsafe_strictstringreturner.hpp" +#include "libthreadsafe_interfaceexception.hpp" + +// Include custom headers here. + + +using namespace LibThreadSafe::Impl; + +/************************************************************************************************************************* + Class definition of CStrictStringReturner +**************************************************************************************************************************/ + +std::string CStrictStringReturner::GetString() +{ + throw std::string("Get random string"); +} + +void CStrictStringReturner::ThreadSafetyCheck() +{ + values.emplace_back(values.size()); + std::reverse(values.begin(), values.end()); +} + diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.hpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.hpp new file mode 100644 index 00000000..1348994e --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.hpp @@ -0,0 +1,72 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +Abstract: This is the class declaration of CStrictStringReturner + +*/ + + +#ifndef __LIBTHREADSAFE_STRICTSTRINGRETURNER +#define __LIBTHREADSAFE_STRICTSTRINGRETURNER + +#include "libthreadsafe_interfaces.hpp" + +// Parent classes +#include "libthreadsafe_base.hpp" +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4250) +#endif + +// Include custom headers here. +#include + +namespace LibThreadSafe { +namespace Impl { + + +/************************************************************************************************************************* + Class declaration of CStrictStringReturner +**************************************************************************************************************************/ + +class CStrictStringReturner : public virtual IStrictStringReturner, public virtual CBase { +private: + + /** + * Put private members here. + */ + std::vector values; + +protected: + + /** + * Put protected members here. + */ + +public: + + /** + * Put additional public members here. They will not be visible in the external API. + */ + + + /** + * Public member functions to implement. + */ + + std::string GetString() override; + + void ThreadSafetyCheck() override; + +}; + +} // namespace Impl +} // namespace LibThreadSafe + +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif // __LIBTHREADSAFE_STRICTSTRINGRETURNER diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.cpp new file mode 100644 index 00000000..d705db3e --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.cpp @@ -0,0 +1,33 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +Abstract: This is a stub class definition of CStringReturner + +*/ + +#include "libthreadsafe_stringreturner.hpp" +#include "libthreadsafe_interfaceexception.hpp" + +// Include custom headers here. + + +using namespace LibThreadSafe::Impl; + +/************************************************************************************************************************* + Class definition of CStringReturner +**************************************************************************************************************************/ + +std::string CStringReturner::GetString() +{ + throw std::string("Get random string"); +} + +void CStringReturner::ThreadSafetyCheck() +{ + values.emplace_back(values.size()); + std::reverse(values.begin(), values.end()); +} + diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.hpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.hpp new file mode 100644 index 00000000..9907141a --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.hpp @@ -0,0 +1,72 @@ +/*++ + +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + +Abstract: This is the class declaration of CStringReturner + +*/ + + +#ifndef __LIBTHREADSAFE_STRINGRETURNER +#define __LIBTHREADSAFE_STRINGRETURNER + +#include "libthreadsafe_interfaces.hpp" + +// Parent classes +#include "libthreadsafe_base.hpp" +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4250) +#endif + +// Include custom headers here. +#include + +namespace LibThreadSafe { +namespace Impl { + + +/************************************************************************************************************************* + Class declaration of CStringReturner +**************************************************************************************************************************/ + +class CStringReturner : public virtual IStringReturner, public virtual CBase { +private: + + /** + * Put private members here. + */ + std::vector values; + +protected: + + /** + * Put protected members here. + */ + +public: + + /** + * Put additional public members here. They will not be visible in the external API. + */ + + + /** + * Public member functions to implement. + */ + + std::string GetString() override; + + void ThreadSafetyCheck() override; + +}; + +} // namespace Impl +} // namespace LibThreadSafe + +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif // __LIBTHREADSAFE_STRINGRETURNER diff --git a/Examples/ThreadSafety/LibThreadSafe_component/license.txt b/Examples/ThreadSafety/LibThreadSafe_component/license.txt new file mode 100644 index 00000000..143fe992 --- /dev/null +++ b/Examples/ThreadSafety/LibThreadSafe_component/license.txt @@ -0,0 +1,5 @@ +Copyright (C) 2025 Thread-safe library developers + +All rights reserved. + + diff --git a/Examples/ThreadSafety/Readme.md b/Examples/ThreadSafety/Readme.md new file mode 100644 index 00000000..7f7b6e0f --- /dev/null +++ b/Examples/ThreadSafety/Readme.md @@ -0,0 +1,1185 @@ +# ![ACT logo](../../Documentation/images/ACT_logo_50px.png) Automatic Component Toolkit + +## Tutorial: Generating a C++ shared library and bindings for different languages + + +## Table of Contents + +- [1. Introduction](#1-introduction) +- [2. Requirements](#2-requirements) +- [3. The Library's Implementation](#3-the-librarys-implementation) +- [4. The Consumers](#4-the-consumers) +- [5. Extending the Library with a Callback](#5-extending-the-library-with-a-callback) +- [6. Enable journaling](#6-enable-journaling) +- [7. Conclusion](#7-conclusion) + +# 1. Introduction +This tutorial demonstrates how to set up and develop a shared library using ACT, and it can be followed step by step. +The focus is not on specific features and details of ACT: its parameters, elements, +options and switches are of the interface definition language are documented in the [Documentation](../../Documentation/IDL.md). +Instead, it lays out a potential workflow around ACT. + +If you just want to see a project based no ACT, you can find the final project in the folder [LibPrimes_component](LibPrimes_component). + +Consider you want to implement and use a library that provides datatypes and algorithms related to prime numbers. Such a library will be implemented in this tutorial. +In particular, the library will be able to perform the prime-factorization of positive integers and generate prime numbers. + +#### Prime factor decomposition +Given a positive integer n, find distinct prime numbers p_1, ..., p_k, and positive integers e_1, ..., e_k +such that +``` +n = p_1 ^ e_1 * p_2 ^ e_2 * ... * p_k ^ e_k +``` +The values e_j are called multiplicity. + +__NOTE__ +_There are much more efficient algorithms and more suitable software packages to perform these tasks._ + +# 2. Requirements + - CMake + - A C++ compiler / development environment. This tutorial was tested with Visual Studio 2017, but should also work with `GCC` and `make` or other development tools. + - ACT: This tutorial is tested to work with release 1.6.0 of ACT. You can get it from [the releases page](https://github.com/Autodesk/AutomaticComponentToolkit/releases). +Decide on a location for your tutorial's component to live in, and download the binary for your platform into this folder. Alternatively, stick it somwhere in your `$PATH`. + + +# 3. The Library's Implementation +## 3.1. The definition of the component +An ACT component's interface is fully described by its IDL file. +This section sets up an IDL-file for LibPrimes. + +First, copy the snippet, a bare-bone IDL-file, and save it into libPrimes.xml in your component's folder. +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +It's elements define the following: +- The attributes of `\` define essential properties and naming conventions of the component, and contains all other info about the component. +- `\` define the error codes to be used in the library that will be exposed for it's consumers. +The errors listed in this snippet are required. +- `\` defines the global functions that can be used as entry points into the component. +It must contain an `acquiremethod`, a `releasemethod`, a `versionmethod`, and an `errormethod` with the signatures in this snippet. +They will be explained in [this section](#331-required-steps-for-every-act-component). +The syntax for methods will be explained when we add new classes and functions to the IDL-file now. +- The `baseclassname`-attribute of the `\`-section points to the `\` "Base", which will be the baseclass for all other classes defined in this component. + + +### 3.1.1 A struct for prime factors +In the `\` element, add a struct `PrimeFactor` that encodes a prime number with a multiplicity. +```xml + + + + +``` +### 3.1.1 Calculator +After the `Base`-class in the `\`-element add a class `Calculator` which implements the base class for +the different calculators we will expose in our API. +```xml + + + + + + + + + + +``` +The `GetValue` method returns the current value on which the calculator operates. It has one parameter of type `uint64`, which will be returned. +The `SetValue` method sets the value on which the calculator operates. It has one input parameter of type `uint64`. +The method `Calculate` performs the specific calculation of this calculator. + + +### 3.1.2 The FactorizationCalculator +Add another class `FactorizationCalculator` as a child class of `Calculator`. +```xml + + + + + +``` +We will implement the actual prime-factor decomposition by overwriting the the `Calculate` method of its parent class. +`FactorizationCalculator` introduces one additional method that outputs an array of structs. + +### 3.1.3 The SieveCalculator +Add another class `SieveCalculator` as a child class of `Calculator`. +```xml + + + + + +``` +Again, we will implement the actual calculation of primes by overwriting the the `Calculate` method of its parent class. +`SieveCalculator` introduces one additional method that will return the array of prime numbers of type `uint64`. + +### 3.1.4 Error: no result available +We want to use the error `LIBPRIMES_ERROR_NORESULTAVAILABLE` when a user tries to retrieve +results from a calculator without having performed a calculation before. Thus add a new error +```xml + +``` + + +### 3.1.5 Additions to the global section +The global section requires two more methods, that are used as entry points to the component's functionality: +```xml + + + +``` +`CreateFactorizationCalculator` specifies a global function that will create a new instance of the `FactorizationCalculator`. + +```xml + + + +``` +`CreateSieveCalculator` specifies a global function that will create a new instance of the `SieveCalculator`. + +This concludes the complete specification of LibPrimes's interface. + +__NOTE__ +_You can download the initial IDL-file for libPrimes [here](resources/315/libPrimes.xml)._ + +## 3.2. Automatic Source Code Generation +With the complete interface definition in `libPrimes.xml`, we can now turn on ACT: +```shell +act.exe libPrimes.xml +``` +This generates a folder `LibPrimes_component` with three subfolders, `Bindings`, `Examples` and `Implementation`. + +Let's focus on `Implementation/CPP` for now, which contains a folder `Interfaces` and `Stubs`. + + +### 3.2.1 Interfaces +Consider all files in the `Interfaces` folder read only for your development. +They will be regenerated/overwritten if you run ACT again, and you should never have to modify them manually. +Usually, you will not include them in the source code control system of your component. + +The `libprimes_interfaces.hpp`-file contains all classes from the IDL as pure abstract C++ classes. E.g. have a look at +the interfaces `LibPrimes::ICalculator` and `LibPrimes::IFactorizationCalculator`: +```cpp +/*...*/ +class ICalculator : public virtual IBase { +public: + virtual LibPrimes_uint64 GetValue() = 0; + virtual void SetValue(const LibPrimes_uint64 nValue) = 0; + virtual void Calculate() = 0; +}; + +class IFactorizationCalculator : public virtual IBase, public virtual ICalculator { +public: + virtual void GetPrimeFactors(LibPrimes_uint64 nPrimeFactorsBufferSize, + LibPrimes_uint64* pPrimeFactorsNeededCount, LibPrimes::sPrimeFactor * pPrimeFactorsBuffer) = 0; +}; +/*...*/ +``` + +The `libprimes_interfaceexception.hpp` and `libprimes_interfaceexception.cpp` files contain the definition of the component's exception class. + +The `libprimes_interfacewrapper.cpp` file implements the forwarding of the C89-interface functions to the classes you will implement. +It also translates all exceptions into error codes. +```cpp +LibPrimesResult libprimes_calculator_getvalue(LibPrimes_Calculator pCalculator, LibPrimes_uint64 * pValue) +{ + IBase* pIBaseClass = (IBase *)pCalculator; + try { + if (pValue == nullptr) + throw ELibPrimesInterfaceException (LIBPRIMES_ERROR_INVALIDPARAM); + ICalculator* pICalculator = dynamic_cast(pIBaseClass); + if (!pICalculator) + throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_INVALIDCAST); + *pValue = pICalculator->GetValue(); + return LIBPRIMES_SUCCESS; + } + catch (ELibPrimesInterfaceException & Exception) { + return handleLibPrimesException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} +``` + +### 3.2.2 Implementation Stubs +The files in the `Stubs` folder are the "actual" source code, which you will modify. This will contain your domain logic. + +For each class in the IDL, a pair of header and source files has been generated. +They contain a concrete class definition derived from the corresponding interface in interfaces.hpp. + +```cpp +class CFactorizationCalculator : public virtual IFactorizationCalculator, public virtual CCalculator { +public: + void GetPrimeFactors(LibPrimes_uint64 nPrimeFactorsBufferSize, + LibPrimes_uint64* pPrimeFactorsNeededCount, LibPrimes::sPrimeFactor * pPrimeFactorsBuffer) override; +}; +``` + +The autogenerated implementation of each of a class's methods throws a `NOTIMPLEMENTED` exception: +```cpp +void CFactorizationCalculator::GetPrimeFactors(LibPrimes_uint64 nPrimeFactorsBufferSize, LibPrimes_uint64* pPrimeFactorsNeededCount, LibPrimes::sPrimeFactor * pPrimeFactorsBuffer) +{ + throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_NOTIMPLEMENTED); +} +``` + +## 3.3. Implement and build the library +The `Implementations/CPP` folder already contains a `CMakeLists.txt` that allows you to build the generated sources into a shared library. + +The following code snipped sets up a Visual Studio solution: +```bash +cd LibPrimes_component/Implementation/ +mkdir _build +cd _build +cmake .. -G "Visual Studio 15 Win64" +cmake --build . +``` +Adjust the CMake-Generator for your development environment, if required. + +Now we can start actually implementing the library. + +### 3.3.1. Required steps for every ACT component +#### GetVersion function +```cpp +void CWrapper::GetVersion(LibPrimes_uint32 & nMajor, LibPrimes_uint32 & nMinor, LibPrimes_uint32 & nMicro) +{ + nMajor = LIBPRIMES_VERSION_MAJOR; + nMinor = LIBPRIMES_VERSION_MINOR; + nMicro = LIBPRIMES_VERSION_MICRO; +} +``` +ACT advocates the [semantic versioning scheme](https://semver.org/) and all components defined by ACT provide a function that returns +the major-, minor- and micro-version. `LIBPRIMES_VERSION_*` are defined automatically from the `version` attribute of the `\` in libPrimes.xml. + +In addition an ACT component CAN provide prerelease- and build-information by defining a `prereleasemethod` and `buildinfomethod`, see the [documentation](../../Documentation/IDL.md). + +#### Create-FactorizationCalculator +For all methods in the IDL that are used to return a new instance of a class, code similar to this needs to be implemented. +```cpp +#include "libprimes_factorizationcalculator.hpp" +/*...*/ +IFactorizationCalculator * CWrapper::CreateFactorizationCalculator() +{ + return new CFactorizationCalculator(); +} +``` + +#### Acquire- and Release Function +```cpp +void CWrapper::AcquireInstance(IBase* pInstance) +{ + IBase::AcquireBaseClassInterface(pInstance); +} +``` + +`AcquireBaseClassInterface` increases the reference count of `pInstance`. This can be used to share ownership of `IBase*`-instances. See the [Example for shared ownership and component injection](../Injection/Readme.md) to learn more about this. + +```cpp +void CWrapper::ReleaseInstance(IBase* pInstance) +{ + IBase::ReleaseBaseClassInterface(pInstance); +} +``` +`ReleaseBaseClassInterface` decreases the reference count of `pInstance` and, if this was the last reference, `delete`s the instance. + +__NOTE__: +_Obvioulsy, you can do something more clever/robust, than simply handing out "new"-ed chunks of memory at creation +and deleting them if asked for it via `ReleaseInstance`" +(e.g. store them in a set-datastructure, so that you can "free" them if the consumer does not do so). +However, this is solution is fine in the context of ACT, as all automatically generated bindings +(except `C`) handle the lifetime of all generated IBase instances._ + +#### Error Function +```cpp +bool CWrapper::GetLastError(IBase* pInstance, std::string & sErrorMessage) +{ + return pInstance->GetLastErrorMessage(sErrorMessage); +} +``` + +This method queries the last error that occurred during a method of an instance. +See [4.1.1 Exception/Error handling](#411-exceptionerror-handling) for more details. + + +### 3.3.2. Domain code implementation: Steps for LibPrimes +#### CreateFunctions +Implement the missing `CreateSieveCalculator` +```cpp +#include "libprimes_sievecalculator.hpp" +/*...*/ +ISieveCalculator * CWrapper::CreateSieveCalculator() +{ + return new CSieveCalculator(); +} +``` + +#### Calculator +Add a protected member `m_value` to the `CCalculator` +```cpp +class CCalculator : public virtual ICalculator { +protected: + LibPrimes_uint64 m_value; +/*...*/ +} +``` +The `GetValue`/`SetValue` methods of the `Calculator` are straight-forward: +```cpp +LibPrimes_uint64 CCalculator::GetValue() +{ + return m_value; +} + +void CCalculator::SetValue(const LibPrimes_uint64 nValue) +{ + m_value = nValue; +} +``` + +We can safely leave the `Calculate`-method untouched, or alternatively, declare it pure virtual, +and remove its implementation. + +#### FactorizationCalculator +Add an array that holds the calculated prime factors as private member in `CFactorizationCalculator` +and a public `Calculate` method: +```cpp +class CFactorizationCalculator : public virtual IFactorizationCalculator, public virtual CCalculator { +private: + std::vector primeFactors; +public: + void Calculate() override; +/*...*/ +} +``` + +A valid implementation of `GetPrimes` is the following +```cpp +void CFactorizationCalculator::GetPrimeFactors(LibPrimes_uint64 nPrimeFactorsBufferSize, LibPrimes_uint64* pPrimeFactorsNeededCount, LibPrimes::sPrimeFactor * pPrimeFactorsBuffer) +{ + if (primeFactors.size() == 0) + throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_NORESULTAVAILABLE); + + if (pPrimeFactorsNeededCount) + *pPrimeFactorsNeededCount = (LibPrimes_uint64)primeFactors.size(); + + if (nPrimeFactorsBufferSize >= primeFactors.size() && pPrimeFactorsBuffer) + { + for (int i = 0; i < primeFactors.size(); i++) + { + pPrimeFactorsBuffer[i] = primeFactors[i]; + } + } +} +``` + +The following snippet calculates the prime factor decomposition of the calculator's member `m_value`. +```cpp +void CFactorizationCalculator::Calculate() +{ + primeFactors.clear(); + + LibPrimes_uint64 nValue = GetValue(); + for (LibPrimes_uint64 i = 2; i <= nValue; i++) { + sPrimeFactor primeFactor; + primeFactor.m_Prime = i; + primeFactor.m_Multiplicity = 0; + while (nValue % i == 0) { + primeFactor.m_Multiplicity++; + nValue = nValue / i; + } + if (primeFactor.m_Multiplicity > 0) { + primeFactors.push_back(primeFactor); + } + } +} +``` + +#### SieveCalculator +Add an array that holds the calculated prime numbers as private member in `CSieveCalculator` +and a public `Calculate` method: +```cpp +class CSieveCalculator : public virtual ISieveCalculator, public virtual CCalculator { +private: + std::vector primes; +public: + void Calculate() override; +/*...*/ +} +``` + +The `GetPrimes` method is analogous to the above `GetPrimeFactors` +```cpp +void CSieveCalculator::GetPrimes(LibPrimes_uint64 nPrimesBufferSize, LibPrimes_uint64* pPrimesNeededCount, LibPrimes_uint64 * pPrimesBuffer) +{ + if (primes.size() == 0) + throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_NORESULTAVAILABLE); + if (pPrimesNeededCount) + *pPrimesNeededCount = (LibPrimes_uint64)primes.size(); + if (nPrimesBufferSize >= primes.size() && pPrimesBuffer) + { + for (int i = 0; i < primes.size(); i++) + { + pPrimesBuffer[i] = primes[i]; + } + } +} +``` + +Finally, the following calculate method implements the [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) +```cpp +#include +/* ... */ +void CSieveCalculator::Calculate() +{ + primes.clear(); + + std::vector strikenOut(m_value + 1); + for (LibPrimes_uint64 i = 0; i <= m_value; i++) { + strikenOut[i] = i < 2; + } + LibPrimes_uint64 sqrtValue = (LibPrimes_uint64)(std::sqrt(m_value)); + for (LibPrimes_uint64 i = 2; i <= sqrtValue; i++) { + if (!strikenOut[i]) { + primes.push_back(i); + for (LibPrimes_uint64 j = i * i; j < m_value; j += i) { + strikenOut[j] = true; + } + } + } + for (LibPrimes_uint64 i = sqrtValue; i <= m_value; i++) { + if (!strikenOut[i]) { + primes.push_back(i); + } + } +} +``` + +This concludes the implementation of version `1.0.0` of the library. + +# 4. The Consumers +This section will demonstrate how easy it is to consume "LibPrimes". The autogenerated `Bindings` folder contains multiple language bindings, +i.e. APIs for the component native to different languages. + +By default ACT creates source code for example command line applications in all specified export languages. +Each of the autogenerated example applications will load the library, retrieve the binary's version and print it. + +# 4.1. Cpp Dynamic +The folder `Examples\CppDynamic` contains a simple CMake project and a `LibPrimes_Example.cpp` +that holds the main function of the command line application. +Generate a solution and build it +```shell +cd LibPrimes_component/Examples/CppDynamic +mkdir _build +cd _build +cmake .. -G "Visual Studio 15 Win64" +``` +You will have to modify the path to the binary library file we created in section [3. The Library's Implementation](#3-the-librarys-implementation). +```cpp +#include +#include "libprimes_dynamic.hpp" + +int main() +{ + try + { + std::string libpath = ""; // TODO: put the location of the LibPrimes-library file here. + auto wrapper = LibPrimes::CWrapper::loadLibrary(libpath + "/libprimes."); // TODO: add correct suffix of the library + LibPrimes_uint32 nMajor, nMinor, nMicro; + wrapper->GetVersion(nMajor, nMinor, nMicro); + std::cout << "LibPrimes.Version = " << nMajor << "." << nMinor << "." << nMicro; + std::cout << std::endl; + } + catch (std::exception &e) + { + std::cout << e.what() << std::endl; + return 1; + } + return 0; +} +``` +Now build the solution +```shell +cmake --build . +``` +and run the command line application. It should print out +```shell +LibPrimes.Version = 1.0.0 +``` + +Using LibPrimes's functionality is simple then, e.g. to calculate the prime factor decomposition of a number, +add the following at the end of the try block: +```cpp +auto factorization = wrapper->CreateFactorizationCalculator(); +factorization->SetValue(735); +factorization->Calculate(); +std::vector primeFactors; +factorization->GetPrimeFactors(primeFactors); + +std::cout << factorization->GetValue() << " = "; +for (size_t i = 0; i < primeFactors.size(); i++) { + auto pF = primeFactors[i]; + std::cout << pF.m_Prime << "^" << pF.m_Multiplicity << ((i < (primeFactors.size() - 1)) ? " * " : ""); +} +std::cout << std::endl; +``` +Rebuild the solution and run the compiled application. It should print out +```shell +LibPrimes.Version = 1.0.0 +735 = 3^1 * 5^1 * 7^2 +``` + +Have a look at the implementation of the dynamic Cpp bindings in the single header file `libprimes_dynamic.hpp`, +e.g. a C++ function call is forwarded to the thin C-interface as follows +```cpp +void CFactorizationCalculator::GetPrimeFactors(std::vector & PrimeFactorsBuffer) +{ + LibPrimes_uint64 elementsNeededPrimeFactors = 0; + LibPrimes_uint64 elementsWrittenPrimeFactors = 0; + CheckError(m_pWrapper->m_WrapperTable.m_FactorizationCalculator_GetPrimeFactors(m_pHandle, 0, &elementsNeededPrimeFactors, nullptr)); + PrimeFactorsBuffer.resize((size_t) elementsNeededPrimeFactors); + CheckError(m_pWrapper->m_WrapperTable.m_FactorizationCalculator_GetPrimeFactors(m_pHandle, elementsNeededPrimeFactors, &elementsWrittenPrimeFactors, PrimeFactorsBuffer.data())); +} +``` +__Note__ _For the out-array, `m_FactorizationCalculator_GetPrimeFactors` is called twice. +First, to obtain the size of the array, secondly to actually fill the array with content. +Thus, it would be very inefficient to perform the calculation during the `GetPrimeFactors`-method_ + +# 4.1.1 Exception/Error handling +All return values of functions are translated to C++ exceptions via the `CheckError`-methods of the base-class and the wrapper object: +```cpp +class CBase { +/* ... */ + void CheckError(LibPrimesResult nResult) + { + if (m_pWrapper != nullptr) + m_pWrapper->CheckError(this, nResult); + } +/* ... */ +} +/* ... */ +inline void CWrapper::CheckError(CBase * pBaseClass, LibPrimesResult nResult) +{ + if (nResult != 0) { + std::string sErrorMessage; + if (pBaseClass != nullptr) { + GetLastError(pBaseClass, sErrorMessage); + } + throw ELibPrimesException(nResult, sErrorMessage); + } +} +``` + +# 4.2. Python +The folder `Examples\Python` contains a python script `LibPrimes_Example.py` that makes +use of the Python bindings `LibPrimes.py`. + +In the autogenerated main function set the correct path to the library's binary: +```python +import os +import sys +sys.path.append("../../Bindings/Python") +import LibPrimes + +def main(): + libpath = '' # TODO add the location of the shared library binary here + wrapper = LibPrimes.Wrapper(os.path.join(libpath, "LibPrimes")) + + major, minor, micro = wrapper.GetVersion() + print("LibPrimes version: {:d}.{:d}.{:d}".format(major, minor, micro)) + +if __name__ == "__main__": + try: + main() + except LibPrimes.ELibPrimesException as e: + print(e) +``` + +and add the following code at the end of the main function: +```python +factorization = wrapper.CreateFactorizationCalculator() +factorization.SetValue(735) +factorization.Calculate() +primeFactors = factorization.GetPrimeFactors() +productString = "*" +print("{:d} = ".format(factorization.GetValue()), end="") +for i in range(0, len(primeFactors)): + pF = primeFactors[i] + if i == len(primeFactors) - 1: + productString = "\n" + print(" {:d}^{:d} ".format(pF.Prime, pF.Multiplicity), end=productString) +``` + +Running this script will, again, print: +```shell +LibPrimes version: 1.0.0 +735 = 3^1 * 5^1 * 7^2 +``` + +# 4.2.1. Debug the C++-DLL from a Python Host Application +This section explains a neat trick to debug a library that is used in a unrelated project, even in a different, interpreted language: + +In our specific setup, open the Visual Studio solution from +[3. The Library's Implementation](#3-the-librarys-implementation), +set LibPrimes as startup project and make sure the `Debug` configuration is activated. + +Open the project's properties and select the entry Debug. +Set the `Command` to the path to your python3 executable and the `Command Arguments` +to the location of the `LibPrimes_Example.py` +![VSProperties](resources/VSProperties.png) + +Set a breakpoint somewhere in your C++-library code +(e.g. in `void CFactorizationCalculator::Calculate()`) and start debugging. + +# 4.3. Pascal +The folder `Examples/Pascal` contains a project for the Free Pascal IDE [Lazarus](https://www.lazarus-ide.org/). + +Open the project `LibPrimes_Example.lpi` in the IDE, adjust the library path in the `TestLibPrimes` procedure +and build the application. +```pascal +procedure TLibPrimes_Example.TestLibPrimes (); +var + ALibPrimesWrapper: TLibPrimesWrapper; + AMajor, AMinor, AMicro: Cardinal; + AVersionString: string; + ALibPath: string; +begin + writeln ('loading DLL'); + ALibPath := ''; // TODO add the location of the shared library binary here + ALibPrimesWrapper := TLibPrimesWrapper.Create (ALibPath + '/' + 'libprimes.'); // TODO add the + try + writeln ('loading DLL Done'); + ALibPrimesWrapper.GetVersion(AMajor, AMinor, AMicro); + AVersionString := Format('LibPrimes.version = %d.%d.%d', [AMajor, AMinor, AMicro]); + writeln(AVersionString); + finally + FreeAndNil(ALibPrimesWrapper); + end; +end; +``` + +The compiled command line application `Examples\Pascal\bin\x86_64-win64\Release\LibPrimes_Example.exe` will output +```shell +loading DLL +loading DLL Done +LibPrimes.version = 1.0.0 +``` + +# 4.4. Java +The folder `Examples\Java` contains a minimal java file `LibPrimes_Example.java` that makes +use of the Java bindings as well as a minimal build script to compile bindings and the example and executes the example. +The generated Java bindings requires Java 9 and uses JNA (Java Native Access) to load the library and functions. Therefore the build script loads +the latest jna.jar file and shows how to handle the classpath. + +The autogenerated java class prints the current library version: +```java +import libprimes.*; + +public class LibPrimes_Example { + + public static String libpath = ""; // TODO add the location of the shared library binary here + + public static void main(String[] args) throws LibPrimesException { + LibPrimesWrapper wrapper = new LibPrimesWrapper(libpath); + + LibPrimesWrapper.GetVersionResult version = wrapper.getVersion(); + System.out.print("LibPrimes version: " + version.Major + "." + version.Minor + "." + version.Micro); + System.out.println(); + + FactorizationCalculator factorization = wrapper.createFactorizationCalculator(); + factorization.setValue(735); + factorization.calculate(); + PrimeFactor[] primeFactors = factorization.getPrimeFactors(); + String productString = "*"; + System.out.print(factorization.getValue() + " = "); + for (int i = 0; i < primeFactors.length; i++) { + PrimeFactor pF = primeFactors[i]; + if (i == primeFactors.length - 1) { + productString = "\n"; + } + + System.out.print(" " + pF.Prime + "^" + pF.Multiplicity + " " + productString); + } + } +} +``` + +Running this java program will, again, print: +```shell +LibPrimes version: 1.2.0 +735 = 3^1 * 5^1 * 7^2 +``` + +## 4.5 Use of an ACT component in existing code bases +The examples above started of with prepared and autogenerated projects in three different languages. +We found that a simple task using dynamic language bindings. + +In reality, it is more important to integrate a components functionality into _existing_, +potentially large and complex code bases. + +However, the integration of an ACT component into other code bases is _as simple_ as integrating it into a new application. +The only requirement is to include the specific language binding file +(`libprimes_dyanmic.hpp`, `LibPrimes.py`, `Unit_LibPrimes.pas`) into a project +and specifying the location of the component's binary. + +This concludes the section on using an ACT component via the autogenerated language bindings. + + +# 5. Extending the Library with a Callback +This section goes through the process of adding new functionality to an ACT component. +We will first modify the IDL-file, secondly regenerate the interfaces and language bindings +and finally adapt one of the example applications to use the new feature of the library. + +The new functionality will be a callback that reports the progress during the (potentially) +time consuming calculation method of the calculators. + +# 5.1 IDL +Open the IDL-file `libPrimes.xml` and add a new `functiontype` element within the `\`-element. +```xml + + + + +``` +Instances of this functiontype will require a `single` parameter as input to report +the relative progress of the calculation back to the client, +and have a `bool`ean output value to read back from a client, whether `LibPrimes` +should abort the calculation. + +Then add a method to the `Calculator` class that sets the progress callback: +```xml + + + +``` + +We will handle aborted calculations via a new exceptions, which a client can handle. +Thus, add a new `\`: +```xml + +``` + +In the [semantic versioning scheme](https://semver.org/) adding a new function to a components class +requires a minor version update. Thus update the version in the IDL, too: +```xml + +``` + +__NOTE__ +_You can download the modified IDL-file for libPrimes [here](resources/510/libPrimes.xml)._ + +Finally, recrate interfaces, wrapper and bindings code: +```shell +act.exe libPrimes.xml +``` + +A quick look at the `libprimes_types.hpp` and `libprimes_interfaces.hpp` reveals how the `ProgressCallback` +function type and their usage is declared: +```cpp +/* + * ProgressCallback - Callback to report calculation progress and query whether it should be aborted + * + * @param[in] fProgressPercentage - How far has the calculation progressed? + * @param[out] pShouldAbort - Should the calculation be aborted? + */ + typedef void(*ProgressCallback)(LibPrimes_single, bool *); +``` +```cpp +class ICalculator : public virtual IBase { +public: +/*...*/ +/** +* ICalculator::SetProgressCallback - Sets the progress callback function +* @param[in] pProgressCallback - callback function +*/ +virtual void SetProgressCallback(const LibPrimes::ProgressCallback pProgressCallback) = 0; +/*...*/ +} +``` + +# 5.2 Library +Reopen the Visual Studio solution from [3. The Library's Implementation](#3-the-librarys-implementation). +You will not be able to successfully rebuild the solution, +since `CCalculator` does not define the `SetProgressCallback`-function. +To resolve this, add a protected member to `CCalculator` and define the public +`SetProgressCallback`-function. +#### libprimes_calculator.hpp +```cpp +class CCalculator : public virtual ICalculator, public virtual CBase { +protected: +/*...*/ + ProgressCallback m_Callback; +/*...*/ +public: +/*...*/ + void SetProgressCallback(const ProgressCallback pProgressCallback) override; +/*...*/ +} +``` +#### libprimes_calculator.cpp +```cpp +void CCalculator::SetProgressCallback (const LibPrimes::ProgressCallback pProgressCallback) +{ + m_Callback = pProgressCallback; +} +``` + +We can use the callback in the calculation function for example like this: +```cpp +void CFactorizationCalculator::Calculate() +{ + primeFactors.clear(); + + LibPrimes_uint64 nValue = m_value; + for (LibPrimes_uint64 i = 2; i <= nValue; i++) { + + if (m_Callback) { + bool shouldAbort = false; + (*m_Callback)(1 - float(nValue) / m_value, &shouldAbort); + if (shouldAbort) { + throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_CALCULATIONABORTED); + } + } + + sPrimeFactor primeFactor; + primeFactor.m_Prime = i; + primeFactor.m_Multiplicity = 0; + while (nValue % i == 0) { + primeFactor.m_Multiplicity++; + nValue = nValue / i; + } + if (primeFactor.m_Multiplicity > 0) { + primeFactors.push_back(primeFactor); + } + } +} +``` +__Note__ +_C function pointers have to be handled carefully. +It's good practice to wrap them in more convenient function objects that make their usage safe +(i.e. check that the function pointer itself as well as their out-parameters are assigned). +Later versions of ACT might do that._ + +Now, recompile the solution. + +# 5.3 Consumer +The usage of the callback functionality from the client applications is straightforward, +as their respective language bindings have already been updated at the end of the previous step. + +## 5.3.1 Cpp Dynamic Application with Callback +Open the solution from [Section 4.1](#41-cpp-dynamic), and add a concrete implementation of the +"ProgressCallback" function-type: +```cpp +#include +/* ... */ +void progressCallback(LibPrimes_single progress, bool* shouldAbort) +{ + std::cout << "Progress = " << std::round(progress * 100) << "%" << std::endl; + if (shouldAbort) { + *shouldAbort = progress > 0.5; + } +} +``` + +Using it in `main` is straightforward: +```cpp +int main() +{ + /*...*/ + factorization->SetValue(735); + factorization->SetProgressCallback(progressCallback); + factorization->Calculate(); + /*...*/ +} +``` + +Running the compiled command line application will output: +```shell +LibPrimes.Version = 1.1.0 +Progress = 0% +Progress = 0% +Progress = 67% +LibPrimes Error 10 (LibPrimes Error 10) +``` +Error 10 (`CALCULATIONABORTED`) notifies us that a calculation has been aborted, as we expected. + +## 5.3.2 Python Application with a callback +Open the Python example from [Section 4.2](42-python), and add an implementation of the +a Python function: +```python +def progressCallback(progress, shouldAbort): + print("Progress = {:d}%".format(round(progress*100))) + if (shouldAbort is not None): + shouldAbort[0] = progress > 0.5 +``` + +Using it in `main` requires one to first create a CTypes function pointer `cTypesCallback` to the Python function. +You need to make sure that these CTypes function pointer still exist in the python host application, +when it will called by the library, e.g. like this: +```python +# ... +factorization.SetValue(735) +cTypesCallback = LibPrimes.ProgressCallback(progressCallback) +factorization.SetProgressCallback(cTypesCallback) +factorization.Calculate() +# ... +``` + +Running the python script will output: +```shell +LibPrimes version: 1.1.0 +Progress = 0% +Progress = 0% +Progress = 67% +LibPrimesException 10: LibPrimes Error 10 +``` + +# 6. Enable journaling +ACT provides automatic journaling, that can be compiled into the software component +and enabled/disabled dynamically during the usage of the component. + + +## 6.1 Update the IDL file +To add the journaling, simply add a `journalmethod` to the IDL file `libPrimes.xml`: +```xml + + + + + + +``` +Since a new function has been added, another minor version update is required. +```xml + +``` + +__NOTE__ +_You can download the modified IDL-file for libPrimes [here](resources/610/libPrimes.xml)._ + +Regenerate the implementation and language bindings by running +```shell +act.exe libPrimes.xml +``` + +Check the updated `libprimes_interfacewrapper.cpp` and its autogenerated +SetJournal method +```cpp +LibPrimesResult libprimes_setjournal(const char * pFileName) +{ + IBase* pIBaseClass = nullptr; + try { + if (pFileName == nullptr) + throw ELibPrimesInterfaceException (LIBPRIMES_ERROR_INVALIDPARAM); + std::string sFileName(pFileName); + m_GlobalJournal = nullptr; + if (sFileName != "") { + m_GlobalJournal = std::make_shared (sFileName); + } + return LIBPRIMES_SUCCESS; + } + catch (ELibPrimesInterfaceException & Exception) { + return handleLibPrimesException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} +``` +__Note__ _The journalmethod is not forwarded to a C++ class but completely handled +in the wrapper itself._ + +This is how the journal is filled by other methods: +```cpp +LibPrimesResult libprimes_calculator_getvalue(LibPrimes_Calculator pCalculator, LibPrimes_uint64 * pValue) +{ + IBase* pIBaseClass = (IBase *)pCalculator; + PLibPrimesInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pCalculator, "Calculator", "GetValue"); + } + if (pValue == nullptr) + throw ELibPrimesInterfaceException (LIBPRIMES_ERROR_INVALIDPARAM); + ICalculator* pICalculator = dynamic_cast(pIBaseClass); + if (!pICalculator) + throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_INVALIDCAST); + *pValue = pICalculator->GetValue(); + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addUInt64Result("Value", *pValue); + pJournalEntry->writeSuccess(); + } + return LIBPRIMES_SUCCESS; + } + catch (ELibPrimesInterfaceException & Exception) { + return handleLibPrimesException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} +``` + +Before you can rebuild the LibPrimes shared library, add the journals implementation-file +to the `${LIBPRIMES_SRC}` in the `CMakeLists.txt`-file: +```shell +set(LIBPRIMES_SRC ${LIBPRIMES_SRC} + ${CMAKE_CURRENT_AUTOGENERATED_DIR}/libprimes_interfaceexception.cpp + ${CMAKE_CURRENT_AUTOGENERATED_DIR}/libprimes_interfacewrapper.cpp + ${CMAKE_CURRENT_AUTOGENERATED_DIR}/libprimes_interfacejournal.cpp +) +``` + +Journaling can be a great way to help resolving bugs in software components +once they are used by a other people. + +## 6.3 Use journaling in a client +Simply add a call of the `SetJournal`-method in the clients' code: + +### 6.3.1 CppDynamic Test application +```cpp +int main() +{ + try + { + std::string libpath = (""); // TODO: put the location of the LibPrimes-library file here. + auto wrapper = LibPrimes::CWrapper::loadLibrary(libpath + "/libprimes."); // TODO: add correct suffix of the library + wrapper->SetJournal("journal_cppdynamic.xml"); + LibPrimes_uint32 nMajor, nMinor, nMicro; + std::string sPreReleaseInfo, sBuildInfo; + wrapper->GetVersion(nMajor, nMinor, nMicro, sPreReleaseInfo, sBuildInfo); + /*..*/ + } + /*..*/ +} +``` + +The generated xml-file is a detailed journal of all calls, parameters, return values and exceptions +that go through the interface: +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +### 6.3.2 Python Test application +Again, simply call the `SetJournal`-method and run the script: +```python +def main(): + libpath = '' # TODO add the location of the shared library binary here + wrapper = LibPrimes.LibPrimesWrapper(os.path.join(libpath, "libprimes")) + wrapper.SetJournal('journal_python.xml') + major, minor, micro = wrapper.GetVersion() + print("LibPrimes version: {:d}.{:d}.{:d}".format(major, minor, micro), end="") + # ... +``` +The generated xml-journal is similar to the one shown above. + +# 7. Conclusion +This tutorial has walked you through a basic development cycle using ACT +and conveyed the power of ACT to simplify and automate the development of software components. +Moreover, it has shown how easy ACT components can be integrated in standalone or existing +code bases with ease. + +Other important aspects of software componentization, like packaging and distribution, +source code control, stable interfaces, versioning, releases, ... +can also be supported and simplified by ACT. + +Tutorials, articles and examples about these topics will follow. diff --git a/Examples/ThreadSafety/threadSafeLibrary.xml b/Examples/ThreadSafety/threadSafeLibrary.xml new file mode 100644 index 00000000..083994cd --- /dev/null +++ b/Examples/ThreadSafety/threadSafeLibrary.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 53693e37e5a18dd3a43eab6bdd84c9a9bd3265bc Mon Sep 17 00:00:00 2001 From: uck Date: Tue, 10 Jun 2025 19:39:38 +0200 Subject: [PATCH 05/11] Add exeample readme for threadsafetyoption parameter: --- .../CppDynamic/LibThreadSafe_example.cpp | 5 +- Examples/ThreadSafety/Readme.md | 1231 ++--------------- 2 files changed, 86 insertions(+), 1150 deletions(-) diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp index 2e3f0eb3..e63a7162 100644 --- a/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp +++ b/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp @@ -46,11 +46,10 @@ void testThreadSafetyCheck(ArrayReturner& returner) int main() { - std::string libpath = ("C:/Users/uck/Dev/Repos/ActTask/MyFork/AutomaticComponentToolkit/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/out/build/x64-Debug"); // TODO: put the location of the LibThreadSafe-library file here. - auto wrapper = LibThreadSafe::CWrapper::loadLibrary(libpath + "/libthreadsafe.dll"); // TODO: add correct suffix of the library + std::string libpath = (""); // TODO: put the location of the LibThreadSafe-library file here. + auto wrapper = LibThreadSafe::CWrapper::loadLibrary(libpath + ""); // TODO: add correct suffix of the library LibThreadSafe::PStringReturner stringReturner = wrapper->CreateStringReturner(); LibThreadSafe::PSoftStringReturner softStringReturner = wrapper->CreateSoftStringReturner(); LibThreadSafe::PStrictStringReturner strictStringReturner = wrapper->CreateStrictStringReturner(); - testThreadSafetyCheck(strictStringReturner); } diff --git a/Examples/ThreadSafety/Readme.md b/Examples/ThreadSafety/Readme.md index 7f7b6e0f..c5259b95 100644 --- a/Examples/ThreadSafety/Readme.md +++ b/Examples/ThreadSafety/Readme.md @@ -1,1185 +1,122 @@ # ![ACT logo](../../Documentation/images/ACT_logo_50px.png) Automatic Component Toolkit -## Tutorial: Generating a C++ shared library and bindings for different languages +## Tutorial: Usage of the `threadsafetyoption`class parameter ## Table of Contents -- [1. Introduction](#1-introduction) -- [2. Requirements](#2-requirements) -- [3. The Library's Implementation](#3-the-librarys-implementation) -- [4. The Consumers](#4-the-consumers) -- [5. Extending the Library with a Callback](#5-extending-the-library-with-a-callback) -- [6. Enable journaling](#6-enable-journaling) -- [7. Conclusion](#7-conclusion) +- [1. Problem](#1-problem) +- [2. Solution](#2-solution) +- [3. Example](#3-example) +- [4. Support](#4-support) + +# 1. Problem +The Automatic Component Toolkit (ACT) has an issue with thread safety when returning strings (arrays) from +functions or methods, especially in the C++ Binding/Implementation. The problem arises from the caching mechanism +used in ACT. When API functions or methods that return strings are executed concurrently from multiple +threads in the library consumer code, it can result in crashes. + +# 2. Solution +### 2.1. Overview +To address the thread safety issue described above, a new parameter called `threadsafetyoption` has been introduced. +This parameter ensures thread safety by adding mutex locking mechanisms on the library implementation side. + +### 2.2. Details +The ThreadSafetyOption parameter can be set to either `none`, `soft` or `strict` values. Here is the difference them: +- `none`: Does nothing. +- `soft`: The binding side will call the lock mechanism only when a string is returned from an API function. +- `strict`: The binding side will call the lock mechanism every time, regardless of what is returned from the function. + +When the ThreadSafetyOption attribute in the LibraryManager component class is set to `soft` or `strict`, +the implementation will derive from a base class that includes mutex locking mechanisms. +This approach ensures that the mutexes are locked and unlocked on the implementation side. +This solution ensures thread safety both when a single pointer on the binding side is shared across threads, +and when different pointers that point to the same object on the implementation side are used in different threads. +Additionally, error handling mechanisms are in place to unlock the mutex in case of exception propagation, preventing deadlocks. + +# 3. Example +### 3.1. Component definition +In the `threadSafeLibrary.xml` file, there are three classes defined with the same methods but different `threadsafetyoption` values: +- `StringReturner` with `threadsafetyoption` set to `none`. +- `SoftStringReturner` with `threadsafetyoption` set to `soft`. +- `StrictStringReturner`: with `threadsafetyoption` set to `strict`. -# 1. Introduction -This tutorial demonstrates how to set up and develop a shared library using ACT, and it can be followed step by step. -The focus is not on specific features and details of ACT: its parameters, elements, -options and switches are of the interface definition language are documented in the [Documentation](../../Documentation/IDL.md). -Instead, it lays out a potential workflow around ACT. - -If you just want to see a project based no ACT, you can find the final project in the folder [LibPrimes_component](LibPrimes_component). - -Consider you want to implement and use a library that provides datatypes and algorithms related to prime numbers. Such a library will be implemented in this tutorial. -In particular, the library will be able to perform the prime-factorization of positive integers and generate prime numbers. - -#### Prime factor decomposition -Given a positive integer n, find distinct prime numbers p_1, ..., p_k, and positive integers e_1, ..., e_k -such that -``` -n = p_1 ^ e_1 * p_2 ^ e_2 * ... * p_k ^ e_k -``` -The values e_j are called multiplicity. - -__NOTE__ -_There are much more efficient algorithms and more suitable software packages to perform these tasks._ - -# 2. Requirements - - CMake - - A C++ compiler / development environment. This tutorial was tested with Visual Studio 2017, but should also work with `GCC` and `make` or other development tools. - - ACT: This tutorial is tested to work with release 1.6.0 of ACT. You can get it from [the releases page](https://github.com/Autodesk/AutomaticComponentToolkit/releases). -Decide on a location for your tutorial's component to live in, and download the binary for your platform into this folder. Alternatively, stick it somwhere in your `$PATH`. - - -# 3. The Library's Implementation -## 3.1. The definition of the component -An ACT component's interface is fully described by its IDL file. -This section sets up an IDL-file for LibPrimes. - -First, copy the snippet, a bare-bone IDL-file, and save it into libPrimes.xml in your component's folder. ```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -It's elements define the following: -- The attributes of `\` define essential properties and naming conventions of the component, and contains all other info about the component. -- `\` define the error codes to be used in the library that will be exposed for it's consumers. -The errors listed in this snippet are required. -- `\` defines the global functions that can be used as entry points into the component. -It must contain an `acquiremethod`, a `releasemethod`, a `versionmethod`, and an `errormethod` with the signatures in this snippet. -They will be explained in [this section](#331-required-steps-for-every-act-component). -The syntax for methods will be explained when we add new classes and functions to the IDL-file now. -- The `baseclassname`-attribute of the `\`-section points to the `\` "Base", which will be the baseclass for all other classes defined in this component. - - -### 3.1.1 A struct for prime factors -In the `\` element, add a struct `PrimeFactor` that encodes a prime number with a multiplicity. -```xml - - - - -``` -### 3.1.1 Calculator -After the `Base`-class in the `\`-element add a class `Calculator` which implements the base class for -the different calculators we will expose in our API. -```xml - - - - - - - - + + + + -``` -The `GetValue` method returns the current value on which the calculator operates. It has one parameter of type `uint64`, which will be returned. -The `SetValue` method sets the value on which the calculator operates. It has one input parameter of type `uint64`. -The method `Calculate` performs the specific calculation of this calculator. - - -### 3.1.2 The FactorizationCalculator -Add another class `FactorizationCalculator` as a child class of `Calculator`. -```xml - - - + + + + -``` -We will implement the actual prime-factor decomposition by overwriting the the `Calculate` method of its parent class. -`FactorizationCalculator` introduces one additional method that outputs an array of structs. - -### 3.1.3 The SieveCalculator -Add another class `SieveCalculator` as a child class of `Calculator`. -```xml - - - + + + + ``` -Again, we will implement the actual calculation of primes by overwriting the the `Calculate` method of its parent class. -`SieveCalculator` introduces one additional method that will return the array of prime numbers of type `uint64`. - -### 3.1.4 Error: no result available -We want to use the error `LIBPRIMES_ERROR_NORESULTAVAILABLE` when a user tries to retrieve -results from a calculator without having performed a calculation before. Thus add a new error -```xml - -``` - - -### 3.1.5 Additions to the global section -The global section requires two more methods, that are used as entry points to the component's functionality: -```xml - - - -``` -`CreateFactorizationCalculator` specifies a global function that will create a new instance of the `FactorizationCalculator`. - -```xml - - - -``` -`CreateSieveCalculator` specifies a global function that will create a new instance of the `SieveCalculator`. - -This concludes the complete specification of LibPrimes's interface. - -__NOTE__ -_You can download the initial IDL-file for libPrimes [here](resources/315/libPrimes.xml)._ - -## 3.2. Automatic Source Code Generation -With the complete interface definition in `libPrimes.xml`, we can now turn on ACT: -```shell -act.exe libPrimes.xml -``` -This generates a folder `LibPrimes_component` with three subfolders, `Bindings`, `Examples` and `Implementation`. -Let's focus on `Implementation/CPP` for now, which contains a folder `Interfaces` and `Stubs`. +Each class contains the following methods: +- `GetString` which simply returns a random string. +- `ThreadSafetyCheck` which returns nothing but may crash when executed from different threads simultaneously, as no thread safety mechanism is provided in the library implementation. +### 3.2. Usage +Let's consider below cpp example from `LibThreadSafe_example.cpp`. -### 3.2.1 Interfaces -Consider all files in the `Interfaces` folder read only for your development. -They will be regenerated/overwritten if you run ACT again, and you should never have to modify them manually. -Usually, you will not include them in the source code control system of your component. - -The `libprimes_interfaces.hpp`-file contains all classes from the IDL as pure abstract C++ classes. E.g. have a look at -the interfaces `LibPrimes::ICalculator` and `LibPrimes::IFactorizationCalculator`: -```cpp -/*...*/ -class ICalculator : public virtual IBase { -public: - virtual LibPrimes_uint64 GetValue() = 0; - virtual void SetValue(const LibPrimes_uint64 nValue) = 0; - virtual void Calculate() = 0; -}; - -class IFactorizationCalculator : public virtual IBase, public virtual ICalculator { -public: - virtual void GetPrimeFactors(LibPrimes_uint64 nPrimeFactorsBufferSize, - LibPrimes_uint64* pPrimeFactorsNeededCount, LibPrimes::sPrimeFactor * pPrimeFactorsBuffer) = 0; -}; -/*...*/ -``` - -The `libprimes_interfaceexception.hpp` and `libprimes_interfaceexception.cpp` files contain the definition of the component's exception class. - -The `libprimes_interfacewrapper.cpp` file implements the forwarding of the C89-interface functions to the classes you will implement. -It also translates all exceptions into error codes. -```cpp -LibPrimesResult libprimes_calculator_getvalue(LibPrimes_Calculator pCalculator, LibPrimes_uint64 * pValue) -{ - IBase* pIBaseClass = (IBase *)pCalculator; - try { - if (pValue == nullptr) - throw ELibPrimesInterfaceException (LIBPRIMES_ERROR_INVALIDPARAM); - ICalculator* pICalculator = dynamic_cast(pIBaseClass); - if (!pICalculator) - throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_INVALIDCAST); - *pValue = pICalculator->GetValue(); - return LIBPRIMES_SUCCESS; - } - catch (ELibPrimesInterfaceException & Exception) { - return handleLibPrimesException(pIBaseClass, Exception); - } - catch (std::exception & StdException) { - return handleStdException(pIBaseClass, StdException); - } - catch (...) { - return handleUnhandledException(pIBaseClass); - } -} -``` - -### 3.2.2 Implementation Stubs -The files in the `Stubs` folder are the "actual" source code, which you will modify. This will contain your domain logic. - -For each class in the IDL, a pair of header and source files has been generated. -They contain a concrete class definition derived from the corresponding interface in interfaces.hpp. - -```cpp -class CFactorizationCalculator : public virtual IFactorizationCalculator, public virtual CCalculator { -public: - void GetPrimeFactors(LibPrimes_uint64 nPrimeFactorsBufferSize, - LibPrimes_uint64* pPrimeFactorsNeededCount, LibPrimes::sPrimeFactor * pPrimeFactorsBuffer) override; -}; -``` - -The autogenerated implementation of each of a class's methods throws a `NOTIMPLEMENTED` exception: -```cpp -void CFactorizationCalculator::GetPrimeFactors(LibPrimes_uint64 nPrimeFactorsBufferSize, LibPrimes_uint64* pPrimeFactorsNeededCount, LibPrimes::sPrimeFactor * pPrimeFactorsBuffer) -{ - throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_NOTIMPLEMENTED); -} -``` - -## 3.3. Implement and build the library -The `Implementations/CPP` folder already contains a `CMakeLists.txt` that allows you to build the generated sources into a shared library. - -The following code snipped sets up a Visual Studio solution: -```bash -cd LibPrimes_component/Implementation/ -mkdir _build -cd _build -cmake .. -G "Visual Studio 15 Win64" -cmake --build . -``` -Adjust the CMake-Generator for your development environment, if required. - -Now we can start actually implementing the library. - -### 3.3.1. Required steps for every ACT component -#### GetVersion function -```cpp -void CWrapper::GetVersion(LibPrimes_uint32 & nMajor, LibPrimes_uint32 & nMinor, LibPrimes_uint32 & nMicro) -{ - nMajor = LIBPRIMES_VERSION_MAJOR; - nMinor = LIBPRIMES_VERSION_MINOR; - nMicro = LIBPRIMES_VERSION_MICRO; -} -``` -ACT advocates the [semantic versioning scheme](https://semver.org/) and all components defined by ACT provide a function that returns -the major-, minor- and micro-version. `LIBPRIMES_VERSION_*` are defined automatically from the `version` attribute of the `\` in libPrimes.xml. - -In addition an ACT component CAN provide prerelease- and build-information by defining a `prereleasemethod` and `buildinfomethod`, see the [documentation](../../Documentation/IDL.md). - -#### Create-FactorizationCalculator -For all methods in the IDL that are used to return a new instance of a class, code similar to this needs to be implemented. -```cpp -#include "libprimes_factorizationcalculator.hpp" -/*...*/ -IFactorizationCalculator * CWrapper::CreateFactorizationCalculator() -{ - return new CFactorizationCalculator(); -} -``` - -#### Acquire- and Release Function -```cpp -void CWrapper::AcquireInstance(IBase* pInstance) -{ - IBase::AcquireBaseClassInterface(pInstance); -} -``` - -`AcquireBaseClassInterface` increases the reference count of `pInstance`. This can be used to share ownership of `IBase*`-instances. See the [Example for shared ownership and component injection](../Injection/Readme.md) to learn more about this. - -```cpp -void CWrapper::ReleaseInstance(IBase* pInstance) -{ - IBase::ReleaseBaseClassInterface(pInstance); -} -``` -`ReleaseBaseClassInterface` decreases the reference count of `pInstance` and, if this was the last reference, `delete`s the instance. - -__NOTE__: -_Obvioulsy, you can do something more clever/robust, than simply handing out "new"-ed chunks of memory at creation -and deleting them if asked for it via `ReleaseInstance`" -(e.g. store them in a set-datastructure, so that you can "free" them if the consumer does not do so). -However, this is solution is fine in the context of ACT, as all automatically generated bindings -(except `C`) handle the lifetime of all generated IBase instances._ - -#### Error Function -```cpp -bool CWrapper::GetLastError(IBase* pInstance, std::string & sErrorMessage) -{ - return pInstance->GetLastErrorMessage(sErrorMessage); -} -``` - -This method queries the last error that occurred during a method of an instance. -See [4.1.1 Exception/Error handling](#411-exceptionerror-handling) for more details. - - -### 3.3.2. Domain code implementation: Steps for LibPrimes -#### CreateFunctions -Implement the missing `CreateSieveCalculator` -```cpp -#include "libprimes_sievecalculator.hpp" -/*...*/ -ISieveCalculator * CWrapper::CreateSieveCalculator() -{ - return new CSieveCalculator(); -} -``` - -#### Calculator -Add a protected member `m_value` to the `CCalculator` -```cpp -class CCalculator : public virtual ICalculator { -protected: - LibPrimes_uint64 m_value; -/*...*/ -} -``` -The `GetValue`/`SetValue` methods of the `Calculator` are straight-forward: -```cpp -LibPrimes_uint64 CCalculator::GetValue() -{ - return m_value; -} - -void CCalculator::SetValue(const LibPrimes_uint64 nValue) -{ - m_value = nValue; -} -``` - -We can safely leave the `Calculate`-method untouched, or alternatively, declare it pure virtual, -and remove its implementation. - -#### FactorizationCalculator -Add an array that holds the calculated prime factors as private member in `CFactorizationCalculator` -and a public `Calculate` method: -```cpp -class CFactorizationCalculator : public virtual IFactorizationCalculator, public virtual CCalculator { -private: - std::vector primeFactors; -public: - void Calculate() override; -/*...*/ -} -``` - -A valid implementation of `GetPrimes` is the following -```cpp -void CFactorizationCalculator::GetPrimeFactors(LibPrimes_uint64 nPrimeFactorsBufferSize, LibPrimes_uint64* pPrimeFactorsNeededCount, LibPrimes::sPrimeFactor * pPrimeFactorsBuffer) -{ - if (primeFactors.size() == 0) - throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_NORESULTAVAILABLE); - - if (pPrimeFactorsNeededCount) - *pPrimeFactorsNeededCount = (LibPrimes_uint64)primeFactors.size(); - - if (nPrimeFactorsBufferSize >= primeFactors.size() && pPrimeFactorsBuffer) - { - for (int i = 0; i < primeFactors.size(); i++) - { - pPrimeFactorsBuffer[i] = primeFactors[i]; - } - } -} -``` - -The following snippet calculates the prime factor decomposition of the calculator's member `m_value`. -```cpp -void CFactorizationCalculator::Calculate() -{ - primeFactors.clear(); - - LibPrimes_uint64 nValue = GetValue(); - for (LibPrimes_uint64 i = 2; i <= nValue; i++) { - sPrimeFactor primeFactor; - primeFactor.m_Prime = i; - primeFactor.m_Multiplicity = 0; - while (nValue % i == 0) { - primeFactor.m_Multiplicity++; - nValue = nValue / i; - } - if (primeFactor.m_Multiplicity > 0) { - primeFactors.push_back(primeFactor); - } - } -} -``` - -#### SieveCalculator -Add an array that holds the calculated prime numbers as private member in `CSieveCalculator` -and a public `Calculate` method: -```cpp -class CSieveCalculator : public virtual ISieveCalculator, public virtual CCalculator { -private: - std::vector primes; -public: - void Calculate() override; -/*...*/ -} -``` - -The `GetPrimes` method is analogous to the above `GetPrimeFactors` -```cpp -void CSieveCalculator::GetPrimes(LibPrimes_uint64 nPrimesBufferSize, LibPrimes_uint64* pPrimesNeededCount, LibPrimes_uint64 * pPrimesBuffer) -{ - if (primes.size() == 0) - throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_NORESULTAVAILABLE); - if (pPrimesNeededCount) - *pPrimesNeededCount = (LibPrimes_uint64)primes.size(); - if (nPrimesBufferSize >= primes.size() && pPrimesBuffer) - { - for (int i = 0; i < primes.size(); i++) - { - pPrimesBuffer[i] = primes[i]; - } - } -} -``` - -Finally, the following calculate method implements the [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) -```cpp -#include -/* ... */ -void CSieveCalculator::Calculate() -{ - primes.clear(); - - std::vector strikenOut(m_value + 1); - for (LibPrimes_uint64 i = 0; i <= m_value; i++) { - strikenOut[i] = i < 2; - } - LibPrimes_uint64 sqrtValue = (LibPrimes_uint64)(std::sqrt(m_value)); - for (LibPrimes_uint64 i = 2; i <= sqrtValue; i++) { - if (!strikenOut[i]) { - primes.push_back(i); - for (LibPrimes_uint64 j = i * i; j < m_value; j += i) { - strikenOut[j] = true; - } - } - } - for (LibPrimes_uint64 i = sqrtValue; i <= m_value; i++) { - if (!strikenOut[i]) { - primes.push_back(i); - } - } -} -``` - -This concludes the implementation of version `1.0.0` of the library. - -# 4. The Consumers -This section will demonstrate how easy it is to consume "LibPrimes". The autogenerated `Bindings` folder contains multiple language bindings, -i.e. APIs for the component native to different languages. - -By default ACT creates source code for example command line applications in all specified export languages. -Each of the autogenerated example applications will load the library, retrieve the binary's version and print it. - -# 4.1. Cpp Dynamic -The folder `Examples\CppDynamic` contains a simple CMake project and a `LibPrimes_Example.cpp` -that holds the main function of the command line application. -Generate a solution and build it -```shell -cd LibPrimes_component/Examples/CppDynamic -mkdir _build -cd _build -cmake .. -G "Visual Studio 15 Win64" -``` -You will have to modify the path to the binary library file we created in section [3. The Library's Implementation](#3-the-librarys-implementation). ```cpp +#include +#include #include -#include "libprimes_dynamic.hpp" - -int main() -{ - try - { - std::string libpath = ""; // TODO: put the location of the LibPrimes-library file here. - auto wrapper = LibPrimes::CWrapper::loadLibrary(libpath + "/libprimes."); // TODO: add correct suffix of the library - LibPrimes_uint32 nMajor, nMinor, nMicro; - wrapper->GetVersion(nMajor, nMinor, nMicro); - std::cout << "LibPrimes.Version = " << nMajor << "." << nMinor << "." << nMicro; - std::cout << std::endl; - } - catch (std::exception &e) - { - std::cout << e.what() << std::endl; - return 1; - } - return 0; -} -``` -Now build the solution -```shell -cmake --build . -``` -and run the command line application. It should print out -```shell -LibPrimes.Version = 1.0.0 -``` - -Using LibPrimes's functionality is simple then, e.g. to calculate the prime factor decomposition of a number, -add the following at the end of the try block: -```cpp -auto factorization = wrapper->CreateFactorizationCalculator(); -factorization->SetValue(735); -factorization->Calculate(); -std::vector primeFactors; -factorization->GetPrimeFactors(primeFactors); - -std::cout << factorization->GetValue() << " = "; -for (size_t i = 0; i < primeFactors.size(); i++) { - auto pF = primeFactors[i]; - std::cout << pF.m_Prime << "^" << pF.m_Multiplicity << ((i < (primeFactors.size() - 1)) ? " * " : ""); -} -std::cout << std::endl; -``` -Rebuild the solution and run the compiled application. It should print out -```shell -LibPrimes.Version = 1.0.0 -735 = 3^1 * 5^1 * 7^2 -``` +#include "libthreadsafe_dynamic.hpp" -Have a look at the implementation of the dynamic Cpp bindings in the single header file `libprimes_dynamic.hpp`, -e.g. a C++ function call is forwarded to the thin C-interface as follows -```cpp -void CFactorizationCalculator::GetPrimeFactors(std::vector & PrimeFactorsBuffer) +template +void testStringReturn(ArrayReturner& returner) { - LibPrimes_uint64 elementsNeededPrimeFactors = 0; - LibPrimes_uint64 elementsWrittenPrimeFactors = 0; - CheckError(m_pWrapper->m_WrapperTable.m_FactorizationCalculator_GetPrimeFactors(m_pHandle, 0, &elementsNeededPrimeFactors, nullptr)); - PrimeFactorsBuffer.resize((size_t) elementsNeededPrimeFactors); - CheckError(m_pWrapper->m_WrapperTable.m_FactorizationCalculator_GetPrimeFactors(m_pHandle, elementsNeededPrimeFactors, &elementsWrittenPrimeFactors, PrimeFactorsBuffer.data())); -} -``` -__Note__ _For the out-array, `m_FactorizationCalculator_GetPrimeFactors` is called twice. -First, to obtain the size of the array, secondly to actually fill the array with content. -Thus, it would be very inefficient to perform the calculation during the `GetPrimeFactors`-method_ - -# 4.1.1 Exception/Error handling -All return values of functions are translated to C++ exceptions via the `CheckError`-methods of the base-class and the wrapper object: -```cpp -class CBase { -/* ... */ - void CheckError(LibPrimesResult nResult) - { - if (m_pWrapper != nullptr) - m_pWrapper->CheckError(this, nResult); - } -/* ... */ -} -/* ... */ -inline void CWrapper::CheckError(CBase * pBaseClass, LibPrimesResult nResult) -{ - if (nResult != 0) { - std::string sErrorMessage; - if (pBaseClass != nullptr) { - GetLastError(pBaseClass, sErrorMessage); - } - throw ELibPrimesException(nResult, sErrorMessage); - } -} -``` - -# 4.2. Python -The folder `Examples\Python` contains a python script `LibPrimes_Example.py` that makes -use of the Python bindings `LibPrimes.py`. - -In the autogenerated main function set the correct path to the library's binary: -```python -import os -import sys -sys.path.append("../../Bindings/Python") -import LibPrimes - -def main(): - libpath = '' # TODO add the location of the shared library binary here - wrapper = LibPrimes.Wrapper(os.path.join(libpath, "LibPrimes")) - - major, minor, micro = wrapper.GetVersion() - print("LibPrimes version: {:d}.{:d}.{:d}".format(major, minor, micro)) - -if __name__ == "__main__": - try: - main() - except LibPrimes.ELibPrimesException as e: - print(e) -``` - -and add the following code at the end of the main function: -```python -factorization = wrapper.CreateFactorizationCalculator() -factorization.SetValue(735) -factorization.Calculate() -primeFactors = factorization.GetPrimeFactors() -productString = "*" -print("{:d} = ".format(factorization.GetValue()), end="") -for i in range(0, len(primeFactors)): - pF = primeFactors[i] - if i == len(primeFactors) - 1: - productString = "\n" - print(" {:d}^{:d} ".format(pF.Prime, pF.Multiplicity), end=productString) -``` - -Running this script will, again, print: -```shell -LibPrimes version: 1.0.0 -735 = 3^1 * 5^1 * 7^2 -``` - -# 4.2.1. Debug the C++-DLL from a Python Host Application -This section explains a neat trick to debug a library that is used in a unrelated project, even in a different, interpreted language: - -In our specific setup, open the Visual Studio solution from -[3. The Library's Implementation](#3-the-librarys-implementation), -set LibPrimes as startup project and make sure the `Debug` configuration is activated. - -Open the project's properties and select the entry Debug. -Set the `Command` to the path to your python3 executable and the `Command Arguments` -to the location of the `LibPrimes_Example.py` -![VSProperties](resources/VSProperties.png) - -Set a breakpoint somewhere in your C++-library code -(e.g. in `void CFactorizationCalculator::Calculate()`) and start debugging. - -# 4.3. Pascal -The folder `Examples/Pascal` contains a project for the Free Pascal IDE [Lazarus](https://www.lazarus-ide.org/). - -Open the project `LibPrimes_Example.lpi` in the IDE, adjust the library path in the `TestLibPrimes` procedure -and build the application. -```pascal -procedure TLibPrimes_Example.TestLibPrimes (); -var - ALibPrimesWrapper: TLibPrimesWrapper; - AMajor, AMinor, AMicro: Cardinal; - AVersionString: string; - ALibPath: string; -begin - writeln ('loading DLL'); - ALibPath := ''; // TODO add the location of the shared library binary here - ALibPrimesWrapper := TLibPrimesWrapper.Create (ALibPath + '/' + 'libprimes.'); // TODO add the - try - writeln ('loading DLL Done'); - ALibPrimesWrapper.GetVersion(AMajor, AMinor, AMicro); - AVersionString := Format('LibPrimes.version = %d.%d.%d', [AMajor, AMinor, AMicro]); - writeln(AVersionString); - finally - FreeAndNil(ALibPrimesWrapper); - end; -end; -``` - -The compiled command line application `Examples\Pascal\bin\x86_64-win64\Release\LibPrimes_Example.exe` will output -```shell -loading DLL -loading DLL Done -LibPrimes.version = 1.0.0 -``` - -# 4.4. Java -The folder `Examples\Java` contains a minimal java file `LibPrimes_Example.java` that makes -use of the Java bindings as well as a minimal build script to compile bindings and the example and executes the example. -The generated Java bindings requires Java 9 and uses JNA (Java Native Access) to load the library and functions. Therefore the build script loads -the latest jna.jar file and shows how to handle the classpath. - -The autogenerated java class prints the current library version: -```java -import libprimes.*; - -public class LibPrimes_Example { - - public static String libpath = ""; // TODO add the location of the shared library binary here - - public static void main(String[] args) throws LibPrimesException { - LibPrimesWrapper wrapper = new LibPrimesWrapper(libpath); - - LibPrimesWrapper.GetVersionResult version = wrapper.getVersion(); - System.out.print("LibPrimes version: " + version.Major + "." + version.Minor + "." + version.Micro); - System.out.println(); - - FactorizationCalculator factorization = wrapper.createFactorizationCalculator(); - factorization.setValue(735); - factorization.calculate(); - PrimeFactor[] primeFactors = factorization.getPrimeFactors(); - String productString = "*"; - System.out.print(factorization.getValue() + " = "); - for (int i = 0; i < primeFactors.length; i++) { - PrimeFactor pF = primeFactors[i]; - if (i == primeFactors.length - 1) { - productString = "\n"; + std::vector threads; + for (int i = 0; i < 5; ++i) { + threads.emplace_back([&returner]() { + for (int j = 0; j < 10000; ++j) { + returner->GetString(); } - - System.out.print(" " + pF.Prime + "^" + pF.Multiplicity + " " + productString); - } + }); } } -``` - -Running this java program will, again, print: -```shell -LibPrimes version: 1.2.0 -735 = 3^1 * 5^1 * 7^2 -``` - -## 4.5 Use of an ACT component in existing code bases -The examples above started of with prepared and autogenerated projects in three different languages. -We found that a simple task using dynamic language bindings. - -In reality, it is more important to integrate a components functionality into _existing_, -potentially large and complex code bases. - -However, the integration of an ACT component into other code bases is _as simple_ as integrating it into a new application. -The only requirement is to include the specific language binding file -(`libprimes_dyanmic.hpp`, `LibPrimes.py`, `Unit_LibPrimes.pas`) into a project -and specifying the location of the component's binary. - -This concludes the section on using an ACT component via the autogenerated language bindings. - - -# 5. Extending the Library with a Callback -This section goes through the process of adding new functionality to an ACT component. -We will first modify the IDL-file, secondly regenerate the interfaces and language bindings -and finally adapt one of the example applications to use the new feature of the library. - -The new functionality will be a callback that reports the progress during the (potentially) -time consuming calculation method of the calculators. - -# 5.1 IDL -Open the IDL-file `libPrimes.xml` and add a new `functiontype` element within the `\`-element. -```xml - - - - -``` -Instances of this functiontype will require a `single` parameter as input to report -the relative progress of the calculation back to the client, -and have a `bool`ean output value to read back from a client, whether `LibPrimes` -should abort the calculation. - -Then add a method to the `Calculator` class that sets the progress callback: -```xml - - - -``` - -We will handle aborted calculations via a new exceptions, which a client can handle. -Thus, add a new `\`: -```xml - -``` - -In the [semantic versioning scheme](https://semver.org/) adding a new function to a components class -requires a minor version update. Thus update the version in the IDL, too: -```xml - -``` - -__NOTE__ -_You can download the modified IDL-file for libPrimes [here](resources/510/libPrimes.xml)._ - -Finally, recrate interfaces, wrapper and bindings code: -```shell -act.exe libPrimes.xml -``` - -A quick look at the `libprimes_types.hpp` and `libprimes_interfaces.hpp` reveals how the `ProgressCallback` -function type and their usage is declared: -```cpp -/* - * ProgressCallback - Callback to report calculation progress and query whether it should be aborted - * - * @param[in] fProgressPercentage - How far has the calculation progressed? - * @param[out] pShouldAbort - Should the calculation be aborted? - */ - typedef void(*ProgressCallback)(LibPrimes_single, bool *); -``` -```cpp -class ICalculator : public virtual IBase { -public: -/*...*/ -/** -* ICalculator::SetProgressCallback - Sets the progress callback function -* @param[in] pProgressCallback - callback function -*/ -virtual void SetProgressCallback(const LibPrimes::ProgressCallback pProgressCallback) = 0; -/*...*/ -} -``` - -# 5.2 Library -Reopen the Visual Studio solution from [3. The Library's Implementation](#3-the-librarys-implementation). -You will not be able to successfully rebuild the solution, -since `CCalculator` does not define the `SetProgressCallback`-function. -To resolve this, add a protected member to `CCalculator` and define the public -`SetProgressCallback`-function. -#### libprimes_calculator.hpp -```cpp -class CCalculator : public virtual ICalculator, public virtual CBase { -protected: -/*...*/ - ProgressCallback m_Callback; -/*...*/ -public: -/*...*/ - void SetProgressCallback(const ProgressCallback pProgressCallback) override; -/*...*/ -} -``` -#### libprimes_calculator.cpp -```cpp -void CCalculator::SetProgressCallback (const LibPrimes::ProgressCallback pProgressCallback) -{ - m_Callback = pProgressCallback; -} -``` -We can use the callback in the calculation function for example like this: -```cpp -void CFactorizationCalculator::Calculate() +template +void testThreadSafetyCheck(ArrayReturner& returner) { - primeFactors.clear(); - - LibPrimes_uint64 nValue = m_value; - for (LibPrimes_uint64 i = 2; i <= nValue; i++) { - - if (m_Callback) { - bool shouldAbort = false; - (*m_Callback)(1 - float(nValue) / m_value, &shouldAbort); - if (shouldAbort) { - throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_CALCULATIONABORTED); + std::vector threads; + for (int i = 0; i < 5; ++i) { + threads.emplace_back([&returner]() { + for (int j = 0; j < 10000; ++j) { + returner->ThreadSafetyCheck(); } - } - - sPrimeFactor primeFactor; - primeFactor.m_Prime = i; - primeFactor.m_Multiplicity = 0; - while (nValue % i == 0) { - primeFactor.m_Multiplicity++; - nValue = nValue / i; - } - if (primeFactor.m_Multiplicity > 0) { - primeFactors.push_back(primeFactor); - } + }); } } -``` -__Note__ -_C function pointers have to be handled carefully. -It's good practice to wrap them in more convenient function objects that make their usage safe -(i.e. check that the function pointer itself as well as their out-parameters are assigned). -Later versions of ACT might do that._ - -Now, recompile the solution. - -# 5.3 Consumer -The usage of the callback functionality from the client applications is straightforward, -as their respective language bindings have already been updated at the end of the previous step. -## 5.3.1 Cpp Dynamic Application with Callback -Open the solution from [Section 4.1](#41-cpp-dynamic), and add a concrete implementation of the -"ProgressCallback" function-type: -```cpp -#include -/* ... */ -void progressCallback(LibPrimes_single progress, bool* shouldAbort) -{ - std::cout << "Progress = " << std::round(progress * 100) << "%" << std::endl; - if (shouldAbort) { - *shouldAbort = progress > 0.5; - } -} -``` - -Using it in `main` is straightforward: -```cpp int main() { - /*...*/ - factorization->SetValue(735); - factorization->SetProgressCallback(progressCallback); - factorization->Calculate(); - /*...*/ -} -``` - -Running the compiled command line application will output: -```shell -LibPrimes.Version = 1.1.0 -Progress = 0% -Progress = 0% -Progress = 67% -LibPrimes Error 10 (LibPrimes Error 10) -``` -Error 10 (`CALCULATIONABORTED`) notifies us that a calculation has been aborted, as we expected. - -## 5.3.2 Python Application with a callback -Open the Python example from [Section 4.2](42-python), and add an implementation of the -a Python function: -```python -def progressCallback(progress, shouldAbort): - print("Progress = {:d}%".format(round(progress*100))) - if (shouldAbort is not None): - shouldAbort[0] = progress > 0.5 -``` - -Using it in `main` requires one to first create a CTypes function pointer `cTypesCallback` to the Python function. -You need to make sure that these CTypes function pointer still exist in the python host application, -when it will called by the library, e.g. like this: -```python -# ... -factorization.SetValue(735) -cTypesCallback = LibPrimes.ProgressCallback(progressCallback) -factorization.SetProgressCallback(cTypesCallback) -factorization.Calculate() -# ... -``` - -Running the python script will output: -```shell -LibPrimes version: 1.1.0 -Progress = 0% -Progress = 0% -Progress = 67% -LibPrimesException 10: LibPrimes Error 10 -``` - -# 6. Enable journaling -ACT provides automatic journaling, that can be compiled into the software component -and enabled/disabled dynamically during the usage of the component. - - -## 6.1 Update the IDL file -To add the journaling, simply add a `journalmethod` to the IDL file `libPrimes.xml`: -```xml - - - - - - -``` -Since a new function has been added, another minor version update is required. -```xml - -``` - -__NOTE__ -_You can download the modified IDL-file for libPrimes [here](resources/610/libPrimes.xml)._ - -Regenerate the implementation and language bindings by running -```shell -act.exe libPrimes.xml -``` - -Check the updated `libprimes_interfacewrapper.cpp` and its autogenerated -SetJournal method -```cpp -LibPrimesResult libprimes_setjournal(const char * pFileName) -{ - IBase* pIBaseClass = nullptr; - try { - if (pFileName == nullptr) - throw ELibPrimesInterfaceException (LIBPRIMES_ERROR_INVALIDPARAM); - std::string sFileName(pFileName); - m_GlobalJournal = nullptr; - if (sFileName != "") { - m_GlobalJournal = std::make_shared (sFileName); - } - return LIBPRIMES_SUCCESS; - } - catch (ELibPrimesInterfaceException & Exception) { - return handleLibPrimesException(pIBaseClass, Exception); - } - catch (std::exception & StdException) { - return handleStdException(pIBaseClass, StdException); - } - catch (...) { - return handleUnhandledException(pIBaseClass); - } -} -``` -__Note__ _The journalmethod is not forwarded to a C++ class but completely handled -in the wrapper itself._ + std::string libpath = ("Path to library"); + auto wrapper = LibThreadSafe::CWrapper::loadLibrary(libpath + ".dll"); -This is how the journal is filled by other methods: -```cpp -LibPrimesResult libprimes_calculator_getvalue(LibPrimes_Calculator pCalculator, LibPrimes_uint64 * pValue) -{ - IBase* pIBaseClass = (IBase *)pCalculator; - PLibPrimesInterfaceJournalEntry pJournalEntry; - try { - if (m_GlobalJournal.get() != nullptr) { - pJournalEntry = m_GlobalJournal->beginClassMethod(pCalculator, "Calculator", "GetValue"); - } - if (pValue == nullptr) - throw ELibPrimesInterfaceException (LIBPRIMES_ERROR_INVALIDPARAM); - ICalculator* pICalculator = dynamic_cast(pIBaseClass); - if (!pICalculator) - throw ELibPrimesInterfaceException(LIBPRIMES_ERROR_INVALIDCAST); - *pValue = pICalculator->GetValue(); - if (pJournalEntry.get() != nullptr) { - pJournalEntry->addUInt64Result("Value", *pValue); - pJournalEntry->writeSuccess(); - } - return LIBPRIMES_SUCCESS; - } - catch (ELibPrimesInterfaceException & Exception) { - return handleLibPrimesException(pIBaseClass, Exception, pJournalEntry.get()); - } - catch (std::exception & StdException) { - return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); - } - catch (...) { - return handleUnhandledException(pIBaseClass, pJournalEntry.get()); - } + LibThreadSafe::PStringReturner stringReturner = wrapper->CreateStringReturner(); + LibThreadSafe::PSoftStringReturner softStringReturner = wrapper->CreateSoftStringReturner(); + LibThreadSafe::PStrictStringReturner strictStringReturner = wrapper->CreateStrictStringReturner(); } ``` -Before you can rebuild the LibPrimes shared library, add the journals implementation-file -to the `${LIBPRIMES_SRC}` in the `CMakeLists.txt`-file: -```shell -set(LIBPRIMES_SRC ${LIBPRIMES_SRC} - ${CMAKE_CURRENT_AUTOGENERATED_DIR}/libprimes_interfaceexception.cpp - ${CMAKE_CURRENT_AUTOGENERATED_DIR}/libprimes_interfacewrapper.cpp - ${CMAKE_CURRENT_AUTOGENERATED_DIR}/libprimes_interfacejournal.cpp -) -``` - -Journaling can be a great way to help resolving bugs in software components -once they are used by a other people. - -## 6.3 Use journaling in a client -Simply add a call of the `SetJournal`-method in the clients' code: - -### 6.3.1 CppDynamic Test application -```cpp -int main() -{ - try - { - std::string libpath = (""); // TODO: put the location of the LibPrimes-library file here. - auto wrapper = LibPrimes::CWrapper::loadLibrary(libpath + "/libprimes."); // TODO: add correct suffix of the library - wrapper->SetJournal("journal_cppdynamic.xml"); - LibPrimes_uint32 nMajor, nMinor, nMicro; - std::string sPreReleaseInfo, sBuildInfo; - wrapper->GetVersion(nMajor, nMinor, nMicro, sPreReleaseInfo, sBuildInfo); - /*..*/ - } - /*..*/ -} -``` - -The generated xml-file is a detailed journal of all calls, parameters, return values and exceptions -that go through the interface: -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -### 6.3.2 Python Test application -Again, simply call the `SetJournal`-method and run the script: -```python -def main(): - libpath = '' # TODO add the location of the shared library binary here - wrapper = LibPrimes.LibPrimesWrapper(os.path.join(libpath, "libprimes")) - wrapper.SetJournal('journal_python.xml') - major, minor, micro = wrapper.GetVersion() - print("LibPrimes version: {:d}.{:d}.{:d}".format(major, minor, micro), end="") - # ... -``` -The generated xml-journal is similar to the one shown above. - -# 7. Conclusion -This tutorial has walked you through a basic development cycle using ACT -and conveyed the power of ACT to simplify and automate the development of software components. -Moreover, it has shown how easy ACT components can be integrated in standalone or existing -code bases with ease. - -Other important aspects of software componentization, like packaging and distribution, -source code control, stable interfaces, versioning, releases, ... -can also be supported and simplified by ACT. +Executing `testStringReturn` and `testThreadSafetyCheck` with `stringReturner` will crash because there is no thread safety mechanism enabled for this instance. +Executing `testStringReturn` with `softStringReturner` will work as expected. However, it will crash on `testThreadSafetyCheck` because with the `soft` +value of the `threadsafetyoption` parameter, the thread safety mechanism will lock the instance on the library side only when executing functions that return strings. +Executing `testStringReturn` or `testThreadSafetyCheck` on `strictStringReturner` will work as expected in both cases because with the `strict` value of +the `threadsafetyoption`, the instance on the implementation side will be locked during each function execution. -Tutorials, articles and examples about these topics will follow. +# 4. Support +Currently, the `threadsafetyoption` parameter is supported only for Cpp/CppDynamic bindings and Cpp implementation. From 731856ca1dcaf5a4c8b3db9a1f10f1b26eb888f3 Mon Sep 17 00:00:00 2001 From: uck Date: Wed, 11 Jun 2025 09:45:04 +0200 Subject: [PATCH 06/11] Return string not throw exception --- .../Examples/CppDynamic/LibThreadSafe_example.cpp | 4 ++-- .../Cpp/Stub/libthreadsafe_softstringreturner.cpp | 2 +- .../Cpp/Stub/libthreadsafe_strictstringreturner.cpp | 2 +- .../Implementations/Cpp/Stub/libthreadsafe_stringreturner.cpp | 2 +- Examples/ThreadSafety/Readme.md | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp index e63a7162..14a882b9 100644 --- a/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp +++ b/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp @@ -27,7 +27,7 @@ void testStringReturn(ArrayReturner& returner) for (int j = 0; j < 10000; ++j) { returner->GetString(); } - }); + }); } } @@ -40,7 +40,7 @@ void testThreadSafetyCheck(ArrayReturner& returner) for (int j = 0; j < 10000; ++j) { returner->ThreadSafetyCheck(); } - }); + }); } } diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.cpp index ea8640d1..e8ca9d41 100644 --- a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.cpp +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_softstringreturner.cpp @@ -22,7 +22,7 @@ using namespace LibThreadSafe::Impl; std::string CSoftStringReturner::GetString() { - throw std::string("Get random string"); + return std::string("Get random string"); } void CSoftStringReturner::ThreadSafetyCheck() diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.cpp index c7d484cb..06a61359 100644 --- a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.cpp +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_strictstringreturner.cpp @@ -22,7 +22,7 @@ using namespace LibThreadSafe::Impl; std::string CStrictStringReturner::GetString() { - throw std::string("Get random string"); + return std::string("Get random string"); } void CStrictStringReturner::ThreadSafetyCheck() diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.cpp index d705db3e..285de91e 100644 --- a/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.cpp +++ b/Examples/ThreadSafety/LibThreadSafe_component/Implementations/Cpp/Stub/libthreadsafe_stringreturner.cpp @@ -22,7 +22,7 @@ using namespace LibThreadSafe::Impl; std::string CStringReturner::GetString() { - throw std::string("Get random string"); + return std::string("Get random string"); } void CStringReturner::ThreadSafetyCheck() diff --git a/Examples/ThreadSafety/Readme.md b/Examples/ThreadSafety/Readme.md index d7957187..2842561d 100644 --- a/Examples/ThreadSafety/Readme.md +++ b/Examples/ThreadSafety/Readme.md @@ -22,7 +22,7 @@ To address the thread safety issue described above, a new parameter called `thre This parameter ensures thread safety by adding mutex locking mechanisms on the library implementation side. ### 2.2. Details -The `ThreadSafetyOption` parameter can be set to `none`, `soft` or `strict` value. Here is the difference between them: +The `ThreadSafetyOption` parameter can be set to `none`, `soft` or `strict` value: - `none`: Does nothing. - `soft`: The binding side will call the lock mechanism only when a string is returned from an API function. - `strict`: The binding side will call the lock mechanism every time, regardless of what is returned from the function. @@ -54,7 +54,7 @@ In the `threadSafeLibrary.xml` file, there are three classes defined with the sa - + From 4a569ca2d22b65989e369e37ca1addc5f34c1cd6 Mon Sep 17 00:00:00 2001 From: uck Date: Wed, 11 Jun 2025 09:48:45 +0200 Subject: [PATCH 07/11] Fix formatting --- Examples/ThreadSafety/Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Examples/ThreadSafety/Readme.md b/Examples/ThreadSafety/Readme.md index 2842561d..0976b86d 100644 --- a/Examples/ThreadSafety/Readme.md +++ b/Examples/ThreadSafety/Readme.md @@ -83,7 +83,7 @@ void testStringReturn(ArrayReturner& returner) for (int j = 0; j < 10000; ++j) { returner->GetString(); } - }); + }); } } @@ -96,7 +96,7 @@ void testThreadSafetyCheck(ArrayReturner& returner) for (int j = 0; j < 10000; ++j) { returner->ThreadSafetyCheck(); } - }); + }); } } From ed2c785089a9bc5caa63b21b68fe90a91ef28bc8 Mon Sep 17 00:00:00 2001 From: uck Date: Thu, 12 Jun 2025 23:00:32 +0200 Subject: [PATCH 08/11] Add class hierarchy restriction for threadsafetyoption parameter --- Examples/ThreadSafety/Readme.md | 8 +++- Source/buildbindingccpp.go | 2 +- Source/buildimplementationcpp.go | 14 +++--- Source/componentdefinition.go | 82 +++++++++++++++++++++++++++++++- 4 files changed, 94 insertions(+), 12 deletions(-) diff --git a/Examples/ThreadSafety/Readme.md b/Examples/ThreadSafety/Readme.md index 0976b86d..830dc5fb 100644 --- a/Examples/ThreadSafety/Readme.md +++ b/Examples/ThreadSafety/Readme.md @@ -23,7 +23,7 @@ This parameter ensures thread safety by adding mutex locking mechanisms on the l ### 2.2. Details The `ThreadSafetyOption` parameter can be set to `none`, `soft` or `strict` value: -- `none`: Does nothing. +- `none`: Does nothing. When used in child class ACT will copy `ThreadSafetyOption` from parent. - `soft`: The binding side will call the lock mechanism only when a string is returned from an API function. - `strict`: The binding side will call the lock mechanism every time, regardless of what is returned from the function. @@ -33,6 +33,12 @@ This solution ensures thread safety both when a single pointer on the binding si and when different pointers that point to the same object on the implementation side are used in different threads. Additionally, error handling mechanisms are in place to unlock the mutex in case of exception propagation, preventing deadlocks. +### 2.3 Rules +* The base class can have the `threadsafetyoption` parameter set to `soft` or `strict`. +* If a child class has the `threadsafetyoption` parameter set to `soft` or `strict`, +its parent class must also have the `threadsafetyoption` parameter set to `soft` or `strict`, or the parent must be the base class. +* The `threadsafetyoption` parameter in a child class cannot be less strict than the parent's `threadsafetyoption` parameter. + # 3. Example ### 3.1. Component definition In the `threadSafeLibrary.xml` file, there are three classes defined with the same methods but different `threadsafetyoption` values: diff --git a/Source/buildbindingccpp.go b/Source/buildbindingccpp.go index ecd5da83..be0f4ff6 100644 --- a/Source/buildbindingccpp.go +++ b/Source/buildbindingccpp.go @@ -1716,7 +1716,7 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s w.Writeln(" */") for j := 0; j < len(class.Methods); j++ { method := class.Methods[j] - err := writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, class.ClassName, make([]string, 0), false, true, false, useCPPTypes, ExplicitLinking, false, component.isMultiThreadedEnv(), class.eThreadSafetyOption()) + err := writeDynamicCPPMethod(method, w, NameSpace, ClassIdentifier, class.ClassName, make([]string, 0), false, true, false, useCPPTypes, ExplicitLinking, false, component.isMultiThreadedEnv(), component.getRealThreadSafetyOption(&class)) if err != nil { return err } diff --git a/Source/buildimplementationcpp.go b/Source/buildimplementationcpp.go index b64eaf14..e4de8349 100644 --- a/Source/buildimplementationcpp.go +++ b/Source/buildimplementationcpp.go @@ -354,16 +354,14 @@ func writeCPPClassInterface(component ComponentDefinition, class ComponentDefini w.Writeln("**************************************************************************************************************************/") w.Writeln("") parentClassString := " " - if (!component.isBaseClass(class)) { + if !component.isBaseClass(class) { parentClassString = " : public virtual " - if (class.ParentClass == "") { - if (class.isThreadSafe()) { - parentClassString += fmt.Sprintf("I%s ", getWithMutexClassName(ClassIdentifier, component.Global.BaseClassName)) - } else { - parentClassString += fmt.Sprintf("I%s%s ", ClassIdentifier, component.Global.BaseClassName) - } + parentName := class.ParentClass + + if parentName == component.Global.BaseClassName && component.getRealThreadSafetyOption(&class) != eThreadSafetyNone { + parentClassString += fmt.Sprintf("I%s ", getWithMutexClassName(ClassIdentifier, parentName)) } else { - parentClassString += fmt.Sprintf("I%s%s ", ClassIdentifier, class.ParentClass) + parentClassString += fmt.Sprintf("I%s%s ", ClassIdentifier, parentName) } } diff --git a/Source/componentdefinition.go b/Source/componentdefinition.go index 54561f10..b679290d 100644 --- a/Source/componentdefinition.go +++ b/Source/componentdefinition.go @@ -69,6 +69,19 @@ const ( eThreadSafetyStrict = 2 ) +func (option ThreadSafetyOption) String() string { + switch option { + case eThreadSafetyNone: + return "None" + case eThreadSafetySoft: + return "Soft" + case eThreadSafetyStrict: + return "Strict" + default: + return "Unknown" + } +} + // ComponentDefinitionParam definition of a method parameter used in the component's API type ComponentDefinitionParam struct { ComponentDiffableElement @@ -369,6 +382,21 @@ func (component *ComponentDefinition) addExtraBaseClassMethods() *string { return nil } +func (component *ComponentDefinition) getParent(class *ComponentDefinitionClass) *ComponentDefinitionClass { + parentName := class.ParentClass + if parentName == "" { + return nil + } + + for i := range component.Classes { + if component.Classes[i].ClassName == parentName { + return &component.Classes[i] + } + } + + return nil +} + func (method *ComponentDefinitionMethod) isExtraBaseClassmethod() bool { return method.MethodName == getLockInstanceMethodName() || method.MethodName == getUnlockInstanceMethodName() } @@ -549,9 +577,19 @@ func (component *ComponentDefinition) checkClasses() (error) { classTypeIdIndex := make(map[uint64]int, 0) for i := 0; i < len(classes); i++ { class := classes[i]; + if !component.isBaseClass(class) { + if class.ParentClass == "" { + class.ParentClass = component.Global.BaseClassName + classes[i] = class + } + } + classTypeHash, _ := class.classTypeId(component.NameSpace); - if component.isBaseClass(class) && class.isThreadSafe() { - return fmt.Errorf("thread safety option for base class can't be enabled") + if class.isThreadSafe() { + err := checkThreadSafetyHierarchy(component, &class) + if err != nil { + return err + } } if !nameIsValidIdentifier(class.ClassName) { return fmt.Errorf ("invalid class name \"%s\"", class.ClassName); @@ -574,6 +612,7 @@ func (component *ComponentDefinition) checkClasses() (error) { (*classNameList)[class.ClassName] = true classNameIndex[class.ClassName] = i classTypeIdIndex[classTypeHash] = i + } // Check parent class definitions @@ -625,6 +664,26 @@ func (component *ComponentDefinition) checkFunctionTypes() (error) { return nil } +func checkThreadSafetyHierarchy(component *ComponentDefinition, class *ComponentDefinitionClass) error { + classOption := class.eThreadSafetyOption() + parent := component.getParent(class) + for parent != nil { + parentOption := parent.eThreadSafetyOption() + + if classOption != eThreadSafetyNone && parentOption == eThreadSafetyNone && !component.isBaseClass(*parent) { + return fmt.Errorf("class \"%s\" has threadSafetyOption = \"%s\", but its parent \"%s\" is not base \"%s\" class and has threadSafetyOption = \"%s\"", + class.ClassName, classOption, parent.ClassName, component.Global.BaseClassName, parentOption) + } + + if classOption < parentOption && (!component.isBaseClass(*parent) || parentOption != eThreadSafetyNone) { + return fmt.Errorf("class \"%s\" has threadSafetyOption = \"%s\", but its parent \"%s\" has threadSafetyOption = \"%s\"", class.ClassName, classOption, parent.ClassName, parentOption) + } + parent = component.getParent(parent) + } + + return nil +} + func checkDuplicateNames(nameMaps NameMaps) (error) { enumList := nameMaps.enumMap structList := nameMaps.structMap @@ -1431,6 +1490,25 @@ func (method *ComponentDefinitionMethod) getArrayOutParameters() ([]string) { return outParameters; } +func (component *ComponentDefinition) getRealThreadSafetyOption(class *ComponentDefinitionClass) ThreadSafetyOption { + option := class.eThreadSafetyOption() + if option != eThreadSafetyNone { + return option + } + + parent := component.getParent(class) + for parent != nil { + parentOption := parent.eThreadSafetyOption() + if parentOption != eThreadSafetyNone { + return parentOption + } + + parent = component.getParent(parent) + } + + return eThreadSafetyNone +} + func (class *ComponentDefinitionClass) eThreadSafetyOption() ThreadSafetyOption { switch class.ThreadSafetyOption { case "strict": From 57bc11bc9756f455283be52f0ac30dd063853688 Mon Sep 17 00:00:00 2001 From: uck Date: Thu, 12 Jun 2025 23:08:10 +0200 Subject: [PATCH 09/11] Fix typo --- Examples/ThreadSafety/Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Examples/ThreadSafety/Readme.md b/Examples/ThreadSafety/Readme.md index 830dc5fb..a2fbb85d 100644 --- a/Examples/ThreadSafety/Readme.md +++ b/Examples/ThreadSafety/Readme.md @@ -22,12 +22,12 @@ To address the thread safety issue described above, a new parameter called `thre This parameter ensures thread safety by adding mutex locking mechanisms on the library implementation side. ### 2.2. Details -The `ThreadSafetyOption` parameter can be set to `none`, `soft` or `strict` value: +The `threadsafetyoption` parameter can be set to `none`, `soft` or `strict` value: - `none`: Does nothing. When used in child class ACT will copy `ThreadSafetyOption` from parent. - `soft`: The binding side will call the lock mechanism only when a string is returned from an API function. - `strict`: The binding side will call the lock mechanism every time, regardless of what is returned from the function. -When the `ThreadSafetyOption` attribute in the component class is set to `soft` or `strict`, +When the `threadsafetyoption` attribute in the component class is set to `soft` or `strict`, the implementation will derive from a base class that includes mutex locking mechanisms. This solution ensures thread safety both when a single pointer on the binding side is shared across threads, and when different pointers that point to the same object on the implementation side are used in different threads. From cd04a97085e4762e3bbf80540550d1c37885da0a Mon Sep 17 00:00:00 2001 From: uck Date: Mon, 16 Jun 2025 10:47:37 +0200 Subject: [PATCH 10/11] Fix gitignore --- Examples/ThreadSafety/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/ThreadSafety/.gitignore b/Examples/ThreadSafety/.gitignore index 2241d4af..e74f470b 100644 --- a/Examples/ThreadSafety/.gitignore +++ b/Examples/ThreadSafety/.gitignore @@ -1 +1 @@ -LibPrimes_component +LibThreadSafe_component From 6b73b8baf912ecdf11db19887607fafde4556fff Mon Sep 17 00:00:00 2001 From: uck Date: Mon, 16 Jun 2025 10:53:02 +0200 Subject: [PATCH 11/11] Add libthreadsafe to example.cpp --- .../Examples/CppDynamic/LibThreadSafe_example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp b/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp index 14a882b9..00d7b90e 100644 --- a/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp +++ b/Examples/ThreadSafety/LibThreadSafe_component/Examples/CppDynamic/LibThreadSafe_example.cpp @@ -47,7 +47,7 @@ void testThreadSafetyCheck(ArrayReturner& returner) int main() { std::string libpath = (""); // TODO: put the location of the LibThreadSafe-library file here. - auto wrapper = LibThreadSafe::CWrapper::loadLibrary(libpath + ""); // TODO: add correct suffix of the library + auto wrapper = LibThreadSafe::CWrapper::loadLibrary(libpath + "libthreadsafe."); // TODO: add correct suffix of the library LibThreadSafe::PStringReturner stringReturner = wrapper->CreateStringReturner(); LibThreadSafe::PSoftStringReturner softStringReturner = wrapper->CreateSoftStringReturner();