TMap Problems. 🤔

I’m trying to return this in for my blueprint:

UFUNCTION(BlueprintCallable)
   TMap<FString, TMap<FString, TScriptInterface<IGenericSystem>>> GetAllObjs();

but I get an error.

   ....h(72): [] The type 'TMap<FString,TScriptInterface<IGenericSystem> >' can not be used as a value in a TMap

C++ is not my strong point. I understand that TMAP cannot be used like that. I need this functionality, is there a way to solve it?

Sometimes I don’t understand why I can’t return everything I want or how I want it in UE5.
Blueprint support is very limited.
For this Tmap is there any kind of solution?

‎‎‎‎‎‎‎‎‎‎‎‎ㅤ
‎‎‎
Note: I do not choose TArray since with the “contains” of TMap I reduce the complexity from O(n) to O(1). While the TArray would have to search each sublevel which would make it O(n) and depending on the depth it would be 0(n^2) in the worst case.

With TArrays, there is a thing that you cannot have nested TArrays and iirc error message clearly suggest to put the content into struct. I guess this also should be the case with maps.

So, have you tried something like this?:

USTRUCT()
struct MyStruct
{
    TMap<FString, TScriptInterface<IGenericSystem>> MyMap;
};

UFUNCTION(BlueprintCallable)
TMap<FString, MyStruct> GetAllObjs();
1 Like

Yes, I have thought about it. I don’t like. But it seems to be the only way. I’m still looking at alternatives to that.

Afaik it’s the engine limitations, so yes, it should be the only way, unless i’m missing something.

At the worst case, if you want to keep things pretty, you may make some custom wrapper over TMap of TMaps with the api you like

I have tried encapsulating with an object (but of course, then I can’t access inside)

Finally, and against what I wanted, I tried a Struct, as you say, I have this error in

UFUNCTION(BlueprintCallable)
 FObjectWrapperReturning GetAllObjs();

USTRUCT()
struct FObjectWrapperReturning {
   GENERATED_BODY()

public:
  TMap<FString, TMap<FString, TScriptInterface<IGenericSystem>>> MapWraper;

};

Error:

  ....h(72): [ ] Type 'FObjectWrapperReturning' is not supported by blueprint. Function: GetAllObjs Parameter ReturnValue

I also proved like this:

TMap<FString, FObjectWrapperReturning>

But it doesn’t work either, I have the same error.

That might be, because classes need to be marked as blueprint type.

The container / blueprint limitations are a pain in the butt but have always been there and will always be there. it’s a prototyping tool or “fun” ish way for starting developers to learn code. It’s mostly dead to me.

I create 99% of the code in c++ and expose only certain methods to blueprint to use for debugging purposes. If you’re not targeting any blueprint users on the marketplace as customers or anything this is the way to go. You could even create a function library later on to provide an interface for blueprint users to access your c++ stuff, a bit like the UKistmetMathLibrary just uses FMath for most things behind the scenes.

Depends on your needs.

What if you pass around the UObject and cast that to interface instead of passing around TScriptInterface? This is most common. Won’t get rid of the container nesting limitation though. If you don’t need to care about map performance at this point then consider using an array of a struct where the struct contains your ID (FString / GUID) and filter by predicate like I show here:

Having two TMap to access the same instances - #4 by Roy_Wierer.Seda145

1 Like

This is happening because you’re trying to pass a TMap made with a type which is not supported by Reflection System - TScriptInterface - Editor/Blueprints just don’t recognize it as valid type, and you won’t be able to access it from there in any way.

A nested TMap may also be a problem (not sure about that).
A nested TArray wouldn’t make any sense - Array is a continuous block of memory, nesting them could potentially mess a lot of stuff due to how they work.

Yes, it’s impossible to return/pass everything.
Reflection System must be aware of type that you’re trying to return/pass.

There are few types that are supported out of the box (e.g. int, float, double, bool, FString)- https://docs.unrealengine.com/5.0/en-US/unreal-engine-uproperties/

