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
// the terms of the GNU Lesser General Public License as published by the
@ -21,6 +21,14 @@
#undef round
#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 "include/menu.h" /// simple menu subsystem
#include "c++.h" // c++ additions
@ -72,8 +80,25 @@
# undef PROGMEM
# define PROGMEM __attribute__(( section(".progmem.data") ))
# 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
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
//
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),
_key(key | k_key_not_located),
_name(name),
@ -54,7 +54,7 @@ AP_Var::AP_Var(Key key, const prog_char *name, Flags flags) :
// 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),
_key(index),
_name(name),

View File

@ -161,7 +161,7 @@ public:
/// @param name An optional name by which the variable may be known.
/// @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
///
@ -170,7 +170,7 @@ public:
/// @param name An optional name by which the variable may be known.
/// @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
///
@ -361,7 +361,7 @@ private:
AP_Var_group *_group; ///< Group that the variable may be a member of
AP_Var *_link; ///< linked list pointer to next variable
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
// static state used by ::lookup
@ -415,7 +415,7 @@ public:
/// @param key Storage key for 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)
{
_bytes_in_use += sizeof(*this);
@ -483,7 +483,7 @@ public:
///
AP_VarT<T> (const T initial_value = 0,
Key key = k_key_none,
const prog_char *name = NULL,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(key, name, flags),
_value(initial_value)
@ -506,7 +506,7 @@ public:
AP_VarT<T> (AP_Var_group *group, // XXX maybe make this a ref?
Key index,
T initial_value,
const prog_char *name = NULL,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(group, index, name, flags),
_value(initial_value)
@ -616,7 +616,7 @@ public:
/// @param flags Optional flags that may affect the behavior of the variable.
///
AP_VarS<T> (Key key = k_key_none,
const prog_char *name = NULL,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(key, name, flags)
{
@ -636,7 +636,7 @@ public:
///
AP_VarS<T> (AP_Var_group *group, // XXX maybe make this a ref?
Key index,
const prog_char *name = NULL,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(group, index, name, flags)
{
@ -726,7 +726,7 @@ public:
/// @param flags Optional flags that may affect the behavior of the variable.
///
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) :
AP_Var(key, name, flags)
{
@ -746,7 +746,7 @@ public:
///
AP_VarA<T,N> (AP_Var_group *group, // XXX maybe make this a ref?
Key index,
const prog_char *name = NULL,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(group, index, name, flags)
{
@ -841,7 +841,7 @@ public:
///
AP_Float16(float initial_value = 0,
Key key = k_key_none,
const prog_char *name = NULL,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Float(initial_value, key, name, flags)
{
@ -851,7 +851,7 @@ public:
AP_Float16(AP_Var_group *group,
Key index,
float initial_value = 0,
const prog_char *name = NULL,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Float(group, index, initial_value, name, flags)
{

View File

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

View File

@ -109,13 +109,13 @@ void AP_GPS_NMEA::init(void)
BetterStream *bs = (BetterStream *)_port;
// 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
bs->print_P(_MTK_init_string);
bs->print_P((const prog_char_t *)_MTK_init_string);
// 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)

View File

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

View File

@ -13,6 +13,7 @@
#include <Stream.h>
#include <avr/pgmspace.h>
#include <AP_Common.h>
class BetterStream : public Stream {
public:
@ -20,13 +21,15 @@ public:
}
// Stream extensions
void print_P(const char *);
void println_P(const char *);
void print_P(const prog_char_t *);
void println_P(const prog_char_t *);
void printf(const char *, ...)
__attribute__ ((format(__printf__, 2, 3)));
void printf_P(const char *, ...)
void _printf_P(const prog_char *, ...);
__attribute__ ((format(__printf__, 2, 3)));
#define printf_P(fmt, ...) _printf_P((const prog_char *)fmt, ## __VA_ARGS__)
private:
void _vprintf(unsigned char, const char *, va_list)
__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");
if (vtype & FTOA_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)
ndigs += 'I' - 'i';
write(ndigs);

View File

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