RobotC preprocessor

RobotC preprocessor is acting funny. While trying to enforce reasonable code structure, I wanted to introduce my students to the common C pattern of guard ifdef:


#ifndef _LIBRARY_H
#define _LIBRARY_H
... library code ...]
#endif

But when we tried to include from multiple places, RobotC issued a warning (and apparently took care of excluding redundant include itself).

Worse yet, we have one pattern where we actually need to include a single header twice (with different defines each time), and that, due to RobotC avoiding legal repeated includes, doesn’t work at all. Is there a way to make the RobotC preprocessor behave in a little more standard way?

Okay so short answer is I am pretty sure you can’t get it to act in a standard way. Also confused how it could be helpful.

So it all comes down to the linker. ROBOTC doesn’t have one. So normal behavior of 5 c files would be they each might include math.h and you want them each to only include it once so you put a header guard. In ROBOTC you are including your c files into your main c file, so each c file including math.h will be stacked on top of each other. Because any file being included counts as included everywhere you should only really need each file to be included once. Their include guards are probably the only thing stopping tons of redundant functions when people include a file in multiple c files.

I think I follow what your goal is


#define bananna 2 
#include file 
#define bananna 1
#include file

But I am pretty confused with exactly what that gives you that couldn’t be achieved another way wayyyy easier… (It is however after 3 am so maybe I could have some ideas in the morning)

Include guard is unrelated to linker and is plenty necessary in common C world, where system headers cross-include and, for example, use defines which would otherwise lead to multiple definitions…

How could repeated includes be helpful? Consider a simple program selection code of:

int index;
string names] = { "First", "Second", "Simple" };
void pre_auton() {
  index = lcdProgramSelect(names, ARRAYSIZE(names));
}
task autonomous() {
  switch (index) {
    0: runFirst(); break;
    1: runSecond(); break;
    2: runSimple(50); break;
  }
}

This is pretty much what my middle schoolers came up with. But it is also quite error prone, since they have to manually match names and indexes. If RobotC had function pointers, they could have used a struct pairing a program name with its implementation. So I tried suggesting this preprocessor technique:


-------- programs.h: --------
AUTO_PROGRAM("First", runFirst())
AUTO_PROGRAM("Second", runSecond())
AUTO_PROGRAM("Simple", runSimple(50))
-------- main.c: --------
string names] = {
#define AUTO_PROGRAM(name, code)                \
    name,
#include "programs.h"
#undef AUTO_PROGRAM
};

task autonomous() {
 #define AUTO_PROGRAM(name, code)                \
    if (index-- == 0) {                        \
        code;                                    \
    } else

#include "programs.h"
#undef AUTO_PROGRAM
;
}

[/CODE]

(in reality hidden in helper header files so the code in main.c is much simpler. No, I didn't invent this, it's decades old approach)
Try running above through any standard C preprocessor in case the trick isn't clear.

But, alas, RobotC seems to drops the second include altogether instead of re-interpretting it under the new definition (If I copy programs.h into programs2.h and use that in the second include, the code works exactly as intended. But, we're back at consistecy).

Now, I don't care about this particular technique, I am primarily looking for a simple way to let them
pair their autonomous routines with labels (and indexes) . Any better idea?

(In an ideal world, I'd let them spread the auton labels anywhere over the code, used attribute for a linker section assignment,  write a not-so-fancy ld script and picked up the linker-generated array of structs at runtime. But they aren't ready for PROS yet...)

I am just thinking an enums so hard-coded index values are replaced with simple text but of course that could be done with a variable just as easily.

I will definitely read this thread again after I finally go to bed and see if I have any other thoughts.

I understand what you’re trying to do, and I know that this and many similar techniques are essential to large project development in C. Been there, a lot. But RobotC is a C-like environment and tool “chain” (though the chain is very short here) not really a C environment. The “preprocessor” to the degree there is one, isn’t going to do what a C programmer would predict and expect. Also, many build environments give you a way to invoke external tools like preprocessors; that would solve the problem as well. Can’t do that in RobotC.

@jpearman may be able to say how wrong I am, and offer something that makes multiple includes of the same file with different constants work as expected. I hope so, but I doubt it.

ROBOTC caches files that is has opened, that allows you to edit a file and recompile the project without saving the file. Many of these decisions were made by my predecessors many years ago.

I tried this hack and it seems to work.
programs.h

#define PROGRAMS AUTO_PROGRAM("First", runFirst()) \
AUTO_PROGRAM("Second", runSecond()) \
AUTO_PROGRAM("Simple", runSimple(50))

main.c

#include "programs.h"

string names] = {
#define AUTO_PROGRAM(name, code)                \
    name,
    PROGRAMS
#undef AUTO_PROGRAM
};