When it comes to structs, to be able to access it from BPs, you must mark it with USTRUCT(BlueprintType) macro, and then every member os said struct must also be marked with UPROPERTY macro with according specifier (e.g. UPROPERTY(BlueprintReadWrite) ) - https://docs.unrealengine.com/5.0/en-US/unreal-engine-uproperties/

More about Reflection System here:
https://docs.unrealengine.com/5.0/en-US/reflection-system-in-unreal-engine/

1 Like

After a few hours of an unexpected build we can continue.

I am not in favor of using Structs (it’s my thing), since not all languages have Structs and if they do, they are either different or have their rules, such as in UE5 the “F” prefix and the “GenerateBody”…
While object-oriented programming follows the same pattern (standard) for all languages. (Object-oriented programming is like a general framework for languages).
I never use Structs, datatables… (unless strictly necessary). I use objects, since they will always work (unless you have a custom C++ like Unreal Engine, Unity, CryEngine…).

I’ll try to explain it. Because I had to do trial and error.

1 - It doesn’t work with Struct.

FObjectWrapperReturning GetAllObjs(); does not work
Tmap<FString, FObjectWrapperReturning> doesn’t work
2 - With OOP Yes it works.
UObjectWrapperReturning GetAllObjs(); doesn’t work
If it works Tmap<FString, UObjectWrapperReturning> GetAllObjs(); but with a couple of details.

How I have tried to do it:
UObjectWrapperReturning GetAllObjs().… Doesn’t work since you will have to expose

UFUNCTION(BlueprintCallable)
TMap<FString, TMap<FString, TScriptInterface<IGenericSystem>>>

and it is not allowed, so I decided to wrap the part inside the TMap, that is, the Map inside.

So I decided to wrap the inside part into an object.
TMap<FString, UObjectWrapperReturning> GetAllObjs();

I do not use TSoftObjectPtr, I use “*”, you can use the one you need the most since otherwise the garbage collector will clean the variable.

After the compiler warned me that it could not be passed as a “non-pointer” inside an object within a TMap, I decided to also apply “*” for the variable:

TMap<FString, UObjectWrapperReturning*> * GetAllObjsVar;

and the method:

Map<FString, UObjectWrapperReturning*> GetAllObjs

Obviously within UObjectWrapperReturning it is only an object with the encapsulation principle. Complying with SOLID.

UFUNCTION(BlueprintCallable)
   TMap<FString, TScriptInterface<IGenericSystem>> GetObjectWrapperReturning()

// This part is the most important, since it can be executed towards Blueprint. (I previously tested it separately and it worked)

Well now we just have to go to the Blueprint.

GetAllObjs → (Map) Values → For each → GetObjectWrapperReturning → (Map) Values → Cast the one that implements the interface → take the object you need.

A roller coaster experience.
The problem is that UE5 takes a long time (at least on my cheap computer) to run, compile, debug… I waste a lot of time with that.

I hope it helps you.

I honestly think you are overcomplicating the solution right now, because the “UObjectWrapperReturning” is actually a worse solution than a struct. Both are classes, but the "U"on UObjectWrapperReturning implies the object you return inherits from the UObject class, which does a ton of different things that you don’t need to pass data along.

https://docs.unrealengine.com/5.1/en-US/API/Runtime/CoreUObject/UObject/UObject/

It’s all OOP.

It’s not custom c++, UE does a lot behind the scenes with macros which is why you need to add UPROPERTY, UFUNCTION, GENERATE_BODY, UCLASS and whatnot. This is NOT required when you write plain c++ in UE, only if you want to expose that directly to the blueprint system or editor. What you can do is write everything in plain c++ and write additional classes or a function library that is exposed to blueprint / editor and simply calls your plain c++ methods.

The garbage collector will not clean the variable as long as it is in scope or referenced in a UPROPERTY. the collector walks a chain of references, anything without a reference is cleaned.

Garbage Collection & Dynamic Memory Allocation - Old UE4 Wiki

