Skip to content

Commit 2ac44b8

Browse files
authored
Merge pull request #95 from woodwan/woodwan/TolerateMissingMethods
## Description Suppose two components collaborate by passing abstract class instances between one another. The definition of the abstract class interface against which the two components were built must be exactly the same. For example, suppose we add a new method to an interface and regenerate one of the components, but not the other. If we pass an implementation of this interface from the component _without_ knowledge of the new method to the component _with_ knowledge of the new method, the generated ACT code in the newly-generated component will throw an error at the point where the object is passed over because an expected method is missing. It's desirable to have a little more leeway here, since during development of collaborating components it is often the case that they will go temporarily 'out of sync ' - e.g. testing a development build against a prebuilt version of a collaborator. I've address this in the generated C++ binding here by throwing an error at the point of calling a missing abstract method, not at the point where loading it fails. ## Verification Created two 'concrete' components, a shared 'base' component definition containing an abstract class, and a executable consuming them. One component produces an instance of the interface, the other consumes it. The executable 'wires' them together. Generated, built and verified that this worked. Modified the interface to add a new method. Regenerated the 'consumer' component (so it should expect the new method, which won't be present.) When the consumer is regenerated _without_ these changes, it throws up an error. When the consumer is generated _with_ these changes, it works, since it does not attempt to call the new method.
2 parents 37ededc + e6ac5e2 commit 2ac44b8

File tree

1 file changed

+19
-4
lines changed

1 file changed

+19
-4
lines changed

Source/buildbindingccpp.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ func buildDynamicCPPMethodDeclaration(method ComponentDefinitionMethod, NameSpac
609609
}
610610

611611
func writeDynamicCPPMethod(component ComponentDefinition, method ComponentDefinitionMethod, w LanguageWriter, ClassIdentifier string, ClassName string,
612-
implementationLines []string, isGlobal bool, includeComments bool, checkErrorSafely bool, useCPPTypes bool, ExplicitLinking bool) error {
612+
implementationLines []string, isGlobal bool, includeComments bool, checkErrorSafely bool, useCPPTypes bool, ExplicitLinking bool, isAbstract bool) error {
613613
NameSpace := component.NameSpace
614614

615615
CMethodName := ""
@@ -868,6 +868,10 @@ func writeDynamicCPPMethod(component ComponentDefinition, method ComponentDefini
868868

869869
w.Writeln(" {")
870870
w.Writelns(" ", definitionCodeLines)
871+
if isAbstract {
872+
w.Writeln(" if (!%s)", CMethodName)
873+
w.Writeln(" throw E%sException(%s_ERROR_NOTIMPLEMENTED, \"Method '%s' not found\");", NameSpace, strings.ToUpper(NameSpace), method.MethodName)
874+
}
871875
if requiresInitCall {
872876
w.Writeln(" %s%s(%s)%s;", checkErrorCodeBegin, CMethodName, initCallParameters, checkErrorCodeEnd)
873877
}
@@ -903,8 +907,12 @@ func writeLoadingOfClassFunctionTable(component ComponentDefinition, stubfile La
903907

904908
for k := 0; k < len(class.Methods); k++ {
905909
method := class.Methods[k]
910+
readMethod := "readMethodInto"
911+
if (class.IsAbstract()) {
912+
readMethod = "readAbstractMethodInto"
913+
}
906914
CMethodName := strings.ToLower(fmt.Sprintf("%s_%s_%s", NameSpace, class.ClassName, method.MethodName));
907-
stubfile.Writeln(" %s::readMethodInto(pLookupFunction, \"%s\", (void**)&(%s->m_%s_%s));", WrapperName, CMethodName, sTableName, class.ClassName, method.MethodName);
915+
stubfile.Writeln(" %s::%s(pLookupFunction, \"%s\", (void**)&(%s->m_%s_%s));", WrapperName, readMethod, CMethodName, sTableName, class.ClassName, method.MethodName);
908916
}
909917
}
910918

@@ -1413,6 +1421,13 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s
14131421
w.Writeln(" }")
14141422
w.Writeln("")
14151423

1424+
w.Writeln(" static void readAbstractMethodInto(%sSymbolLookupType pLookupMethod, std::string sFunctionName, void** pfnTarget) {", NameSpace )
1425+
w.Writeln(" %sResult eLookupError = (*pLookupMethod)(sFunctionName.c_str(), pfnTarget);", NameSpace);
1426+
w.Writeln(" if (eLookupError != %s_SUCCESS)", strings.ToUpper(NameSpace))
1427+
w.Writeln(" *pfnTarget = 0;")
1428+
w.Writeln(" }")
1429+
w.Writeln("")
1430+
14161431

14171432
for i := 0; i < len(component.Classes); i++ {
14181433
class := component.Classes[i]
@@ -1510,7 +1525,7 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s
15101525
checkErrorSafely = true
15111526
}
15121527

1513-
err = writeDynamicCPPMethod(component, method, w, ClassIdentifier, "Wrapper", implementationLines, true, true, checkErrorSafely, useCPPTypes, ExplicitLinking)
1528+
err = writeDynamicCPPMethod(component, method, w, ClassIdentifier, "Wrapper", implementationLines, true, true, checkErrorSafely, useCPPTypes, ExplicitLinking, false)
15141529
if err != nil {
15151530
return err
15161531
}
@@ -1599,7 +1614,7 @@ func buildCppHeader(component ComponentDefinition, w LanguageWriter, NameSpace s
15991614
checkErrorSafely = true
16001615
}
16011616

1602-
err := writeDynamicCPPMethod(component, method, w, ClassIdentifier, class.ClassName, make([]string,0), false, true, checkErrorSafely, useCPPTypes, ExplicitLinking)
1617+
err := writeDynamicCPPMethod(component, method, w, ClassIdentifier, class.ClassName, make([]string,0), false, true, checkErrorSafely, useCPPTypes, ExplicitLinking, class.IsAbstract())
16031618
if err != nil {
16041619
return err
16051620
}

0 commit comments

Comments
 (0)