After much digging and a good amount of debugging, I have the root cause of the issue.
The reasoning behind the byte offset.
While executing the python delegate, the SelectedItem
is interpreted as an FNameProperty
rather than a FStrProperty
. This indirection is where we loose those extra few bytes causing the python cast to fail. Such a cool issue!
This lead me to believe there is a problem with the python delegate generation rather than the conversion tooling. Pretty nifty that the engine doesn’t crash because of this fluke.
The engine builds a dynamic delegate when we use the python script:
widget.on_selection_changed.add_callable_unique(...)
That runs, among other things, the delegate metadata scan to pull in information about said delegate (PyWrapperDelegate.h:L94
)
TPyWrapperDelegateMetaData::GetMetaData(PyType);
That call should return us a structure containing the FStrProperty
for the SelectedItem
. Remember, the SelectedItem member in the ComboBoxString
is an FString
. but instead we’re getting a FNameProperty
which breaks the chain.
The Reason
The ComboBoxString
is not the only class to define a FOnSelectionChangedEvent
- in fact, the ComboBoxKey
defines a delegate with the same name, but uses an FName parameter instead of the FString
.
Here, at last, is our issue.
The python registry for these callbacks uses a flat TMap<FName, PyTypeObject*>
in PyWrapperTypeRegistry.h
. Because these two delegates collide by name, (OnSelectionChangedEvent__DelegateSignature
) we have a cache miss and poof! The error chain is complete and we cannot successfully assign to a ComboBoxString
change delegate via python.
Possible Fix
With all of this data compiled, we need to have the python type registry capable of handling multiple delegates with the same name. But it would also be nice to avoid a complex mapping of structures.
The only difference between our delegates, besides their signature, is their owner. Each of them is owned by their respective class (ComboBoxKey
and ComboBoxString
). So, with a little extra digging and a small C++ change, I’ve submitted a PR for the engine:
https://github.com/EpicGames/UnrealEngine/pull/9069