Memory Management & Garbage Collection in Unreal Engine 5 | Cas Mikelis' Game Blog

There are different types of pointers for different situations, soft pointer is not the type you use to pass along data which you collect and combine on the spot (function parameter like a struct). soft pointers are used to reference an object or class without loading it into memory, so you can load it into memory manually when you want to.

All about Soft and Weak pointers | Tutorial

Two different things, but for a datatable you are required to supply a struct. The beauty of a datatable is data injection into your classes. They can be exported to csv / json for external processing which makes it even more powerful. None of your data will be hardcoded in your code if you can inject it through a datatable. This gets even more interesting when data is not simply text and numbers but soft object pointers. Now you can reference all the icons you want on your UI (*without loading it into memory) and swap out the entire icon set by just referencing another datatable (row). Another bonus is that when you make changes to that data, you only need to save the datatable and not all the classes the data affects. This is useful when versioning and when other assets may not be edited.

1 Like

I’m going for points.

I have to say that you probably have much more experience than me in C++, but there are things that I don’t agree with you.

"I honestly think you are overcomplicating the solution right now, because the “UObjectWrapperReturning” is actually a worse solution than a struct. Both are classes, but the “U"on UObjectWrapperReturning implies the object you return inherits from the UObject class, which does a ton of different things that you don’t need to pass data along.”

Using a struct or a UObject class may depend on what you need to do.

Sometimes there is more than one way to solve a problem, in my case, using a structure did not solve the problem. Therefore, if something “complicated” and easy for me solves the problem, then I choose to solve the problem. They are objects and are much more powerful than a structure.
Therefore, I opted for a Wrapper object which apparently worked correctly, since as I said Unreal Engine is a C++ “Custom” (now I’m going to go into depth with this).

“An object worse than a structure.”

I don’t agree with that statement at all. Each one has a purpose. An Object may involve more cost but it has more modularity, more power and more necessary functions such as serialization, network functions, inheritance, polymorphism or others.

Personally, I will always use OOP Objects, since not all programming languages have structures.

“It’s all OOP.”

Wrong.

The structures are not literally OOP, that is, they are in some of the OOP programming languages.
If I now move to my Java API to access certain resources, I don’t have to use any Structs. If I have to do Typescript I don’t use Structs. No, I don’t just do Unreal Engine Custom C++. I do more things. In my case, I can’t afford to only work with C++.
Therefore, from my point of view if someone is touching several languages “I strongly and fervently recommend” not using Structs.
But you do what you’re good at, if it works for you, I have no problem.
But think that if you change language everything will be with Objects. There are no Structs in other languages.

In other words, you can use structs in object-oriented programming, but the structs themselves are not object-oriented and are not object-oriented in other programming languages. Structures are simply a way to group related variables in one place.

“It’s not custom C++, UE does a lot behind the scenes with macros which is why you need to add UPROPERTY, UFUNCTION, GENERATE_BODY, UCLASS and whatnot.”

When you modify the standard of a language in this case C++ to adapt it to your needs as Unreal Engine does, and as you say with the macros and the entire reflection system and that there are “limitations”, you are having a very personalized and limited C++ code or closed. Therefore, there are many limitations on the part of UE5 since it has custom C++ code, apparently I cannot use the full potential in UE5.

I have recently come to C++, I come from Java and I see clearly that Unreal Engine is “custom” code, you can call it whatever you want. If you prefer to say no, that’s fine with me. What I did of wrapping an object is not complicated at all. In Java there are things much more complex than this nonsense of wrapping a class.

“The garbage collector will not clean the variable as long as it is in scope or referenced in a UPROPERTY. the collector walks a chain of references, anything without a reference is cleaned.”

I totally agree with the statement, however, you would be surprised to know that the garbage collector (of each IDE/ENGINE) is capable of giving you headaches. For that reason, I said “you can use whichever you need the most if “*” or TSoftObjectPtr”.
I am not the garbage collector and I don’t want one day to say… “oops, the application closed”, therefore, it is better to always say it. Like a band-aid. You can skip it if you are going to remember all the variables and their scope of your application.
Although a variable may be in scope, that alone does not guarantee that the object it points to will not be collected by the garbage collector. I prefer it never happens to me. Personal opinion.

