Actually 'UE_LOG(...for duty on %s!"), *ObjectName)' is NOT dereferencing a pointer ObjectName

Hello all

Just for your sanity let me clarify something:

In the code to display the object’s name in the console, we use the UE_LOG macro which demands a pointer as the last parameter.

void UPositionReport::BeginPlay() {
	Super::BeginPlay();
	FString ObjectName = GetOwner()->GetName();
	UE_LOG(LogTemp, Warning, TEXT("Position report reporting for duty on %s!"), *ObjectName)
}

Ben tells us to use the *ObjectName construction to get the module to compile.

He mentions we are dereferencing the FString ObjectName.

But that is not really true.

Actually FString is an actual class, and you cannot dereference an instance as ObjectName:

FString ObjectName ...;  // ObjectName is an instance of FString, you CANNOT dereference with *ObjectName
FString *ObjectName ...; // ObjectName is a pointer to FString, you CAN dereference with *ObjectName

So. Why the code compiles? Why it Works?

Actually what we are doing is not pointer dereferncing nor pointer-related stuff.

The trick here is that there is a custom operator defined with the name *. This * operator is defined in the file UnrealString.h as follows:

/**
 * Get pointer to the string
 *
 * @Return Pointer to Array of TCHAR if Num, otherwise the empty string
 */
FORCEINLINE const TCHAR* operator*() const
{
	return Data.Num() ? Data.GetData() : TEXT("");
}

So by using the *ObjectName construction we are really calling this code above, and not doing any C++ pointer dereferencig.

It is a bit difficult for us beginners to find out what is C++ stuff and what is custom code (as the operator*() above), but once you learn the relatively simple rules of C++, you start to detect this kind of constructions.

Ramón Gil Moreno

9 Likes

Thanks !

Ben lost me when he said he was dereferencing ObjectName that we just initiated as an Fstring.
But i wasn’t completely sure about that -> (looking through pointers) stuff

if you can correct me:

in the line:
Fstring ObjectName = GetOwner( ) -> GetName( );

GetOwner( ) is returning a pointer to an AActor class instance
and GetName( ) is looking for the name in this AActor class instance through the pointer returned by GetOwner
And then putting the result in a Fstring class instance named ObjectName.

I progressively begin to understand that some lines are easyer to read from the right to the left. Am I Right?

Anyway i’m letting all that operator*( ) stuff on the side for the moment.

Hi BrainKite.

In the line:
FString ObjectName = GetOwner( ) -> GetName( );

GetOwner( ) is returning a pointer to an AActor class instance
and GetName( ) is looking for the name in this AActor class instance through the pointer returned by GetOwner
And then putting the result in a FString class instance named ObjectName.

I will say yes because it is the way the code is meant to be used.

At the end I will show you some aspects of C++ that could add more complexity/steps to the interpretation of yours.

I progressively begin to understand that some lines are easier to read from the right to the left. Am I Right?

Sort of.

In the case you are puzzled, you can always debug your code and run each statement with “step into”.

That will take your through the methods/constructors/operators actually being run; there you can see what parameters they receive and what types they return.

C++ customizations that may drive you nuts:

Disclaimer: the following stuff is not intended for beginners like us; but will introduce you to some C++ aspects that we might find while debugging our Unreal code.

The C++ language allows quite a lot of different solutions involving copy constructors and assignment operators.

These facilities allows fine control of how our classes will work, but we pay the price of increased possible complexity.

About constructors: you can create copy constructors that will be called when a new object is being created by copying a different instance. Here some similar pieces of code can run different sequence of methods/constructors.

e.g. this is the typical case of a copy constructor:

A a = b;

but this alternate version runs a default constructor (no arguments) then runs the assignment operator

A a;
a = b;

See here about constructors: http://www.cplusplus.com/articles/y8hv0pDG/

About operators: the -> symbol is an operator you can customize. Same thing for =. See here a reference about all possible C++ operators: http://en.cppreference.com/w/cpp/language/operators

Thank you so much @ramongilmoreno. I was scratching my head so long on this concept. I even googled whether it is possible to deference a value type :stuck_out_tongue_winking_eye: . Finally got my concepts right

Thanks for this clarification, I have marked this video for an update so that this doesn’t confuse anyone else.

1 Like

I’m uploading a patched version of this video now, with new audio starting from 7:10 for about 30s. Thanks again.

4 Likes

Hi Ben.

Sorry for bringing more complexity to your explanations :smiley:

Everything should be made as simple as possible, but not simpler (the quote is not mine)

Ramón Gil Moreno.

No worries, this is an important distinction.

I think it’s fairly safe to say that using the ‘*’ operator (which semantically in C and C++ is the “dereference a pointer” operator) to actually cast to a TCHAR* would have been better served with an actual named function. Someone probably thought it would be fewer characters to just use the ‘*’, but as we can see from this discussion, it’s just confusing. I know that when I tried to “race ahead” and get the code working myself, I was looking through the FString members and not finding what I was looking for, in terms of a “get string” sort of thing. It was only because I had caught a glimpse of the finished code early on in the video that I knew to look at that operator.

Basically: if you’re confused, it’s not you. They chose to do it in a confusing way. :slight_smile:

2 Likes

I have a question:
FString ObjectName = GetOwner( ) -> GetName( );
how can function call the function GetOwner( ) -> GetName( ); like this

class A {
protected:
string name;
int age;
public:

  A() : name("Leo"), age(31) {}
 //A(string n= "leo", int v= 31) { name = n; age = v; }
 string getName() { return name; }
 int getAge() { return age; }
 A *getObject() const { A a; return &a; }

};

int main() {

A *b,c;
b = &c;
string s = getObject()->getName();
cout << s << endl;
return 0;

}

this simple code isn’t work because string s = getObject()->getName(); it’s wrong order

can somebody answer me it’s so complicate for me!!!

I guess the point is to ensure that we aren’t allocating memory at runtime?

I don’t understand the relationship between FString and TCHAR*. I’ve looked them both up and can’t find a relationship. FString doesn’t have a GetData() method.

When I Google, I find an answer that suggests that you can just dereference an FString to get a TCHAR*.

But it makes me uncomfortable that I can’t derive that answer from the documentation myself.

Privacy & Terms