Outpost Universe Forums

Projects & Development => Outpost 2 Programming & Development => Topic started by: Vagabond on February 07, 2016, 01:56:59 PM

Title: Adding Operator Functions + and - for LOCATION in SDK
Post by: Vagabond on February 07, 2016, 01:56:59 PM
It would be nice to be able to add or subtract LOCATION structures from each other using a standard operator function like + or -.

For example, you could place structures relative to each other like this:
Code: [Select]
Unit unit;

TethysGame::CreateUnit(unit, map_id::mapCommandCenter, LOCATION(20 + X_, 15 + Y_), 0, map_id::mapNone, UnitDirection::East);
TethysGame::CreateUnit(unit, map_id::mapAgridome, unit.Location() + LOCATION(7, 0), 0, map_id::mapNone, UnitDirection::East);
TethysGame::CreateUnit(unit, map_id::mapStandardLab, unit.Location() - LOCATION(-7, 0), 0, map_id::mapNone, UnitDirection::East);

I don't think the definition of the structure LOCATION can be modified, so the operator functions have to be declared globally instead of being declared as members of LOCATION. (I think the definition of LOCATION is being pulled directly from Outpost 2 code, but not sure).

I added the following declarations and definitions to a local copy of the SDK and tested it. I'm still new to C++, so please take it with a discerning eye.

OP2Helper.h Addition
Code: [Select]
LOCATION operator+ (LOCATION &loc1, LOCATION &loc2);
LOCATION operator- (LOCATION &loc1, LOCATION &loc2);

OP2Helper.cpp Addition
Code: [Select]
/*Global Operator Functions for structure LOCATION.
* This allows LOCATION loc = LOCATION(10, 10) + LOCATION(5, 0)
* Or          LOCATION loc = LOCATION(10, 10) - LOCATION(5, 0)*/
LOCATION operator+ (LOCATION &loc1, LOCATION &loc2)
{
LOCATION newLoc;

newLoc.x = loc1.x + loc2.x;
newLoc.y = loc1.y + loc2.y;

return newLoc;
}

LOCATION operator- (LOCATION &loc1, LOCATION &loc2)
{
LOCATION newLoc;

newLoc.x = loc1.x - loc2.x;
newLoc.y = loc1.y - loc2.y;

return newLoc;
}

Attached files with modifications included.
Title: Re: Adding Operator Functions + and - for LOCATION in SDK
Post by: Hooman on February 09, 2016, 01:08:28 AM
That's a good idea.

I believe you can optimize that slightly. You should be able to change the return type to LOCATION&, and do an in-place construction of the LOCATION object at the return statement. The compiler should optimize that by passing in an address for the return value, and constructing it in-place on the caller's stack frame. That would avoid creating a new LOCATION object in the callee's stack frame, copying the object to the caller's stack frame, and then destructing the object from the callee's stack frame.

The result should look something like this:
Code: [Select]
LOCATION& operator+ (LOCATION &loc1, LOCATION &loc2)
{
return LOCATION(loc1.x + loc2.x, loc1.y + loc2.y);
}

I believe you are right in that OP2Helper is an appropriate place to add such an extension. I like how you made it a global operator overload rather than mucking about with a class exported from Outpost2.exe.
Title: Re: Adding Operator Functions + and - for LOCATION in SDK
Post by: Vagabond on February 09, 2016, 04:01:37 PM
Hooman,

Thanks for the suggestions. I implemented constructing the new LOCATION structure in the return statement of operator overload's definition without issue. When I added the address-of operator (&) to the return value, the following compiler warning was issued (using standard compiler with Visual Studio 2015):

Warning C4172 returning address of local variable or temporary
https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k%28C4172%29&rd=true

I think the compiler is worried about losing scope of the LOCATION structure after the operator overload code block is executed since it is only passing a reference to the new LOCATION structure out of the function.
Title: Re: Adding Operator Functions + and - for LOCATION in SDK
Post by: Hooman on February 11, 2016, 07:02:03 AM
Come to think of it, you're probably right to not add the & there. The address passing may be a hidden optimization. It's certainly very true that you don't actually want to return the address of a local variable. It will have gone out of scope by the time control flow reaches the caller, and using the address would produce undefined behaviour.

I just looked it up, and the optimization I was thinking of is called RVO (Return Value Optimization) (https://en.wikipedia.org/wiki/Return_value_optimization). It eliminates copies of objects that would normally be created when returning an object by value. It's notable in that this optimization is allowed to change the observable output of the program, such as when the copy constructor has side effects. A typical example is to print a debug string from the copy constructor, and see if it actually gets printed. The hidden passing around of object points and in place construction are implementations details that are never called for explicitly. You'll note the Wikipedia example doesn't use an & for the return type.