implemented typesafe PSTR()

This makes PSTR() type safe by using a 1 byte wrapper
structure. Attempts to use the wrong varient of a print function will
generate a compilation error.

git-svn-id: https://arducopter.googlecode.com/svn/trunk@1797 f9c3cf11-9bcb-44bc-f272-b75c42450872
This commit is contained in:
tridge60@gmail.com 2011-03-21 07:25:48 +00:00
parent fd10614822
commit 211de598c3
10 changed files with 61 additions and 33 deletions

View File

@ -1,4 +1,4 @@
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*- // -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
// //
// This is free software; you can redistribute it and/or modify it under // This is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the // the terms of the GNU Lesser General Public License as published by the
@ -21,6 +21,14 @@
#undef round #undef round
#undef abs #undef abs
// prog_char_t is used as a wrapper type for prog_char, which is
// a character stored in flash. By using this wrapper type we can
// auto-detect at compile time if a call to a string function is using
// a flash-stored string or not
typedef struct {
char c;
} prog_char_t;
#include <stdint.h> #include <stdint.h>
#include "include/menu.h" /// simple menu subsystem #include "include/menu.h" /// simple menu subsystem
#include "c++.h" // c++ additions #include "c++.h" // c++ additions
@ -72,8 +80,25 @@
# undef PROGMEM # undef PROGMEM
# define PROGMEM __attribute__(( section(".progmem.data") )) # define PROGMEM __attribute__(( section(".progmem.data") ))
# undef PSTR # undef PSTR
# define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) # define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); \
(prog_char_t *)&__c[0];}))
#endif #endif
static inline int strcasecmp_P(const char *str1, const prog_char_t *pstr)
{
return strcasecmp_P(str1, (const prog_char *)pstr);
}
static inline int strcmp_P(const char *str1, const prog_char_t *pstr)
{
return strcmp_P(str1, (const prog_char *)pstr);
}
static inline size_t strlcat_P(char *buffer, const prog_char_t *pstr, size_t buffer_size)
{
return strlcat_P(buffer, (const prog_char *)pstr, buffer_size);
}
//@} //@}

View File

@ -37,7 +37,7 @@ uint16_t AP_Var::_bytes_in_use;
// Constructor for standalone variables // Constructor for standalone variables
// //
AP_Var::AP_Var(Key key, const prog_char *name, Flags flags) : AP_Var::AP_Var(Key key, const prog_char_t *name, Flags flags) :
_group(NULL), _group(NULL),
_key(key | k_key_not_located), _key(key | k_key_not_located),
_name(name), _name(name),
@ -54,7 +54,7 @@ AP_Var::AP_Var(Key key, const prog_char *name, Flags flags) :
// Constructor for variables in a group // Constructor for variables in a group
// //
AP_Var::AP_Var(AP_Var_group *group, Key index, const prog_char *name, Flags flags) : AP_Var::AP_Var(AP_Var_group *group, Key index, const prog_char_t *name, Flags flags) :
_group(group), _group(group),
_key(index), _key(index),
_name(name), _name(name),

View File