void runFirst() {
}
void runSecond() {
}
void runSimple( int n ) {
}

task autonomous() {
    int index = 0;
    
#define AUTO_PROGRAM(name, code)                \
    if (index-- == 0) {                        \
        code;                                    \
    } else
    PROGRAMS

#undef AUTO_PROGRAM
;
}

task main() {
  writeDebugStreamLine("%s", names[0]);
  writeDebugStreamLine("%s", names[1]);
  startTask( autonomous );
}

Thank you James, that inspired me!
If everything else fails, add one more level of indirection!
OK, so I took this idea and optimized it a little. This is best I can get in RobotC:
programs.h (to be edited):


#define PROGRAMS                                \
AUTO_PROGRAM("Sample", autoSample(50))          \
AUTO_PROGRAM("Sample 60", autoSample(60))

include/program-names.h (fixed, part of project template):

define AUTO_PROGRAM(name, code)                \
    name,
PROGRAMS
#undef AUTO_PROGRAM

include/program-dispatch.h (fixed, part of project template):


#define AUTO_PROGRAM(name, code)                \
    if (index-- == 0) {                         \
        code;                                   \
    } else
PROGRAMS;
#undef AUTO_PROGRAM

Usage (in main.c):


#include "auto-programs.h"

// generated list of all declared autonomous programs
string programs] = {
#include "include/program-names.h"
};

task autonomous(){
    // generated dispatch code for all declared autonomous
    // programs, including the arguments
    int index = autonomousProgramIndex;
#include "include/program-dispatch.h"
}

Now, when working towards this, I actually added one more level of indirection and had the following framework:
programs.h (user edited to add more programs):


define PROGRAMS(AUTO_PROGRAM)                   \
AUTO_PROGRAM("Sample", autoSample(50))          \
AUTO_PROGRAM("Sample 60", autoSample(60))

include/program-macros.h (fixed, part of project template):


#define _PROGRAM_DISPATCH(name, code)                \
    if (_index-- == 0) {                             \
        code;                                        \
    } else

#define DISPATCH(indexVar)                           \
    int _index = indexVar;                           \
    PROGRAMS(_PROGRAM_DISPATCH)

#define _PROGRAM_NAME(name, code)                    \
    name,

#define PROGRAM_NAMES PROGRAMS(_PROGRAM_NAME)

The usage is then very simple and intuitive:


#include "programs.h"
#include "include/program-macros.h"

// generated list of all declared autonomous programs
string programs] = { PROGRAM_NAMES };

void autonomous(){
    // generated dispatch code for all declared autonomous programs with arguments
    DISPATCH(autonomousProgramIndex);
}

But while the above works well with gcc’s preprocessor, RobotC doesn’t like it no matter how I slice it.
I have tried adding another level of indirection and plenty of other tricks, but RobotC either doesn’t evaluate the macro at all, or ends up complaining about wrong number of arguments somewhere.

Does anyone here see another trick that might get us close to this form in RobotC?

Ok, we are pushing the pre-processor too far, it’s not that good. However, this is what I came up with.

programs.h


#define PROGRAMS(X)                             \
AUTO_PROGRAM_##X("Sample", autoSample(50))      \
AUTO_PROGRAM_##X("Sample 60", autoSample(60))

program-macros.h

#define AUTO_PROGRAM_A(name, code)                   \
    name,

#define AUTO_PROGRAM_B(name, code)                   \
    if (_index-- == 0) {                             \
        code;                                        \
    } else

#define PROGRAM_NAMES                                \
    PROGRAMS(A)
    
#define DISPATCH(indexVar)                           \
    int _index = indexVar;                           \
    PROGRAMS(B)

and main.c end’s up the same as you had.

#include "programs.h"
#include "program-macro.h"

// generated list of all declared autonomous programs
string programs] = { PROGRAM_NAMES };

int autonomousProgramIndex = 0;

void
autoSample(int x ) {
}

task autonomous() {
    // generated dispatch code for all declared autonomous programs with arguments
    DISPATCH(autonomousProgramIndex);
}

task main() {
  startTask(autonomous);
}

This works and fits the bill perfectly, thank you!
(Now the real litmus test is whether the middle-schoolers would grasp it and adopt/adapt for their use.)