What are you actually trying to accomplish? The Arduino code derives peripheral types from peripheral defines, without going through the intermediate step of guessing an exact chip (although that's implicit.) For example:
#if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */
sbi(TCCR4B, CS42); // set timer4 prescale factor to 64
sbi(TCCR4B, CS41);
sbi(TCCR4B, CS40);
sbi(TCCR4D, WGM40); // put timer 4 in phase- and frequency-correct PWM mode
sbi(TCCR4A, PWM4A); // enable PWM mode for comparator OCR4A
sbi(TCCR4C, PWM4D); // enable PWM mode for comparator OCR4D
#else /* beginning of timer4 block for ATMEGA1280 and ATMEGA2560 */
#if defined(TCCR4B) && defined(CS41) && defined(WGM40)
sbi(TCCR4B, CS41); // set timer 4 prescale factor to 64
sbi(TCCR4B, CS40);
sbi(TCCR4A, WGM40); // put timer 4 in 8-bit phase correct pwm mode
#endif
You MIGHT be able to get some traction out of the "io*x*" include files. For example, there's an "iomx8.h" that defines the common symbols for mega48, mega88, and mega168. It of course defines "_AVR_IOMX8_H_" as part of the usual "allow multiple includes of the same file" provisions. But you'll notice that it's not included by iom328.h, iom328p.h, iom168p.h, and other "newer" chips that ARE in the same family. I think that's because new chips have their io*.h files generated individually from xml files somewhere, while older io*.h files were put together manually. :-(
Your discovery about "expressions cpp doesn't like" means that an idea I had about defining UART structures based on checking distances between known registers won't work either :-(
It shouldn't be too hard to put together an "avrfamilyXX.h" that exhaustively puts all the chips you're interested in into families.
/* avrfamilymxx8.h - atmega48/88/168/328 family chips */
#ifdef __AVR_ATmega88__
#define __AVR_FAMILY_MXX8
#endif
#ifdef __AVR_ATmega168__
#define __AVR_FAMILY_MXX8
#endif
#ifdef __AVR_ATmega168p__
#define __AVR_FAMILY_MXX8
#endif
#ifdef __AVR_ATmega168pa__
#define __AVR_FAMILY_MXX8
#endif
#ifdef __AVR_ATmega168pb__
#define __AVR_FAMILY_MXX8
#endif
// etc
(ugly verbose format chosen for ease of editing and possible automatic generation...)
Then you could have your code include checks for all the families you're interested in handling, like:
#include "avrfamilymxx8.h"
#include "avrfamilytnxx8.h"
#include "avrfamilymxdip40.h"
You might be amused by my recent discovery about the new ATmega328pb. Apparently it has a second uart and twi port.
That ends up meaning that "TWBR0" and "TWBR1" are bitmasks on the m328 and m328p, but SFR register pointers on 328pb!
(of course, it's a bit stupid that they're defined as bitmasks anywhere, since TWBR holds a byte quantity. But still...)