views:

56

answers:

4

I've got a list of definitions: MASTER, SLAVE0, SLAVE1, ... SLAVE9 to control which array of audio data is programmed into a microcontroller. The micro can hold no more than one sound file, so I have included the following definitions at the top of my main.c file:

#define MASTER
#define SLAVE0
#define SLAVE1
....
#define SLAVE9 

Then, I write the following in my audio array:

#if defined(MASTER)
    uint8_t sound[4096] PROGMEM = {127,126, ... }
#elif defined(SLAVE0)
    uint8_t sound[4096] PROGMEM = {126,128, ... }
....
#else 
    #ERROR "One of MASTER-SLAVE9 must be defined!"
#endif

The person wishing to compile must then go through and comment out one and only one of the #define lines. This is not only tedious, but also error-prone. So, I'm looking to simplify the process. Any pointers for any of the following tasks would be helpful:

  1. How can I test a list of macros for the presence of one and only one of these options? A simple if defined(MASTER) && !(defined(SLAVE0) || defined(SLAVE1) ...) would require 11 such tests, each with 11 subtly different conditions. It's a one time expense, but it's kinda ugly. It feels like this might be a common need, and that there ought to be a better way.
  2. How can I simplify the compilation process? I've been using AVRStudio with WinAVR t0 compile. It has an 'export makefile' option, but I have no experience with it. I'm stuck doing this on Windows. An ideal process would build all 11 configurations in a single command, and then I could go through and program each one to the microcontroller individually. The current, very much less-than-ideal build process involves editing the source each time I want to build, and renaming/moving the output file.
+2  A: 

You can use a single test to ensure that only one of the macros is defined.

#if (defined(MASTER) + defined(SLAVE1) + defined(SLAVE2)) != 1
    #error "Too many macros defined"
#endif

As for the definition itself, most compilers allow you to define a macro using a command line option; this might be cleaner than a file with a "configurable options list." You would then be able to create multiple build configurations, each of which defines a different macro, and build them each in sequence (I'm not familiar with your build system to be able to explain how exactly you need to do that).

James McNellis
I wasn't aware that a definition without an argument defaulted to one - Good to know! I'll poke around my system and investigate the possibility of scripting the process.
reemrevnivek
@reemrevnivek: It doesn't. A definition without a replacement list simply means that name is replaced by nothing when it is expanded. However, the `defined` operator yields `1` if the macro is defined and `0` if the macro is not.
James McNellis
Whoops, that's what I meant.
reemrevnivek
A: 

Why not something like:

#define ARRAY_NAME        (SLAVE0)

...


#if (ARRAY_NAME == MASTER)
    // blah
#elif (ARRAY_NAME == SLAVE0)
    // blah
// etc.

or even better, just:

#define ARRAY_MASTER { 1, 2, 3, 4 }
#define ARRAY_SLAVE0 { 5, 6, 7, 8 }
// etc.

...

const uint8_t sound[] = ARRAY_MASTER;
Oli Charlesworth
What goes in `...` and `blah`? I should have clarified that I do some other conditional compilation based on this definition elsewhere in the code, so changing the rvalue in the definition of sound[] would make these operations difficult.
reemrevnivek
+1  A: 

I would just make a block comment with the name of all possible constants, and follow it with a single define. Who wants to compile just writes what he wants. First time he will check the comment to see the list, then he will just write.

Or even better, keep this list in a comment (for reference) and use the -D option that most compilers have (-DMASTER to define master for example) and if your tool supports it, make a build configuration for each where you change the -D value. Using a different build configuration i guess you could also change the output file name, so that would kill two birds with a stone.

Michele Balistreri
+1 for the -D option. I checked, and my compiler (avr-gcc) does support this. A block comment is less useful, because every time I want to reprogram the chain, I need to go through each option one by one.
reemrevnivek
Your tools does not support multiple configurations? If it doesn't. you can export to Makefile. You can then copy the Makefile, change the output target and the -D option, then make a very trivial shell script which calls "make" on each Makefile (I have no idea how it works on Windows though, but if you have cygwin you should have a bash shell and so on)
Michele Balistreri
A: 

You are need an error message when you deined mre than one macro? Well, just write:

#ifdef MASTER
uint8_t sound = { ... };
#endif
#ifdef SLAVE0
uint8_t sound = { ... };
#endif
#ifdef SLAVE1
uint8_t sound = { ... };
#endif

And compiler will complain that one identifier defined multiple times.

Also why not use this?

#define SLAVE <n>

uint8_t sound_master = { ... };
uint8_t sound_slave_0 = { ... };
uint8_t sound_slave_1 = { ... };
uint8_t sound_slave_2 = { ... };

#define XCAT(a,b) a##b
#define CAT(a,b) XCAT(a,b)
#ifdef SLAVE
#define sound CAT(sound_slave_,SLAVE)
#endif
#ifdef MASTER
#ifdef sound
/* warnin or so. but if you need an error just remove this ifdef **/
#endif
#define sound sound_master
#endif
Vovanium