ROBOTC Return Struct

If create a struct, is there any way that I can return a new struct from a function? Or do i just have to set a global.

My life would be made so much easier if I could just write:

StructObject myFunction(){
    StructObject  obj;
    obj->x = foo;
    obj->y = bar;
    obj->r = foobar;
    return obj;
}

Good news and bad news.

Bad news first, apparently you can’t return structs in ROBOTC (according to some trial and error and Googling). I’ll spare you and the rest of the community my anger upon learning this for now.

Good news, there is a simple work around, just pass the struct by reference (which is probably a good habit to form anyway)…

typedef struct {
	int x;
	int y;
	int z;
} StructObject;

void myFunction(StructObject &obj){
    obj.x = 1;
    obj.y = 2;
    obj.z = 3;
    return;
}

task main() {
	
	StructObject myObj;
	myFunction(myObj);

}

If this code doesn’t make sense, check this out.

Hope that helps,
-Cody

Thanks for that, I’ll look into it.

I think I have solved the problem, but created more. Will release code if I get stuck

So I did a bit of digging and figured out that this compiles not sure if it runs yet.

typedef struct {
    int foo;
} foo;

foo* stuff(){
    foo *obj;

    return obj;
}

I fail to see how returning a pointer helps you here any more than passing the reference, especially considering ROBOTC doesn’t have dynamic memory.

I’m just screwing around a bit really, making libraries to make my life easier.

Really whatever works, works I don’t care how it happens

It all depends on what you are trying to achieve here, but here are two legitimate ways of populating a struct in ROBOTC.

typedef struct _ts {
  int a;
  int b;
  int c;
} ts;

ts *foo() {
  static ts localTs;
  
  localTs.a = 1;
  localTs.b = 2;
  localTs.c = 3;
  
  return( &localTs );
}

void bar(ts *ptrTs) {
  ptrTs->a = 4;
  ptrTs->b = 5;
  ptrTs->c = 6;
}

task main()
{
    ts *x;
    ts  y;
    
    x = foo();    
    writeDebugStreamLine("%d", x->a);
    writeDebugStreamLine("%d", x->b);
    writeDebugStreamLine("%d", x->c);
    
    bar(&y);
    writeDebugStreamLine("%d", y.a);
    writeDebugStreamLine("%d", y.b);
    writeDebugStreamLine("%d", y.c);
}

you probably wanted this


ts foobar() {
  ts localTs;
  
  localTs.a = 7;
  localTs.b = 8;
  localTs.c = 9;
  
  return( localTs );
}

with a call like this.

ts y = foobar();    

but that often needs more memory and the structure to be copied so I would generally avoid it.

This will not work the way you would like it to. I assume you would like to do something like the below code example:

typedef struct {
	int x;
} MyStruct;

MyStruct *newMyStruct(int x){
	MyStruct *this;

	this->x = x;

	return this;
}

task main() {
	MyStruct *myStruct = newMyStruct(1);
}

The code will compile fine, but running it fails when trying to set the value of this->x, because ROBOTC does not support dynamic memory allocation.

If you instead try running a program similar to yours, like the one below:

typedef struct {
	int x;
} MyStruct;

MyStruct *newMyStruct(){
	MyStruct *this;

	return this;
}

task main() {
	MyStruct *myStruct = newMyStruct();

	myStruct->x = 1;
}

It will also fail when trying to set the value of myStruct->x, again because ROBOTC does not support dynamic memory allocation.

You can attempt to create a workaround, using references rather than pointers like so:

typedef struct {
	int x;
} MyStruct;

MyStruct *newMyStruct(int x){
	MyStruct this;

	this.x = x;

	return &this;
}

task main() {
	MyStruct *myStruct1 = newMyStruct(1);
	MyStruct *myStruct2 = newMyStruct(2);

	writeDebugStream("myStruct1->x = %d\n", myStruct1->x);
	writeDebugStream("myStruct2->x = %d\n", myStruct2->x);
}

The program compiles and runs just fine, however it does not perform as intended. myStruct1->x and myStruct2->x both have a value of 2, because the reference returned by newMyStruct is the same both times the function is called.

Thus, the solution presented by @Cody is your best bet. I’d like to make a note about his method, which I have rewritten below to stay consistent with my other examples:

typedef struct {
	int x;
} MyStruct;

MyStruct *newMyStruct(MyStruct &this, int x){
	this.x = x;

	return this;
}

task main() {
	MyStruct myStruct;

	newMyStruct(myStruct, 1);
}

I wanted to point out that the “&” before the MyStruct parameter in the newMyStruct function works in C++, but would not be valid in C, as C does not support pass-by-reference. However, it works in ROBOTC, as this is one of the C++ features supported by ROBOTC.

In C, you could accomplish the same thing with the following code (which I have written to work in ROBOTC):

typedef struct {
	int x;
} MyStruct;

