Pages: [1]   Go Down
Author Topic: Conditional compilation fails  (Read 2165 times)
0 Members and 1 Guest are viewing this topic.
UK
Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm trying to do some checking whether const or #define is more memory efficient and so I have the fragment
Code:
#define TYPE long
#define USECONST
#ifdef USECONST
const TYPE KEYA = 1;
#else
#define KEYA ((TYPE)1)
#endif
boolean v;
This works fine, using constants.  However
Code:
#define TYPE long
#undef USECONST
#ifdef USECONST
const TYPE KEYA = 1;
#else
#define KEYA ((TYPE)1)
#endif
boolean v;
fails horribly: for example 'boolean does not name a type'.  I assumed for a moment I'd not got the #define correct, but then I changed the code to
Code:
#define TYPE long
#undef USECONST
#ifdef USECONST
//const TYPE KEYA = 1;
#else
#define KEYA ((TYPE)1)
#endif
boolean v;
and it worked just fine again: the other way (using definitions).  I feel like a complete idiot: obviously conditional compilation doesn't work the way I always thought it did, or I've missed a comma somewhere, or something.  Any ideas about what is wrong here ?
Logged

0
Offline Offline
Shannon Member
****
Karma: 206
Posts: 12048
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The way the pre-processing and compiling of sketches works is rather strange and fussy - I believe (but only based on a few issues I've had) that you should place all typedefs in a header file, not in the .ino - you don't say where those code snippets are, but I'd suggest moving them to a mytypes.h or similar header file in your sketch and #include that.
Logged

[ I won't respond to messages, use the forum please ]

Austin, TX
Offline Offline
Full Member
***
Karma: 2
Posts: 182
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

post the compile output.
Logged

Chris J. Kiick
Robot builder and all around geek.

Offline Offline
Edison Member
*
Karma: 26
Posts: 1339
You do some programming to solve a problem, and some to solve it in a particular language. (CC2)
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The way the pre-processing and compiling of sketches works is rather strange and fussy - I believe (but only based on a few issues I've had) that you should place all typedefs in a header file, not in the .ino - you don't say where those code snippets are, but I'd suggest moving them to a mytypes.h or similar header file in your sketch and #include that.

typedef != #define

There's no problem in placing those inside the .ino file.

If the typedefs or #defines have to be used in multiple files, however, your suggestion is sound, as it avoids code duplication, which is a Good Thing(tm).
Logged

UK
Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the replies... I suppose the compile output would be more than just the message I quoted.  But... I changed the code to include one of two headers files, each with the different stuff in, and it worked OK.  But I think there is a bug in the compilation process.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 610
Posts: 49034
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
But I think there is a bug in the compilation process.
Go ahead and think that. Or, post some real code, some real compiler output, and let us see whether there really is a bug, or whether it is just in your not understanding what the IDE does to your code first.
Logged

UK
Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

OK, PaulS... this is what you wanted...  don't blame me for the size of the post...
Code:
#include <EEPROM.h>
#undef INIT
#define EPROM 1024

#define USECHAR
#ifdef USECHAR
#define TYPE char
#else
#define TYPE long
#endif

#define USECONST
#ifdef USECONST
const TYPE KEYA =  1;   
#else
#define KEYA ((TYPE) 1)   
#endif
boolean v;

int freeRam() {
  extern int __bss_end; // == extern unsigned int __heap_start;
  extern void * __brkval;
  int freememory;
  freememory = ((int)&freememory) - (((int)__brkval == 0)? ((int)&__bss_end): ((int)__brkval));
  return freememory;
}
unsigned long sketchSize(void) {
  extern int _etext;
  extern int _edata;
  return ((unsigned long)(&_etext) + ((unsigned long)(&_edata) - 256L));
}
void setup() {
  Serial.begin(9600);
  TYPE tt = random();
  boolean w = true;
  v = tt == KEYA;
  w = w && v;
  Serial.print("using ");
  #ifdef USECHAR
  Serial.print("char ");
  #else
  Serial.print("long ");
  #endif
  #ifdef USECONST
  Serial.print("constants");
  #else
  Serial.print("definitions");
  #endif
  Serial.print(", sketchsize "); Serial.print(sketchSize());
  Serial.print(", freeram ");Serial.println(freeRam());
  Serial.println(w);
}

void loop() {
  ;
}

This generates the following compiler output...
Code:
avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=101 -I/usr/share/arduino/hardware/arduino/cores/arduino -I/usr/share/arduino/hardware/arduino/variants/standard -I/usr/share/arduino/libraries/EEPROM /tmp/build3179014252548270902.tmp/eepromtest.cpp -o /tmp/build3179014252548270902.tmp/eepromtest.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/EEPROM/EEPROM.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/wiring_pulse.c.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/wiring_analog.c.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/WInterrupts.c.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/wiring_digital.c.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/wiring.c.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/wiring_shift.c.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/HID.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/WString.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/main.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/Stream.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/Tone.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/HardwareSerial.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/Print.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/IPAddress.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/new.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/CDC.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/WMath.cpp.o
  Using previously compiled: /tmp/build3179014252548270902.tmp/USBCore.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/wiring_pulse.c.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/wiring_analog.c.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/WInterrupts.c.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/wiring_digital.c.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/wiring.c.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/wiring_shift.c.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/HID.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/WString.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/main.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/Stream.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/Tone.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/HardwareSerial.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/Print.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/IPAddress.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/new.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/CDC.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/WMath.cpp.o
avr-ar rcs /tmp/build3179014252548270902.tmp/core.a /tmp/build3179014252548270902.tmp/USBCore.cpp.o
avr-gcc -Os -Wl,--gc-sections -mmcu=atmega328p -o /tmp/build3179014252548270902.tmp/eepromtest.cpp.elf /tmp/build3179014252548270902.tmp/eepromtest.cpp.o /tmp/build3179014252548270902.tmp/EEPROM/EEPROM.cpp.o /tmp/build3179014252548270902.tmp/core.a -L/tmp/build3179014252548270902.tmp -lm
avr-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 /tmp/build3179014252548270902.tmp/eepromtest.cpp.elf /tmp/build3179014252548270902.tmp/eepromtest.cpp.eep
avr-objcopy -O ihex -R .eeprom /tmp/build3179014252548270902.tmp/eepromtest.cpp.elf /tmp/build3179014252548270902.tmp/eepromtest.cpp.hex
Binary sketch size: 3,006 bytes (of a 32,256 byte maximum)

undefining USECONST, as follows...

Code:
#undef USECONST
#ifdef USECONST
const TYPE KEYA =  1;   
#else
#define KEYA ((TYPE) 1)   
#endif

Generates the following error output ...

Code:
avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=101 -I/usr/share/arduino/hardware/arduino/cores/arduino -I/usr/share/arduino/hardware/arduino/variants/standard -I/usr/share/arduino/libraries/EEPROM /tmp/build3179014252548270902.tmp/eepromtest.cpp -o /tmp/build3179014252548270902.tmp/eepromtest.cpp.o
eepromtest.cpp:42:1: error: ‘boolean’ does not name a type
eepromtest.cpp: In function ‘void setup()’:
eepromtest.cpp:57:3: error: ‘Serial’ was not declared in this scope
eepromtest.cpp:58:20: error: ‘random’ was not declared in this scope
eepromtest.cpp:59:3: error: ‘boolean’ was not declared in this scope
eepromtest.cpp:59:11: error: expected ‘;’ before ‘w’
eepromtest.cpp:60:3: error: ‘v’ was not declared in this scope
eepromtest.cpp:61:3: error: ‘w’ was not declared in this scope


In case you're wondering about including EEPROM.h, that was when I was using the code for another purpose, but I left it in anyway.

Perhaps you now feel able to comment on the operation of the IDE, Version 1.0.1, by the way.

By the way, I am well aware that const and #define do different things... I wrote the code in order to discover just how different.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 610
Posts: 49034
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Perhaps you now feel able to comment on the operation of the IDE, Version 1.0.1, by the way.
Sure. The IDE modifies your sketch. It adds some include statements, function prototypes, and a main() function.

The key is where it does this.

Add a statement to the top of your first sketch:
Code:
int dummy;

Compile, and you'll get no errors.

Now, make the change to produce the second sketch, with the dummy variable stiff defined. No errors this time, either.

So, there clearly is not a bug in the compiler. The bug is in where the IDE inserts it's stuff. If you enable verbose output in the IDE, you can see the file that the IDE has modified:

Code:
#undef USECONST
#ifdef USECONST
#include "Arduino.h"
int freeRam();
unsigned long sketchSize(void);
void setup();
void loop();
const TYPE KEYA =  1;   
#else
#define KEYA ((TYPE) 1)   
#endif
boolean v;

You can see that the IDE added stuff in the middle of the conditional block, just before the variable declaration statement. Since that block of code will not be seen by the compiler, stripped out by the preprocessor, the include file is not included.

By putting the int dummy; statement at the top, you change where the IDE adds stuff:
Code:
#include "Arduino.h"
int freeRam();
unsigned long sketchSize(void);
void setup();
void loop();
int dummy;
Now, clearly the added stuff is unconditionally added, so there is no problem with compilation.
Logged

Offline Offline
God Member
*****
Karma: 32
Posts: 830
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The short ansewer is that the Arduino IDE preprocessor is buggy, and it will often break completely valid code by preprocessing it into completely uncompilable garbage.

One trick is to put the code that confuses it into an include file; the Arduino IDE preprocessor won't touch anything in an included file.

The other is to put a dummy executable statement before the code that confuses it, as in the example above; that _sometimes_ stops it messing things up after that point. But it is not as robust as the include file method.

You can see the exact error it has caused by looking at the munged <sketchname>.cpp output file in the temporary build directory. Sometimes the results are quite comical! I've seen attempts at extracting a prototype header that look like

else if (condition) {

Yes, it can get that confused... as I say, you have to see some of the stuff it produces to believe it.

The _best_ trick is to simply avoid the Arduino IDE preprocessor altogether. That's what I do, anyway. I use emacs as editor, in combination with Martin Oldfield's makefiles for Arduino builds. And it means that you get normal C/C++ preprocessing working properly and predictably again, which is a significant bonus.


« Last Edit: October 08, 2012, 06:56:08 am by pico » Logged

WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 610
Posts: 49034
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
One trick is to put the code that confuses it into an include file; the preprocessor won't touch anything in an included file.
The preprocessor will to. The IDE won't. Different applications; different processes.

Quote
The _best_ trick is to simply avoid the preprocessor altogether.
I think that you are confusing the C preprocessor and the IDE.
Logged

Offline Offline
God Member
*****
Karma: 32
Posts: 830
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
One trick is to put the code that confuses it into an include file; the preprocessor won't touch anything in an included file.
The preprocessor will to. The IDE won't. Different applications; different processes.

Quote
The _best_ trick is to simply avoid the preprocessor altogether.
I think that you are confusing the C preprocessor and the IDE.

No. I'm referring to the Arduino IDE preprocessor, which is part of the IDE, NOT the C/C++ preprocessor, which is part of avr-gcc.

The C/C++ preprocessor doesn't come into play until after the Arduino IDE preprocessor has already munged things into the <sketchname>.cpp file in the temp build directory. By then, it has already wreaked it's havoc.

In case there is any possible ambiguity I've added "Arduinido IDE" before the word "preprocessor" in my earlier post if appropriate. I appreciate not everyone has a clear understanding of the order of events in the build process.
 
« Last Edit: October 08, 2012, 07:10:08 am by pico » Logged

WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

UK
Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think the point I made is proved, but the terms I used were obviously confusing.  Simply viewed, 'compilation' is everything that happens after I press the 'compile' button in the IDE.  And it is buggy, as discussed above.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 610
Posts: 49034
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
And it is buggy, as discussed above.
It's not buggy. It's "feature rich".
Logged

Offline Offline
God Member
*****
Karma: 32
Posts: 830
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
And it is buggy, as discussed above.
It's "feature rich".

If that's the case, then so is what comes out of the rear end of a horse.

Logged

WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

Pages: [1]   Go Up
Jump to: