Best user-friendly way to set multiple config variables

I have a struct of robot config settings, which includes variables like


wheelBaseWidth

,


wheelSize

and thresholds for autonomous driving. What is the best/most standard way to set all of these?

Options I’ve though of include:

  • call functions like

setWheelBaseWidth()

for each setting,

  • call a function like

setRobotSettings(wheelSize, wheelBaseWidth, thresholdOne, thresholdTwo, etc)

,

  • manually enter data into the struct like

driveSettings.wheelBaseWidth = 16

for each setting,

  • use

#define

for each setting (this would be run better, but creates a lot of clutter, and the definitions would need to be before the


#include "library.c"

.

Also, should I get the user to


setSettingDefaults

and just change what they want (the danger is that the user might not read the default values, and might not know of certain settings.

It is somewhat a matter of preference. A struct is very clean to switch between a whole set of variables.

I don’t know if ROBOTC supports this, but I’m a fan of


drive_t driveSettings = { .wheelSize = 1, .wheelBaseWidth = 2, .thresholdOne = 4, .thresholdTwo = 0, .etc = -1 };

That’d only work when you declare and define at the same time. Here’s a link with some more syntax. Note that it’s very C-flavor specific, so what may work for one specification (i.e. PROS/C99 may not work with PROS/C90, let alone ROBOTC).

@edjubuh That’s a really nice way of writing it. Unfortunately it doesn’t work in ROBOTC 4.55 (


Unexpected '.' during parsing

).

EDIT:
Turns out you can’t even declare it like


drive_t driveSettings = { 1, 2, 4, 0, -1 };

, ROBOTC throws the error


No code generation yet for dynamic initialization of 'struct' variables

. In this link from 2012, a ROBOTC.net admin says don’t count on getting the functionality soon.

That is pretty easy to work around.

#define or a helper function could both work. The #define could fully simulate that exact format while a function would need minor changes.

Do you think you could provide an example please? I haven’t got much experience with using #define for anything other than constants.

I’ve tried:


#define struct2 ((TautoDriveSettings){.wheelSize = 2, .minSpeed = 5})
#define struct3 (drive_t { 1, 2, 4, 0, -1 })
#define drive_t driveSettings = { 1, 2, 4, 0, -1 }

And none of them seem to work.

Thanks :slight_smile:

I honestly don’t know how @tabor473 plans to “fully simulate that exact format” (I would love to find out how), but my suggestion is to do as follows:

//blah blah blah TYPICAL COMPETITION TEMPLATE STUFF, etc.

#define WHEEL_SIZE driveSettings.wheelSize =
#define WHEEL_BASE_WIDTH driveSettings.wheelBaseWidth =
#define THRESHOLD_ONE driveSettings.thresholdOne
#define THRESHOLD_TWO driveSettings.thresholdTwo

typedef struct {
  char wheelSize;
  int wheelBaseWidth;
  short thresholdOne;
  long thresholdTwo;
} drive_t;
drive_t driveSettings;

void pre_auton(){
  WHEEL_SIZE 1; WHEEL_BASE_WIDTH 2; THRESHOLD_ONE = 3; THRESHOLD_TWO = 4;

//blah blah blah REST OF YOUR CODE, etc.

I used 2 slightly different formats:


WHEEL_SIZE

and


WHEEL_BASE_WIDTH

had equal signs in the


#define

statements, while


THRESHOLD_ONE

and


THRESHOLD_TWO

needed equal signs added in usage; you can choose the format that you prefer.
I know this wasn’t quite what you wanted, but this is the most user-friendly way to accomplish the task that I can think of. You can also hide away everything from (inclusive) my first comment to


drive_t driveSettings;

in an


#include

file if you so desire.

In most cases, I find that


#define

can be thought of as just a copy-and-paste system; for example,


driveSettings.wheelSize =

is pasted in place of


WHEEL_SIZE

automatically at compile time. More info on C preprocessors here

Thanks, I’d never thought of having a trailing equals, that’s really neat :slight_smile: I may or may not use your ideas because you’re basically changing one variable name for another, seems kind of pointless.

At the moment I have a function


autoDriveInit(wheelSize, wheelBaseDiameter, minSpeed=25)

for all the essential / basic settings (it also sets reasonable defaults). The user (which to be honest will probably just be me) can set any fancier settings manually (


driveSettings.wheelBaseWidth = 15

)

Technically one does not


#define

a variable… But, other than that, you’re not wrong in your assessment. I do agree that my code serves little purpose other than just looking nicer.

My excuse (albeit meaningless): I use PROS, so I’ve never had to be inventive in this fashion…

Anyway, I hope to see what @tabor473 has come up with.

Okay so my first version was great demonstration until I tried to actually call the value. I had made a mistake in the scope of the declared variable.

The fixed version is a little less effective than I imagined.
So using macros I would do this

#define MAKE_DRIVE(_name,_wheel,_width,_thresOne,_threshTwo)\
do{\
	_name.wheelSize = _wheel;\
	_name.wheelBaseWidth = _width;\
	_name.thresholdOne = _thresOne;\
	_name.thresholdTwo = _threshTwo;\
}while(false)


typedef struct {
	char wheelSize;
	int wheelBaseWidth;
	short thresholdOne;
	long thresholdTwo;
} drive_t;

drive_t driveSettings;
task main()
{
	MAKE_DRIVE(driveSettings,1,2,3,4);
}

MAKE_DRIVE sets the variables in the struct whose name it is passed. I had thought I could put the variable declaration in the define as well but it would be scoped in the do while statement.

Why even bother with the


do


while

? Wouldn’t the code work just fine with just the series of statements inside the


do{}

?

uhh
do{}
isn’t valid c

I mean get rid of the


do{

and get rid of the


}while(false)

, leaving just the 4 setter statements. Sorry for the poor wording in my previous post

Okay so precompiler macros require the code to be grouped.

Good description here
http://www.cprogramming.com/tutorial/cpreprocessor.html

Scroll down to
Multiline macros

What’s the advantage in your opinion of using preprocessor macros vs. a function? It looks the same to me. It accomplishes the same thing in the same number of instructions with any reasonable optimization.

I see now why you did that.

I would actually prefer to use a function in that case to avoid the extra


do{} while()

.