Hey @randymills,
Thank you for reaching out to us. I would not want to rule out that there is either a regression in our API or that I made a mistake when I documented this, but currently it does not look like that to me.
Existing Code
There is for example this thread where I do exactly what is shown in the snippet in the docs, cast an ObjectRef
to an AssetRepositoryRef
, in that case a sub type. The script still runs just fine for me (2024.5.1, Win 11), and when I inspect the relevant section, the values are also what I would expect them to be, i.e., this:
obj: maxon.ObjectRef = maxon.AssetDataBasesInterface.FindRepository(url)
if obj.IsNullValue():
raise RuntimeError(f"Could not establish repository for: {url}.")
# #FindRepository returns an ObjectRef, not an AssetRepositoryRef, we must cast it to its "full"
# form. Specifically, to a reference to a maxon.WatchFolderAssetRepositoryInterface we just
# implemented. Yay, casting Python, the dream becomes true :P
repo: maxon.WatchFolderAssetRepositoryRef = maxon.Cast(maxon.WatchFolderAssetRepositoryRef, obj)
print(f"{obj = }")
print(f"{repo = }")
will print that:
obj = maxon.ObjectRef object at 0x1bce66b2720 with the data: net.maxon.assets.repositorytype.watchfolder@0x000001BCAC5814C0<net.maxon.datatype.internedid> net.maxon.assetrepository.skipinsearch : <bool> false
<net.maxon.datatype.internedid> net.maxon.assetrepository.exportonsaveproject : <bool> true
repo = maxon.WatchFolderAssetRepositoryRef object at 0x1bda1573420 with the data: net.maxon.assets.repositorytype.watchfolder@0x000001BCAC5814C0<net.maxon.datatype.internedid> net.maxon.assetrepository.skipinsearch : <bool> false
<net.maxon.datatype.internedid> net.maxon.assetrepository.exportonsaveproject : <bool> true
General Problem that Interfaces and References are not Interchangeable
On top of that comes that your code uses generally a bit flawed logic; which does not mean that there could not be a bug or an exception. Interfaces and references realize a managed memory architecture in our C++ API and are not interchangeable. The interface is the actual object held in memory, to which zero to many references can exist. The lifetime of an object is then managed by reference counting. This is very similar to what managed languages like Python, Java, C# do internally, it is just a bit more on the nose in our API. To briefly highlight the concept at the example of Python:
# This causes Python to allocate the array [0, 1, 2, 3, 4] in its internal object table and then
# create a reference (the value of id(data)) which is assigned to data.
data: list[int] = [0, 1, 2, 3, 4]
# When we now assign #data to something else, we are just drawing another reference to the actual
# data of #data, #x is the same object as #data.
x: list[int] = data
print(f"{id(data) == id(x) = }") # Will print True, another way to write this would be "data is x".
# Another popular case to demonstrate this are strings, which are also reference managed in most managed
# languages. This means each string is only uniquely held in memory. So, when we allocate "Maxon" twice,
# there is actually only one object in memory.
a: str = "Maxon"
b: str = "Maxon"
# Although we provided here a string literal in both cases, Python catches us duplicating data and
# only holds the object once in its object table.
print(f"{id(a) == id(b) = }") # Will print True.
# This is a bit deceptive, we are actually not modifying here the string which is referenced by #b,
# because strings are immutable in Python due to the "only stored once logic" shown above. When we
# would modify the actual object, we would also change the value of #a. So, Python does actually copy
# the object referenced by #b and then modifies that copy and assigns a reference to that to #b.
b += "Foo"
print(f"{id(a) == id(b) = }") # Will print False, a and b are not the same object anymore.
id(data) == id(x) = True
id(a) == id(b) = True
id(a) == id(b) = False
Interfaces are similar to the hidden entities living in Python's object table, and references are somewhat equivalent to the frontend data instances a user deals with in Python. We have a manual for Python and C++ about the subject for our API. The C++ manual is slightly better, but both unfortunately do their best to obfuscate the subject with a very technical approach rather than explaining it in laymans terms.
When we really boil all that down, then interfaces are "classes" and references are instances of that classes. That is more an analogy than an actual fact but conveys the major idea that an instances of a class are always meant to be dealt with in the form of references to some interface object.
About your Issue
Because of all that, it strikes me as a bit unlikely that you must cast down a reference to an interface (maxon.AssetDataBasesInterface.FindRepository
returns an ObjectRef
, not an ObjectInterface
, i.e., a reference to some data, not the actual data). Because our managed memory approach does expose that actual raw data (all the SomethingInterface
types), you can also technically use the raw object, as all the logic of that type is implemented there. But doing that is very uncommon and you also lack the logic which is provided by references like for example IsEmpty()
, IsNullValue()
, or GetType()
which is not unimportant. References derive from maxon.Data where the these methods provided by references are implemented, interfaces derive from maxon.ObjectInterface and implement the actual functionality of some type, e.g., FindAssets
for AssetRepositoryInterface
. The methods of an interface then "shine through" on its reference, because the reference is also in inheritance relation to its interface.
When you need more assistance here, or want this generally to be clarified, please share your code. There could be a bug or an inconsistency in our API, or I simply documented something incorrectly. But for me it currently looks more like that something in your code is going wrong.
Cheers,
Ferdinand