Download

How do you get a TArray's declared element type?

I’m struggling to get a TArray’s member type. I’ve written a template that accepts different kinds of TArrays but now I’d like to know exactly what’s in that list.

ALTERNATELY, in the end what I’m trying to do is use GetComponents() and ExactCast() to get all the components of a certain type from a scene for processing. Unfortunately, UPointLightComponents are processed twice, once as point lights and once as spotlights, as spotlights inherit from point lights. Is there a way to reduce the double-up and make it more strict?

Can’t you just use “auto”?

http://en.cppreference.com/w/cpp/language/auto

That would’ve been the cleanest solution.

Anyway, type of TArray is available via ElementType.

I.e.



template<typename Arg> void test(const Arg &arg){
     Arg::ElementType tmp{};
}


With auto it SHOULD work like this



template<typename Arg> auto test(const Arg &arg) -> decltype(*arg.GetData()){

     //auto tmp = arg[0];
     //return tmp;  
    //^^^^this makes compiler confused and compilation fails 
    //with "returns address to temporary"
     return *arg.GetData();//<-- this works
}


but msvc compiler apparently deduces unexpected type in commented out case and fails with : “error C4172: returning address of local variable or temporary: tmp”.

So I guess you might be better off with ElementType in this case.

Like this? Check if an item in the list is a specific subclass of a base class or interface, of which the list is comprised.



	TArray<BaseClassOrInterface*> theList;
	for (auto i(theList.CreateIterator()); i; i++)
	{
		if( (*i)>IsA(SubClassYouAreInterestedIn::StaticClass() )
                {
                      // do something
                }
	}


^ I use this for adding different types of classes of custom Components to an Actor, then iterating through them. They all have similar functionality by using an interface, but sometimes I may want to only do work on a specific type in the list.

I suppose it would have helped if I posted some code.


for (FActorIterator it(GWorld); it; ++it) {

    TArray<UPrimitiveComponent *> Primitives;
    ThingSceneManager::MapComponents(it, Primitives);


    TArray<UPointLightComponent *> Lights;
    ThingSceneManager::MapComponents(it, Lights);


    TArray<UDirectionalLightComponent *> DLights;
    ThingSceneManager::MapComponents(it, DLights);


    TArray<USpotLightComponent *> SLights;
    ThingSceneManager::MapComponents(it, SLights);


}


template <typename T>void ThingSceneManager::MapComponents(FActorIterator it, T ComponentList)
{
    it->GetComponents(ComponentList);
    for (auto * Component : ComponentList) {
        Thing::ApiNode * ThingNode = nullptr;
        UE_LOG(LogTemp, Warning, TEXT("[Thing] Evaluating: %s (%s)"), *Component->GetName(), *Component->GetClass()->GetName());


        // 2. Convert geometry and place into Thing scene
        uint32 Converted = 0;
        Converted += ThingImportNew::UEtoThing(ExactCast<UStaticMesh>(Component), &ThingNode);
        Converted += ThingImportNew::UEtoThing(ExactCast<UStaticMeshComponent>(Component), &ThingNode);
        Converted += ThingImportNew::UEtoThing(ExactCast<UPointLightComponent>(Component), &ThingNode);
        Converted += ThingImportNew::UEtoThing(ExactCast<UDirectionalLightComponent>(Component), &ThingNode);
        Converted += ThingImportNew::UEtoThing(ExactCast<USpotLightComponent>(Component), &ThingNode);


        // 4. Map to new ThingConnector structure
        if (Converted > 0) { // ThingNode != nullptr && 
            UE_LOG(LogTemp, Warning, TEXT("[Thing] Converted: %d"), Converted);
            ThingSceneManager::Map.Add(ThingConnector(ThingNode, Component));
        }
    }
}

So the problem isn’t that I don’t have a type, it’s that I need to see what that type IS in order to validate that I’m not getting point lights being handled as spotlights. I did try ElementType but couldn’t work out what to do with it. It’s possible I can filter out the wrong types for that pass early on, I’m just trying to be smart about it.

I suppose I could just pass a string and get the classname from each actual element. I had just hoped that there was a better way to handle it.

I think that’s what I’m looking for! Thank you, I’ll give it a go.

I got it going, but that isn’t eliminating the problem. The pointlight is being cast to a spotlight so it passes that check.

I think I’ll just have to make a list of what I’ve already processed and make a point of doing spot lights last. Without a good way of ensuring GetComponents() doesn’t also grab child classes, that’s all I can do I think.

Might as well toss another question in here - is there a way to evaluate a string as a classname? Or is there a way to check <typename T> against something else to make sure someone isn’t passing in total rubbish to the template?

Also, what’s causing this odd circle? It’s centered on the point light and follows it around. The spotlight is near but doesn’t contribute to that glow (you can just see the edge it along the top).

Spotlights are pointlights. According to UE 4 class structure.

I guess problem is that spot lights are processed twice? First, as pointlights and then as spot lights?

Oddly enough, other way around. My point light is also being processed as a spotlight. I found that odd.

I’m running into every language difficulty I can. Converting between types is complicated.

Why are you using ExactCast? Search for this function online produces very small number of hits, first one being your thread. I’d avoid it and use Cast instead. Judging by the code of the function, if you derive class from spotlight, you won’t be able to cast it back into spotlight. I don’t think this is what you’d want.

Either way.

I think the right idea would be to move this part:



Converted += ThingImportNew::UEtoThing(ExactCast<UStaticMesh>(Component), &ThingNode);
        Converted += ThingImportNew::UEtoThing(ExactCast<UStaticMeshComponent>(Component), &ThingNode);
        Converted += ThingImportNew::UEtoThing(ExactCast<UPointLightComponent>(Component), &ThingNode);
        Converted += ThingImportNew::UEtoThing(ExactCast<UDirectionalLightComponent>(Component), &ThingNode);
        Converted += ThingImportNew::UEtoThing(ExactCast<USpotLightComponent>(Component), &ThingNode);


into subroutine.

First attempt to process spotlight and if it succeeds, return from the subroutine. Also, return after any successful processing attempt.

Also, do you NEED that templated function? Would’ve been easier to debug it without it being templated.


Also, keep in mind that your function might be copying entire TArray by value. I’m not sure if Move semantics will be invoked in this case.

It’s my first crack at writing a template so I was hoping to leave it there, but yeah, templates make everything much harder to debug. I spent half the morning trying to feed a templated type from the word list into a template before I finally clicked that the compiler wasn’t unrolling the types it needed.

I’m using ExactCast because that’s what I started with. Is there a better way to convert between types? I’m still figuring out when and where it’s safe in C++ to pass a child class in when the argument wants a parent class. I’ll try Cast!

There was a specific reason why the cast is outside of the routine, but I think the reason was just brevity.

When you’re passing parameter by reference or pointer, it is pretty much always safe (assuming classes have sane design).

You don’t want to do that kind of thing if you’re trying to pass parameter by value, though. and assign child class to parent class by value. Copy constructor will be invoked and child data won’t be transferred.

Unreal’s “Cast” (and its pure C++ equivalent dynamic_cast) is used when you need to cast from parent to child, and aren’t sure if BaseClass* pointer you’re given actually represents ChildClass*. Those kind of functions return nullptr to you if the cast fails and it isn’t a ChildClass* disguised as BaseClass*.

By the way. If you got PointLight* erroneously start treating it as SpotLight* (i.e. forcefully cast it (using C-cast or something like that) into child pointer without safety checking), then the program can crash when you try to access SpotLight-specific data, because that data doesn’t exist. The crash isn’t guaranteed to happen, however, because doing that kind of thing triggers undefined behavior, and in the worst case scenario you’ll miss that bug (which will resurface later). So, in case of Unreal use Cast when trying to cast from Parent to Child.

So, if I make a TArray of USceneComponents, assign references to the actual scene with it, is it a good idea to also store the original class type of the object that’s being referenced, as well? Or is there a way to tell what the actual child object is without using Cast?

No, not really. It is not a good design. When you need to store class information, it pretty much means that instead of one array you need several. One per type. The idea is that you can store pointer to anything derived from some base, and don’t care what that “anything” actually is - character, static mesh, etc.

Well, objects have GetClass() method, but casting with “Cast()” would be the best idea. When you want some very specific class BUT do not want to include anything derived from it, you are not using OOP to the full extent.

Alright, I’ll have to go for multiple typed lists by the sound of it. So just to be totally clear: TArray <USceneComponent *> can’t store references to UStaticMeshComponent and UPointLightComponent if I want to get the entire child back out of the list. Just making sure I have the concept right.

It can. The point is if you’re going to do a lot of casting, that’s usually not a good design. Not a MAJOR “No”, but something to think about. At least in pure C++.

I.e.



UStaticMeshComponent  *meshComp = ...//points to something
UPointLightComponent *lightComp = ...//points to something else
TArray<USceneComponent*> compArray;
compArray.Add(meshComp);
compArray.Add(lightComp);


However when you want to get the data back:



UStaticMeshComponent  *meshComp = nullptr;
UPointLightComponent *lightComp = nullptr;
TArray<USceneComponent*> compArray = ...; //has data in it

meshComp = compArray[0];//compile error.
meshComp = Cast<UStaticMeshComponent>(compArray[0]);//valid. But if compArray[0] is not a UStaticMeshCOmponent, it'll be nullptr.
lightComp = Cast<UPointLightComponent>(compArray[0]);//also valid. But if compArray[0] is not actually a UPointLightComponent, it'll be nullptr.


Hmm. I still haven’t solved my original problem. So you’re saying it’s generally OK if I’m just using it as a reference to get all the ULightComponent objects and just use that? Because then I’ll avoid the double-ups.

In the end I’m just using this list as a way to get back to an object I’ve mapped. I think I’ve got it from here anyway, thanks!

Okay, final last question. I need to do something like:

USpotLightComponent = Map::Search<USpotLightComponent>(MyLight);

I’m having trouble figuring out what the syntax for writing a template like that is, where you can specify a type in angle brackets. Is that a template, or can I just do that to cast anything that way?