“Structs, datatables…”

They can be very powerful as you mention, but I disagree with you. I prefer to use OOP, without structures or datatables or other types. Why won’t I use them? Because they are exclusive to certain programming languages. Are they powerful? Of course, no one says otherwise.

“data tables can be exported to csv/json” seems interesting.

Thanks for your time. I’m just a guy who only applies OOP instead of using anything language specific. I don’t think it’s bad.

If you want to play nice with UE’s reflection system for blueprint interaction, I’d suggest returning the value as an array of structures that have a key and a value member.

For instance:

USTRUCT(BlueprintType)
struct FMyPair
{
    GENERATED_BODY();

    UPROPERTY(BlueprintReadOnly)
    FString MyKey;

    UPROPERTY(BlueprintReadOnly)
    TScriptInterface<IMyInterface> MyValue;
};

...
    // In your UCLASS
    UFUNCTION(BlueprintCallable)
    TArray<FMyPair> GetAllThings();

UE’s reflection system can handle that easily. The function can transform a TMap into TArray before it returns, and the caller can retransform the TArray result back into a TMap.

Also

In C++ there’s little technical difference between a struct and a class, even though people tend to use them differently.

But in UE there’s a big difference. UCLASS’s are reference/pointer values that are garbage collected when there’s no more “strong” references to them. USTRUCTs are value types that live outside garbage collection. A TArray<UMyUObject*> is very different than TArray<FMyStruct>.

Also

I think you’re confusing “soft” references with “weak” references. A “soft” reference is when you don’t want to load an actual asset as an object instance or as a class via UE’s asset loading until you use it etc - you stream it.

A “weak” reference means a reference that doesn’t count towards Garbage Collection. A “Strong” reference (TObjectPtr as a UPROPERTY) “pins” the object in memory - the garbage collector won’t free it. A “weak” reference does not. It can be garbage collected. There is a method that tells you whether or not the object remains valid or has been garbage collected, so that you don’t try to de-reference unallocated memory.

1 Like

I am hearing and seeing two sides on the networks or some colleague about using or not using things specific to an Engine/Programming Language and I am going to break a spear in favor of not using things specific to a programming language because I see too many things that They are solved with the simple OOP standard. I’m a little “tired” of hearing it. It is understandable for those who will be born and die with a single programming language or engine to always use specific things based on them and defend it tooth and nail over current standards. This also goes for the development team, even if you make a functionality the same as the standard, it will never, ever be the standard.

For those who will not move from a programming language after finishing university and are going to retire with the same language and in the same work chair:

I understand the urge to use something specific to UE5 and C++ as they have a lot of potential.
If your entire working life is going to focus on UE5 and C++ and you are not going to move from this “chair”, then no problem. Otherwise, if you need to be touching more than 7 programming languages (and it does not mean that you are doing something wrong, since each area requires knowledge), then I do not recommend using something specific to UE5, since you will have to memorize very specific things about this engine. If you change your engine tomorrow, you will have to recycle yourself again.

  • If you are only going to be in your working life working until your retirement on the backend of the UE5 application or only in UE5, use whatever you want, such as if you want to mix native conversions and you want to create your own libraries.

  • If you are going to be in the front, back, cloud of the application, as well as the web and others, memorizing specific things about a language will hinder you in the long run. Something that could be done in a moment with the specific things of an engine/language and you need to transplant or move it to another site (technical design issues), then you will not be able to and will have to redo the entire technical document from the base. While in OOP, the impact is minimal and you will never have this problem.

Structures and datatables are specific features of some programming languages. Structures are data types that allow elements of different types to be combined. Datatables, on the other hand, are a specific feature of the Unreal Engine or framework that allows developers to store large sets of custom data.