@ -161,7 +161,7 @@ public:
/// @param name An optional name by which the variable may be known. /// @param name An optional name by which the variable may be known.
/// @param flags Optional flags which control how the variable behaves. /// @param flags Optional flags which control how the variable behaves.
/// ///
AP_Var(Key key = k_key_none, const prog_char *name = NULL, Flags flags = k_flags_none); AP_Var(Key key = k_key_none, const prog_char_t *name = NULL, Flags flags = k_flags_none);
/// Constructor for variable belonging to a group /// Constructor for variable belonging to a group
/// ///
@ -170,7 +170,7 @@ public:
/// @param name An optional name by which the variable may be known. /// @param name An optional name by which the variable may be known.
/// @param flags Optional flags which control how the variable behaves. /// @param flags Optional flags which control how the variable behaves.
/// ///
AP_Var(AP_Var_group *group, Key index, const prog_char *name, Flags flags = k_flags_none); AP_Var(AP_Var_group *group, Key index, const prog_char_t *name, Flags flags = k_flags_none);
/// Destructor /// Destructor
/// ///
@ -361,7 +361,7 @@ private:
AP_Var_group *_group; ///< Group that the variable may be a member of AP_Var_group *_group; ///< Group that the variable may be a member of
AP_Var *_link; ///< linked list pointer to next variable AP_Var *_link; ///< linked list pointer to next variable
Key _key; ///< Storage key; see the discussion of Key above. Key _key; ///< Storage key; see the discussion of Key above.
const prog_char *_name; ///< name known to external agents (GCS, etc.) const prog_char_t *_name; ///< name known to external agents (GCS, etc.)
uint8_t _flags; ///< flag bits uint8_t _flags; ///< flag bits
// static state used by ::lookup // static state used by ::lookup
@ -415,7 +415,7 @@ public:
/// @param key Storage key for the group. /// @param key Storage key for the group.
/// @param name An optional name prefix for members of the group. /// @param name An optional name prefix for members of the group.
/// ///
AP_Var_group(Key key = k_key_none, const prog_char *name = NULL, Flags flags = k_flags_none) : AP_Var_group(Key key = k_key_none, const prog_char_t *name = NULL, Flags flags = k_flags_none) :
AP_Var(key, name, flags | k_flag_is_group) AP_Var(key, name, flags | k_flag_is_group)
{ {
_bytes_in_use += sizeof(*this); _bytes_in_use += sizeof(*this);
@ -483,7 +483,7 @@ public:
/// ///
AP_VarT<T> (const T initial_value = 0, AP_VarT<T> (const T initial_value = 0,
Key key = k_key_none, Key key = k_key_none,
const prog_char *name = NULL, const prog_char_t *name = NULL,
Flags flags = k_flags_none) : Flags flags = k_flags_none) :
AP_Var(key, name, flags), AP_Var(key, name, flags),
_value(initial_value) _value(initial_value)
@ -506,7 +506,7 @@ public:
AP_VarT<T> (AP_Var_group *group, // XXX maybe make this a ref? AP_VarT<T> (AP_Var_group *group, // XXX maybe make this a ref?
Key index, Key index,
T initial_value, T initial_value,
const prog_char *name = NULL, const prog_char_t *name = NULL,
Flags flags = k_flags_none) : Flags flags = k_flags_none) :
AP_Var(group, index, name, flags), AP_Var(group, index, name, flags),
_value(initial_value) _value(initial_value)
@ -616,7 +616,7 @@ public:
/// @param flags Optional flags that may affect the behavior of the variable. /// @param flags Optional flags that may affect the behavior of the variable.
/// ///
AP_VarS<T> (Key key = k_key_none, AP_VarS<T> (Key key = k_key_none,
const prog_char *name = NULL, const prog_char_t *name = NULL,
Flags flags = k_flags_none) : Flags flags = k_flags_none) :
AP_Var(key, name, flags) AP_Var(key, name, flags)
{ {
@ -636,7 +636,7 @@ public:
/// ///
AP_VarS<T> (AP_Var_group *group, // XXX maybe make this a ref? AP_VarS<T> (AP_Var_group *group, // XXX maybe make this a ref?
Key index, Key index,
const prog_char *name = NULL, const prog_char_t *name = NULL,
Flags flags = k_flags_none) : Flags flags = k_flags_none) :
AP_Var(group, index, name, flags) AP_Var(group, index, name, flags)
{ {
@ -726,7 +726,7 @@ public:
/// @param flags Optional flags that may affect the behavior of the variable. /// @param flags Optional flags that may affect the behavior of the variable.
/// ///
AP_VarA<T,N> (Key key = k_key_none, AP_VarA<T,N> (Key key = k_key_none,
const prog_char *name = NULL, const prog_char_t *name = NULL,
Flags flags = k_flags_none) : Flags flags = k_flags_none) :
AP_Var(key, name, flags) AP_Var(key, name, flags)
{ {
@ -746,7 +746,7 @@ public:
/// ///
AP_VarA<T,N> (AP_Var_group *group, // XXX maybe make this a ref? AP_VarA<T,N> (AP_Var_group *group, // XXX maybe make this a ref?
Key index, Key index,
const prog_char *name = NULL, const prog_char_t *name = NULL,
Flags flags = k_flags_none) : Flags flags = k_flags_none) :
AP_Var(group, index, name, flags) AP_Var(group, index, name, flags)
{ {
@ -841,7 +841,7 @@ public:
/// ///
AP_Float16(float initial_value = 0, AP_Float16(float initial_value = 0,
Key key = k_key_none, Key key = k_key_none,
const prog_char *name = NULL, const prog_char_t *name = NULL,
Flags flags = k_flags_none) : Flags flags = k_flags_none) :
AP_Float(initial_value, key, name, flags) AP_Float(initial_value, key, name, flags)
{ {
@ -851,7 +851,7 @@ public:
AP_Float16(AP_Var_group *group, AP_Float16(AP_Var_group *group,
Key index, Key index,
float initial_value = 0, float initial_value = 0,
const prog_char *name = NULL, const prog_char_t *name = NULL,
Flags flags = k_flags_none) : Flags flags = k_flags_none) :
AP_Float(group, index, initial_value, name, flags) AP_Float(group, index, initial_value, name, flags)
{ {

View File

@ -186,9 +186,9 @@ AP_GPS_Auto::_detect(void)
if (0 == tries) { if (0 == tries) {
Serial.print('*'); Serial.print('*');
// use the FastSerial port handle so that we can use PROGMEM strings // use the FastSerial port handle so that we can use PROGMEM strings
_fs->println_P(_mtk_set_binary); _fs->println_P((const prog_char_t *)_mtk_set_binary);
_fs->println_P(_ublox_set_binary); _fs->println_P((const prog_char_t *)_ublox_set_binary);
_fs->println_P(_sirf_set_binary); _fs->println_P((const prog_char_t *)_sirf_set_binary);
// give the GPS time to react to the settings // give the GPS time to react to the settings
delay(100); delay(100);

View File

@ -109,13 +109,13 @@ void AP_GPS_NMEA::init(void)
BetterStream *bs = (BetterStream *)_port; BetterStream *bs = (BetterStream *)_port;
// send the SiRF init strings // send the SiRF init strings
bs->print_P(_SiRF_init_string); bs->print_P((const prog_char_t *)_SiRF_init_string);
// send the MediaTek init strings // send the MediaTek init strings
bs->print_P(_MTK_init_string); bs->print_P((const prog_char_t *)_MTK_init_string);
// send the ublox init strings // send the ublox init strings
bs->print_P(_ublox_init_string); bs->print_P((const prog_char_t *)_ublox_init_string);
} }
bool AP_GPS_NMEA::read(void) bool AP_GPS_NMEA::read(void)

View File

@ -17,16 +17,16 @@
// Stream extensions//////////////////////////////////////////////////////////// // Stream extensions////////////////////////////////////////////////////////////
void void
BetterStream::print_P(const prog_char *s) BetterStream::print_P(const prog_char_t *s)
{ {
char c; char c;
while ('\0' != (c = pgm_read_byte(s++))) while ('\0' != (c = pgm_read_byte((const prog_char *)s++)))
write(c); write(c);
} }
void void
BetterStream::println_P(const char *s) BetterStream::println_P(const prog_char_t *s)
{ {
print_P(s); print_P(s);
println(); println();
@ -43,7 +43,7 @@ BetterStream::printf(const char *fmt, ...)
} }
void void
BetterStream::printf_P(const char *fmt, ...) BetterStream::_printf_P(const prog_char *fmt, ...)
{ {
va_list ap; va_list ap;

View File

@ -13,6 +13,7 @@
#include <Stream.h> #include <Stream.h>
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
#include <AP_Common.h>
class BetterStream : public Stream { class BetterStream : public Stream {
public: public:
@ -20,13 +21,15 @@ public:
} }
// Stream extensions // Stream extensions
void print_P(const char *); void print_P(const prog_char_t *);
void println_P(const char *); void println_P(const prog_char_t *);
void printf(const char *, ...) void printf(const char *, ...)
__attribute__ ((format(__printf__, 2, 3))); __attribute__ ((format(__printf__, 2, 3)));
void printf_P(const char *, ...) void _printf_P(const prog_char *, ...);
__attribute__ ((format(__printf__, 2, 3))); __attribute__ ((format(__printf__, 2, 3)));
#define printf_P(fmt, ...) _printf_P((const prog_char *)fmt, ## __VA_ARGS__)
private: private:
void _vprintf(unsigned char, const char *, va_list) void _vprintf(unsigned char, const char *, va_list)
__attribute__ ((format(__printf__, 3, 0))); __attribute__ ((format(__printf__, 3, 0)));

View File

@ -246,7 +246,7 @@ BetterStream::_vprintf (unsigned char in_progmem, const char *fmt, va_list ap)
p = PSTR("inf"); p = PSTR("inf");
if (vtype & FTOA_NAN) if (vtype & FTOA_NAN)
p = PSTR("nan"); p = PSTR("nan");
while ( (ndigs = pgm_read_byte(p)) != 0) { while ( (ndigs = pgm_read_byte((const prog_char *)p)) != 0) {
if (flags & FL_FLTUPP) if (flags & FL_FLTUPP)
ndigs += 'I' - 'i'; ndigs += 'I' - 'i';
write(ndigs); write(ndigs);

View File

@ -29,7 +29,7 @@ public:
/// @param initial_imax Initial value for the imax term.4 /// @param initial_imax Initial value for the imax term.4
/// ///
PID(AP_Var::Key key, PID(AP_Var::Key key,
const prog_char *name, const prog_char_t *name,
const float &initial_p = 0.0, const float &initial_p = 0.0,
const float &initial_i = 0.0, const float &initial_i = 0.0,
const float &initial_d = 0.0, const float &initial_d = 0.0,
@ -55,7 +55,7 @@ public:
/// @param initial_d Initial value for the D term. /// @param initial_d Initial value for the D term.
/// @param initial_imax Initial value for the imax term.4 /// @param initial_imax Initial value for the imax term.4
/// ///
PID(const prog_char *name, PID(const prog_char_t *name,
const float &initial_p = 0.0, const float &initial_p = 0.0,
const float &initial_i = 0.0, const float &initial_i = 0.0,
const float &initial_d = 0.0, const float &initial_d = 0.0,

View File

@ -18,7 +18,7 @@ class RC_Channel{
/// @param key EEPROM storage key for the channel trim parameters. /// @param key EEPROM storage key for the channel trim parameters.
/// @param name Optional name for the group. /// @param name Optional name for the group.
/// ///
RC_Channel(AP_Var::Key key, const prog_char *name) : RC_Channel(AP_Var::Key key, const prog_char_t *name) :
_group(key, name), _group(key, name),
radio_min (&_group, 0, 1500, name ? PSTR("MIN") : 0), // suppress name if group has no name radio_min (&_group, 0, 1500, name ? PSTR("MIN") : 0), // suppress name if group has no name
radio_trim(&_group, 1, 1500, name ? PSTR("TRIM") : 0), radio_trim(&_group, 1, 1500, name ? PSTR("TRIM") : 0),