[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [avr-libc-dev] New pgm_read_ptr() macro?

From: David Brown
Subject: Re: [avr-libc-dev] New pgm_read_ptr() macro?
Date: Thu, 03 Jun 2010 09:03:14 +0200
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv: Gecko/20100317 Lightning/1.0b1 Thunderbird/3.0.4


Are there any opinions about whether a macro such as the one I gave below should be part of avrlibc? It's not as clear-cut as Dean's pgm_read_ptr() macro, which follows the existing patterns in the headers. Macros like mine are more like template programming than the fixed-size macros, and thus are a slight change in philosophy.

If the idea of a generic pgm_read(&x) macro does appeal, then the same tricks can be used for other accesses such as wrapping an access in "volatile", reading and writing eeprom, and atomically accessing data.



How about a more general solution based on typeof and sizeof?

#define pgm_read(v) ((typeof(*v)) \
(__builtin_choose_expr(sizeof(*v) == 1, pgm_read_byte(v), \
__builtin_choose_expr(sizeof(*v) == 2, pgm_read_word(v), \
__builtin_choose_expr(sizeof(*v) == 4, pgm_read_dword(v), \
({ char error[((sizeof(*v) == 1) || (sizeof(*v) == 2) || \
(sizeof(*v) == 4)) ? 1 : -1]; error[0]; }) \

The macro above looks a bit ugly, but it's not too hard to follow.
Basically, we are using sizeof(*v) to figure out the size of the data
you are wanting, and using it to pick the correct pgm_read_xxx function
for the data size. We use __builtin_choose_expr instead of ?: to avoid
any unwanted expression evaluations or side effects, as well as to allow
different sized return values. The "char error[...]" part is a way to
force a compile-time error if the macro is used with something whose
size is not 1, 2 or 4 bytes.

The result is that you can write:

char x;
char* PROGMEM FlashPointer = &x;
char* FlashPtr = pgm_read(&FlashPointer);

You can equally write:

static const PROGMEM uint8_t x8 = 8;
static const PROGMEM uint16_t x16 = 16;
static const PROGMEM uint32_t x32 = 32;

void test(void) {
volatile uint8_t y8 = pgm_read(&x8);
volatile uint16_t y16 = pgm_read(&x16);
volatile uint32_t y32 = pgm_read(&x32);

Now your pgm_read's are independent of the type, assuming they have a
compatible size.

Personally, my preference would be to change the semantics to remove the
"&", so that you would write:

char* FlashPtr = pgm_read(FlashPointer);

This is just a small change to the macro, but I think it's neater - you
are reading an object from flash, rather than reading data from an
address in flash. But such a change would require a name change to avoid



AVR-libc-dev mailing list

reply via email to

[Prev in Thread] Current Thread [Next in Thread]