The specific features of an Engine or language can be very useful in some cases, but they are not universal like OOP. If you move to a new language or engine that does not support these features, you would have to find an alternative way to achieve the same functionality by investing time, whereas with OOP the impact would be minimal and you would not have to rewrite the technical document in any way.

Therefore, while structures and datatables can offer efficient solutions (as you mentioned about csv/json and its instant export) to specific problems in certain languages or engines, OOP offers a more general approach that is applicable to almost any programming language. programming. Therefore, having a solid understanding of OOP may be more beneficial in the long run.

It’s complicated when you apply engine- or language-specific things to make a feature and then have to “transplant it somewhere else.” If I had an elephant’s memory and could save everything specific to each language, it would be great just like when you memorize multiplication tables. But unfortunately, you can’t memorize everything in this world. For that reason, I prefer to use the standard.

Can they be very efficient and powerful? Yes, no one says otherwise.
There are companies that have had to migrate the code from other Engines to Unreal Engine and have only had to adapt the OOP to the engine’s own objects. While specific things have had to be redone from scratch.

But you have the last word.

1 Like

This thread has really gone off the rails, so I’m just going to skip a lot of what seems to be getting argued about that isn’t really relevant to the problem at hand.

You want this to work:

UFUNCTION(BlueprintCallable)
TMap<FString, TMap<FString, TScriptInterface<IGenericSystem>>> GetAllObjs();

You’ll need to do this:

USTRUCT(BlueprintType)
struct FInnerType
{
     GENERATED_BODY()

     UPROPERTY(BlueprintReadWrite)
     TMap< FString, TScriptInterface<IGenericSystem> > InnerMap;
};

UFUNCTION(BlueprintCallable)
TMap< FString, FInnerType > GetAllObjs();

or

UPROPERTY(BlueprintReadWrite)
TMap< FString, FInnerType > ObjectMap;

This will work and be simpler than allocating UObject instances. Each of your previous attempts you were misapplying the suggested change or misunderstanding the error messages.
Because the reflection system doesn’t support nested containers (this has been true since UE3 at least and UnrealScript), this is a standard (for Unreal at least) work around for connecting C++ and Blueprint logic when complicated structures are required.

I have tried your method (I never rule out any possibility), although I have to say that I have had to do a couple of things for what I wanted, since I want it in a certain way (async and not always in memory).

I did it:

USTRUCT(BlueprintType)
struct FStruct_Wrapper_Map_TScriptInterfaceIGenericSystem {
   GENERATED_BODY()

   UPROPERTY(BlueprintReadWrite)
   TMap<FString, TScriptInterface<IGenericSystem>> WrapperMapIGenericSystem;
};

USTRUCT(BlueprintType)
struct FStruct_Wrapper_Map_Map_TScriptInterfaceIGenericSystem {
   GENERATED_BODY()

   UPROPERTY(BlueprintReadWrite)
   TMap<FString, FStruct_Wrapper_Map_TScriptInterfaceIGenericSystem> WrapperMapIGenericSystem;
};

with

   TSoftObjectPtr<FStruct_Wrapper_Map_Map_TScriptInterfaceIGenericSystem> GetAllobj();

I get this error:


   .....h(104): [] Unable to find 'class' with name 'FStruct_Wrapper_Map_Map_TScriptInterfaceIGenericSystem'

With an object right now I would be doing other things instead of investigating problems. That’s what I mean.

My question comes now.
Why can’t I make a TSoftObjectPtr with Structs? Why can’t I treat a Structure as a class? or so I understand since a structure cannot be a pointer of any type.
Why would I have to know this kind of nonsense about Struct if only applying OOP can solve this? Why have I had to waste $time (That’s what I mean) to investigate this. Would I have wasted time in OOP?, NO. end of discussion. That’s why I say, I don’t just focus on engine-specific things. If you do simple things, you can use whatever you want. For things that require a minimum of interfaces, abstracts, parameterization…
I don’t know why I should be looking for info about it… when the time I spent looking for errors, I could have applied a new functionality on the website or somewhere else. $time.