MyStruct *newMyStruct(MyStruct *this, int x){
	this->x = x;

	return this;
}

task main() {
	MyStruct myStruct;

	newMyStruct(&myStruct, 1);
}

Some argue that using the C++ pass-by-reference method is better because it is cleaner, while others argue that it is more ambiguous, because it is impossible to know if the function newMyStruct can modify myStruct without seeing the function prototype. Take that as you’d like, I prefer the second, C method. ROBOTC only has a tiny number of C++ features anyway.

Thank you all for sharing your knowledge, I will endeavor to learn more about this.

In other news, I have been writing this task for tracking position. But It won’t run.
Calling startTask(OdoTask) will make it not even start the usercontrol task (from what I can tell in debugging), while commenting that line will let it run correctly.
I feel like I have done something wrong but I can’t seem to find it.

Here is the code:

#pragma systemFile
#ifndef INCLUDED
#define INCLUDED

#define COUNTS_PER_REVOLUTION 392.0
#define WHEEL_DIAMETER 4.0
#define TRACK_WIDTH 15.0

typedef struct
{
    // Previous Encoder Counts
    int previousLeftCounts;
    int previousRightCounts;

    // Distance travelled per count (inches)
    float distancePerCount;
    // Radians per encoder count
    float radiansPerCount;

    // Current X Coordinate
    float xPos;
    // Current Y Coordinate
    float yPos;
    // Current Heading
    float heading;
    // Update period
    int period;

} OdometricLocalizer;

OdometricLocalizer *MainOdo;


float getX()
{
    return MainOdo->xPos;
}

float getY()
{
    return MainOdo->yPos;
}

float getR_rad()
{
    return MainOdo->heading;
}

float getR_deg()
{
    return radToDeg(MainOdo->heading);
}

byte ResetPos(float newX = 0, float newY = 0, float newH = 0)
{
    MainOdo->xPos = newX;
    MainOdo->yPos = newY;
    MainOdo->heading = newH;
    return 0;
}

task OdoTask()
{
    MainOdo->distancePerCount = (PI * WHEEL_DIAMETER) / COUNTS_PER_REVOLUTION;
    MainOdo->radiansPerCount = (PI * (WHEEL_DIAMETER / TRACK_WIDTH)) / COUNTS_PER_REVOLUTION;

    ResetPos();

    MainOdo->previousLeftCounts = nMotorEncoder[FL];
    MainOdo->previousRightCounts = nMotorEncoder[FR];

    MainOdo->period = 20;

    while (true)
    {
        int leftCounts = nMotorEncoder[FL];
        int rightCounts = nMotorEncoder[FR];
        int dLeftCounts = leftCounts - MainOdo->previousLeftCounts;
        int dRightCounts = rightCounts - MainOdo->previousRightCounts;

        float dH = (float)(dRightCounts - dLeftCounts) * MainOdo->radiansPerCount;
        float dDistance = 0.5 * (float)(dLeftCounts + dRightCounts) * MainOdo->distancePerCount;
        float dX = dDistance * (float)cos(MainOdo->heading);
        float dY = dDistance * (float)sin(MainOdo->heading);


        MainOdo->xPos += dX;
        MainOdo->yPos += dY;
        MainOdo->heading += dH;

        if (MainOdo->heading > PI)
        {
            MainOdo->heading -= (2*PI);
        }
        else if (MainOdo->heading <= (-1*PI))
        {
            MainOdo->heading += (2*PI);
        }

        MainOdo->previousLeftCounts = leftCounts;
        MainOdo->previousRightCounts = rightCounts;

        wait1Msec(MainOdo->period);
    }
}

#endif INCLUDED

Thanks in advance

First, if you don’t mind a bit of constructive criticism, I would suggest including whatever file contains the radToDeg function in this file here, since it is a dependency. Now onto why your program will not run.

So you have created a struct, and declared a global pointer to a struct of that type. Then later you try modifying the contents of that struct using “MainOdo->previousLeftCounts”, for example, and the program fails when trying to do so. This is (once again) because ROBOTC does not support dynamic memory allocation. Try changing MainOdo to be a struct, rather than a pointer to a struct, and change all of your “MainOdo->” lines to be “MainOdo.” instead. That should allow your program to run properly.

That really has nothing to do with it. Dynamic memory allocation in C is provided by part of the standard library (ie. malloc), even if we supported those library calls you would still need to setup the pointer correctly which is the real problem here.

Take a look at my smart motor library for more example code using structures.

Whoops that function is a #define in the main file that I still have to move over

Thank you, No more pointers for me

Will check it out

Isn’t the only necessary setup for the pointer using malloc to allocate enough memory for the struct it points to? For example, this would work if ROBOTC had support for malloc:

typedef struct {
	int x;
} MyStruct;

task main() {
	MyStruct *myStruct = malloc(sizeof(MyStruct));

	if (myStruct) {
		myStruct->x = 0;
	}
}

Would it not?