Hello, I mentioned in another thread that I had been working with structs and function pointers in ROBOTC recently to try to make structs, similar to classes, for each subsystem on my robot. I tried compiling my code today and had some strange issues. Here is the relevant portion of code …
typedef struct
{
void *forward( float dist );
// other data too, not really significant to the problem
} MecanumDrive;
void driveForward ( float dist ); /*prototype of function to drive forward defined later*/
void initMecanumDrive ( MecanumDrive *drive )
{
// initilizes all fields, etc ...
drive->forward = &driveForward; // this is where the issues are
}
There are other functions but, I’m just going to focus on this one because I can fix the others once I fix this one. When I try to compile the code, it gives me an error on the line where I try to initialize the function pointer ‘forward’. It says that forward is not a field of the struct reference drive. Does anyone with more experience know what I am messing up with the assignment here? Thanks in advance for your help.
Pointers to functions are not supported, that’s one area where the EasyC compiler is still superior. Long story but functions in ROBOTC are not called in the traditional way by putting an address in the program counter register. I have not found any clever workarounds yet either, the best I could do in our drive library was call different functions based on another variable, for example.
void
DriveSystemDrive( int forward, int turn, int right = 0 )
{
switch( theDrive.type )
{
case Mecanum4Motor:
case Mecanum4MotorFieldCentric:
DriveSystemMecanumDrive( forward, turn, right );
break;
case Arcade2Motor:
case Arcade4Motor:
DriveSystemArcadeDrive( forward, turn );
break;
default:
break;
}
}
theDrive.type is set in an initialization routine depending on the type of drive the robot has, a generic function is called to send the drive parameters and the specific drive code is then called depending on what the drive type is.
It’s unfortunate, I was not happy but it is what it is. This gives you an idea of what is happening. A program like this.
void f1() {int x = 123; }
void f2() {int x = 456; }
task main()
{
f1();
f2();
}
compiles to byte code like this.
void f1() {int x = 123; }
**Info***:'x' is written but has no read references
//
//Code segment: f1(); Procedure: 0
//
0000: CA007B00 x:S00(short) = 123
0004: 7B Return() // f1()
void f2() {int x = 456; }
**Info***:'x' is written but has no read references
//
//Code segment: f2(); Procedure: 1
//
0000: CA00C801 x:S00(short) = 456
0004: 7B Return() // f2()
task main()
//
//Code segment: main(); Task: 0
//
0000: 614000 StopAllMotors()
0003: 6732000000000100 configureSerialPort(UART0, uartSystemCommPort)
000B: 6732000100000000 configureSerialPort(UART1, uartNotUsed)
0013: 6732000200000300 configureSerialPort(UART2, uartVEXLCD)
001B: 676701001C ClearSensors(in1..I2C_8)
0020: 676D01000A ClearMotors(port1..port10)
{
f1();
0025: 170000 callSub(f1, 0)
f2();
0028: 170100 callSub(f2, 0)
}
002B: 614000 StopAllMotors()
002E: 7B Return() // main()
0000: 7B Return() // _main__()
ROBOTC gives each function a number starting at 0 in the order they appear in the source file. It knows what the number of each function is and then uses this in the subroutine call, there are a maximum of 256 functions (if i remember correctly) possible but so far I have never been close to that limit.
Looking at that byte code, it wouldn’t be hard to add support for a type of function pointer.
this is robotc’s own virtual machine so all they need is to add an indirect version of callSub say callSubInd and a new variable type functionpointer which is in effect a byte which stores the byte index of the function. callSubInd is then just passed a reference to the functionpointer.
Yes I saw that :), However a lot of the early machines I programmed had the same arcutecture (ok N64 was probably the only one I programmed in C, and PS1 stored it all in ram) but they had no problem with function pointers. Ok you can’t use self modifying code, but since the day of caches you couldn’t anyway… and the program still has to access ram to get function parameters etc. I wonder where the virtual machines PC is stored…
hmmm
yes but as far as I can see the only way to do this is create a function that knows about everything… although… thinking about it, you could give each class its own dispatch table/code.
Ok the master dispatch code would have to know about all the classes, but at least it wouldn’t have to know specifcally about each function in a class, and you could then allow classes to be derived… although sort of upside down.