If you find a solution to the Struct error, I can try to do it with Structs.
Thanks also for your advice.

Why am I using “TSoftObjectPtr”? The names I provided in this example are not the ones I actually have. This has to be async and above all not in memory all the time, since the user has to load it little by little instead of being stuck waiting for them all to load. For that reason I use a TSoftObjectPtr. It is necessary? Yeah. If you consider that it is not necessary, you are not considering my functionality. What am I loading?.. well, things.

note: I have put “FStruct” since I have classes “UWrapper_Map_TScriptInterfaceIGenericSystem” and they collide with each other. (Why? It’s thanks to the UE5 Reflection System)

Now someone with time will invest in finding the solution instead of doing something else. Alright. That’s what I mean.

If I had to apply specific things in each language and memorize “n” errors for the specific things in each “n” languages, I think I would go crazy. That’s why I don’t focus on specific things. Few people are going to come across this nonsense. Therefore, what I said about OOP still stands.

when I wrote
“I do not use TSoftObjectPtr, I use “”, you can use the one you need the most since otherwise the garbage collector will clean the variable.*”
It was simply that if someone has the same problem, they should do it their own way. But really I’m using TSoftObjectPtr. It doesn’t mean my way is the best. But it is necessary for my functionality.

Hi @Drakgoku ,

Initially, I got to this thread to help, but I can see that you already got very good advice from other contributors, but you are looking for something else outside the engine’s bounds.

I don’t see why you won’t stick to UE5 programming conventions and restrictions, which are dictated by the need to cover a large set of platforms (and their nuances) from a single codebase.

I suggest learning the UE5 C++ dialect without trying to force your way into the code: that can only fire back at you. Just my 2 cents.

Cheers!
Pino

1 Like

Very refreshing to watch ppl discussing the basics again. Kinda nostalgic.

I appreciate you trying to help by trying to share your perspective focused on UE5 and C++.
I assume you will have a lot of deep experience in this specific technology stack and be able to give specific advice based on that.

My comment about the OOP was based on the vision of those of us who work every day with various platforms, engines and languages and need to constantly be in each area. Even in individual projects we tend to touch multiple areas.

In those scenarios, sticking tightly to very specific conventions can limit long-term flexibility and adaptability. Of course, you have to find the correct way in each case. In this case, I solved the problem without structures since they cannot solve the problem, as I have specified.

I appreciate that we can exchange these points of view, each one enriched by our respective experiences.

1 Like

I’ve been with UE since the start of UE4, I want to add some information to this even though you want to do things differently than I do :slight_smile: , probably you know this already:

In UE structures are passed by reference. Structures are going to be loaded into memory, but they are tiny and can use soft pointers as properties.

I love async loading. Just for info I share a post on how to do this for others:

[UE4 / UE5] Question about async loading

I fully agree with @PinoDFTGames , There is no way to ignore UE its systems / limitations and use 100% of it. It’s not wrong to test and try everything, learn. I personally rewrote many of my plugins multiple times in the first years as I hated (and still hate) ways UE forces me to write my code but not adapting to UE will bite you at some point.

1 Like

There is no way to ignore UE its systems / limitations and use 100% of it

I agree that you cannot completely ignore the conventions and limitations of an engine like UE. Each platform has its particularities.

I personally rewrote many of my plugins multiple times in the first years as I hated (and still hate) ways UE forces me to write my code

I understand that frustration. However, applying OOP principles such as encapsulation and abstraction can mitigate this by decoupling the business logic from the underlying platform.

For example, if all logic is contained in classes with well-defined interfaces, it is easier to change the internal implementation without affecting other modules. It is also easier to extend or migrate part of the code.

It is true that concessions may have to be made and OOP cannot always be applied in its purest expression. But it’s worth putting in the effort to reduce platform-specific dependencies.

This way, less future time is invested in remodeling or modifying to new requirements. Good OOP practices promote maintainability and adaptability.