Skip to content

Parameter type safety #29

Description

@ZiGaMi

Following code proposed by @tripping-code:

///////////////////////////////////////////////////////////////////////
// param.h
///////////////////////////////////////////////////////////////////////
#ifndef PARAM_H_
#define PARAM_H_

#include <stdint.h>
#include <stdbool.h>
#include <stdatomic.h>

#include "param_cfg.h"

typedef uint16_t param_id_t;

// Declare all parameter types
// NOTE: _Atomic qualifier on value to make possible atomic access removing the need for explicit locks
//
// with SEGGER IDE when using atomics with locks implement
// SEGGER_RTL_X_atomic_lock()
// SEGGER_RTL_X_atomic_unlock()
// SEGGER_RTL_X_atomic_synchronize()
//
// default implementations in \SEGGER Embedded Studio 8.12a\source\emRun\atomicops.c and \atomic_locking.c
// with header in \SEGGER Embedded Studio 8.12a\include\__SEGGER_RTL.h
//
// with softdevice used we should probably use the softdevice provided enter/exit critical section
//
// if custom locking (not that IRQ gets disabled) is used then param_set/get must not be called from ISR
// otherwise can lead to dead-locks
#define PARAM_INFO_TYPENAME(postfix)    param_ ## postfix ## _info_t
#define PARAM_VAL_TYPENAME(postfix)     param_ ## postfix ## _val_t
#define PARAM_DECLARE_TYPE(type, postfix)   \
    typedef struct                          \
    {                                       \
        param_id_t id;                      \
        type def;                           \
        type min;                           \
        type max;                           \
    } PARAM_INFO_TYPENAME(postfix);         \
    typedef _Atomic type PARAM_VAL_TYPENAME(type, postfix)

PARAM_DECLARE_TYPE(uint8_t, u8);
PARAM_DECLARE_TYPE(int8_t, i8);
PARAM_DECLARE_TYPE(uint16_t, u16);
PARAM_DECLARE_TYPE(int16_t, i16);
PARAM_DECLARE_TYPE(uint32_t, u32);
PARAM_DECLARE_TYPE(int32_t, i32);
PARAM_DECLARE_TYPE(uint64_t, u64);
PARAM_DECLARE_TYPE(int64_t, i64);
PARAM_DECLARE_TYPE(float32_t, f32);

#define PARAM_ENUM(type, name)  ePARAM_ ## type ## _ ## name
#define PARAM_U8_ENUM(name) PARAM_ENUM(U8, name)
#define PARAM_I8_ENUM(name) PARAM_ENUM(I8, name)
#define PARAM_U16_ENUM(name) PARAM_ENUM(U16, name)
#define PARAM_I16_ENUM(name) PARAM_ENUM(I16, name)
#define PARAM_U32_ENUM(name) PARAM_ENUM(U32, name)
#define PARAM_I32_ENUM(name) PARAM_ENUM(I32, name)
#define PARAM_U64_ENUM(name) PARAM_ENUM(U64, name)
#define PARAM_I64_ENUM(name) PARAM_ENUM(I64, name)
#define PARAM_F32_ENUM(name) PARAM_ENUM(F32, name)
// Define enum for every parameter type separately, so that we can check when calling set/get
// functions if parameter for the specific type exists by checking if enum for that type exists
//
// u8
typedef enum
{
#define PARAM_U8(name, ...) PARAM_U8_ENUM(name),
#define PARAM_I8(name, ...)
#define PARAM_U16(name, ...)
#define PARAM_I16(name, ...)
#define PARAM_U32(name, ...)
#define PARAM_I32(name, ...)
#define PARAM_U64(name, ...)
#define PARAM_I64(name, ...)
#define PARAM_F32(name, ...)
    PARAM_LIST
    PARAM_U8_ENUM(NUM_OF)
#undef PARAM_U8
#undef PARAM_I8
#undef PARAM_U16
#undef PARAM_I16
#undef PARAM_U32
#undef PARAM_I32
#undef PARAM_U64
#undef PARAM_I64
#undef PARAM_F32
} param_u8_t;
// i8
typedef enum
{
#define PARAM_U8(name, ...)
#define PARAM_I8(name, ...) PARAM_I8_ENUM(name),
#define PARAM_U16(name, ...)
#define PARAM_I16(name, ...)
#define PARAM_U32(name, ...)
#define PARAM_I32(name, ...)
#define PARAM_U64(name, ...)
#define PARAM_I64(name, ...)
#define PARAM_F32(name, ...)
    PARAM_LIST
    PARAM_I8_ENUM(NUM_OF)
#undef PARAM_U8
#undef PARAM_I8
#undef PARAM_U16
#undef PARAM_I16
#undef PARAM_U32
#undef PARAM_I32
#undef PARAM_U64
#undef PARAM_I64
#undef PARAM_F32
} param_i8_t;
// u16
typedef enum
{
#define PARAM_U8(name, ...)
#define PARAM_I8(name, ...)
#define PARAM_U16(name, ...) PARAM_U16_ENUM(name),
#define PARAM_I16(name, ...)
#define PARAM_U32(name, ...)
#define PARAM_I32(name, ...)
#define PARAM_U64(name, ...)
#define PARAM_I64(name, ...)
#define PARAM_F32(name, ...)
    PARAM_LIST
    PARAM_U16_ENUM(NUM_OF)
#undef PARAM_U8
#undef PARAM_I8
#undef PARAM_U16
#undef PARAM_I16
#undef PARAM_U32
#undef PARAM_I32
#undef PARAM_U64
#undef PARAM_I64
#undef PARAM_F32
} param_u16_t;
// i16
typedef enum
{
#define PARAM_U8(name, ...)
#define PARAM_I8(name, ...)
#define PARAM_U16(name, ...)
#define PARAM_I16(name, ...) PARAM_I16_ENUM(name),
#define PARAM_U32(name, ...)
#define PARAM_I32(name, ...)
#define PARAM_U64(name, ...)
#define PARAM_I64(name, ...)
#define PARAM_F32(name, ...)
    PARAM_LIST
    PARAM_I16_ENUM(NUM_OF)
#undef PARAM_U8
#undef PARAM_I8
#undef PARAM_U16
#undef PARAM_I16
#undef PARAM_U32
#undef PARAM_I32
#undef PARAM_U64
#undef PARAM_I64
#undef PARAM_F32
} param_i16_t;
// u32
typedef enum
{
#define PARAM_U8(name, ...)
#define PARAM_I8(name, ...)
#define PARAM_U16(name, ...)
#define PARAM_I16(name, ...)
#define PARAM_U32(name, ...) PARAM_U32_ENUM(name),
#define PARAM_I32(name, ...)
#define PARAM_U64(name, ...)
#define PARAM_I64(name, ...)
#define PARAM_F32(name, ...)
    PARAM_LIST
    PARAM_U32_ENUM(NUM_OF)
#undef PARAM_U8
#undef PARAM_I8
#undef PARAM_U16
#undef PARAM_I16
#undef PARAM_U32
#undef PARAM_I32
#undef PARAM_U64
#undef PARAM_I64
#undef PARAM_F32
} param_u32_t;
// i32
typedef enum
{
#define PARAM_U8(name, ...)
#define PARAM_I8(name, ...)
#define PARAM_U16(name, ...)
#define PARAM_I16(name, ...)
#define PARAM_U32(name, ...)
#define PARAM_I32(name, ...) PARAM_I32_ENUM(name),
#define PARAM_U64(name, ...)
#define PARAM_I64(name, ...)
#define PARAM_F32(name, ...)
    PARAM_LIST
    PARAM_I32_ENUM(NUM_OF)
#undef PARAM_U8
#undef PARAM_I8
#undef PARAM_U16
#undef PARAM_I16
#undef PARAM_U32
#undef PARAM_I32
#undef PARAM_U64
#undef PARAM_I64
#undef PARAM_F32
} param_i32_t;
// u64
typedef enum
{
#define PARAM_U8(name, ...)
#define PARAM_I8(name, ...)
#define PARAM_U16(name, ...)
#define PARAM_I16(name, ...)
#define PARAM_U32(name, ...)
#define PARAM_I32(name, ...)
#define PARAM_U64(name, ...) PARAM_U64_ENUM(name),
#define PARAM_I64(name, ...)
#define PARAM_F32(name, ...)
    PARAM_LIST
    PARAM_U64_ENUM(NUM_OF)
#undef PARAM_U8
#undef PARAM_I8
#undef PARAM_U16
#undef PARAM_I16
#undef PARAM_U32
#undef PARAM_I32
#undef PARAM_U64
#undef PARAM_I64
#undef PARAM_F32
} param_u64_t;
// i64
typedef enum
{
#define PARAM_U8(name, ...)
#define PARAM_I8(name, ...)
#define PARAM_U16(name, ...)
#define PARAM_I16(name, ...)
#define PARAM_U32(name, ...)
#define PARAM_I32(name, ...)
#define PARAM_U64(name, ...)
#define PARAM_I64(name, ...) PARAM_I64_ENUM(name),
#define PARAM_F32(name, ...)
    PARAM_LIST
    PARAM_I64_ENUM(NUM_OF)
#undef PARAM_U8
#undef PARAM_I8
#undef PARAM_U16
#undef PARAM_I16
#undef PARAM_U32
#undef PARAM_I32
#undef PARAM_U64
#undef PARAM_I64
#undef PARAM_F32
} param_i64_t;
// f32
typedef enum
{
#define PARAM_U8(name, ...)
#define PARAM_I8(name, ...)
#define PARAM_U16(name, ...)
#define PARAM_I16(name, ...)
#define PARAM_U32(name, ...)
#define PARAM_I32(name, ...)
#define PARAM_U64(name, ...)
#define PARAM_I64(name, ...)
#define PARAM_F32(name, ...) PARAM_F32_ENUM(name),
    PARAM_LIST
    PARAM_F32_ENUM(NUM_OF)
#undef PARAM_U8
#undef PARAM_I8
#undef PARAM_U16
#undef PARAM_I16
#undef PARAM_U32
#undef PARAM_I32
#undef PARAM_U64
#undef PARAM_I64
#undef PARAM_F32
} param_ft32_t;

// Define extern variables for info and value arrays
// this is only required if we want to have static inline get/set functions API functions in header
// otherwise if not we can have these variables hidden in source file and not exposed in header
//
// info var must be exposed only if we are doing parameter min/max checks here in API functions in header
#define PARAM_INFO_VAR_NAME(postfix)  PARAM_INFO_TYPENAME(postfix) g_param_ ## postfix ## _info_array
extern const PARAM_INFO_VAR_NAME(u8)[];
extern const PARAM_INFO_VAR_NAME(i8)[];
extern const PARAM_INFO_VAR_NAME(u16)[];
extern const PARAM_INFO_VAR_NAME(i16)[];
extern const PARAM_INFO_VAR_NAME(u32)[];
extern const PARAM_INFO_VAR_NAME(i32)[];
extern const PARAM_INFO_VAR_NAME(u64)[];
extern const PARAM_INFO_VAR_NAME(i64)[];
extern const PARAM_INFO_VAR_NAME(f32)[];
#define PARAM_VAL_VAR_NAME(postfix)  PARAM_VAL_TYPENAME(postfix) g_param_ ## postfix ## _val_array
extern PARAM_VAL_VAR_NAME(u8)[];
extern PARAM_VAL_VAR_NAME(i8)[];
extern PARAM_VAL_VAR_NAME(u16)[];
extern PARAM_VAL_VAR_NAME(i16)[];
extern PARAM_VAL_VAR_NAME(u32)[];
extern PARAM_VAL_VAR_NAME(i32)[];
extern PARAM_VAL_VAR_NAME(u64)[];
extern PARAM_VAL_VAR_NAME(i64)[];
extern PARAM_VAL_VAR_NAME(f32)[];
//
//

#define PARAM_CHECK(p_param_info, val)  (((val) >= p_param_info->min) && ((val) <= p_param_info->max))
#define PARAM_SET(p_param_val, p_param_info, val)                       \
    do                                                                  \
    {                                                                   \
        ASSERT(param_check(p_param_info, val));                         \
        atomic_store_explicit(p_param_val, val, memory_order_relaxed);  \
    } while (0)
#define PARAM_GET(p_param_val, val) atomic_load_explicit(p_param_val, memory_order_relaxed)
// Make and use macros/function per type so that:
//   a) user is aware of what type the parameter has so he knows what typed variable to use
//   b) type safety, if type of parameter changes compiler will emit error/warning, for example user uses
//      param_set_u32() but parameter type changed from uint32_t to uint8_t (because ePARA_U32_XXX enum will no longer exist)
//   c) extra type safety, because atomic_store_explicit() and atomic_load_explicit() are compiler builtins
//      they only check that sizes between atomic pointer and value match and not also signdness
#define param_set_u8(param, val)    param_set_u8_internal(PARAM_U8_ENUM(param), val)
#define param_set_i8(param, val)    param_set_i8_internal(PARAM_I8_ENUM(param), val)
#define param_set_u16(param, val)   param_set_u16_internal(PARAM_U16_ENUM(param), val)
#define param_set_i16(param, val)   param_set_i16_internal(PARAM_I16_ENUM(param), val)
#define param_set_u32(param, val)   param_set_u32_internal(PARAM_U32_ENUM(param), val)
#define param_set_i32(param, val)   param_set_i32_internal(PARAM_I32_ENUM(param), val)
#define param_set_u64(param, val)   param_set_u64_internal(PARAM_U64_ENUM(param), val)
#define param_set_i64(param, val)   param_set_i64_internal(PARAM_I64_ENUM(param), val)
#define param_set_f32(param, val)   param_set_f32_internal(PARAM_F32_ENUM(param), val)
//
#define param_get_u8(param)     param_get_u8_internal(PARAM_U8_ENUM(param))
#define param_get_i8(param)     param_get_i8_internal(PARAM_I8_ENUM(param))
#define param_get_u16(param)    param_get_u16_internal(PARAM_U16_ENUM(param))
#define param_get_i16(param)    param_get_i16_internal(PARAM_I16_ENUM(param))
#define param_get_u32(param)    param_get_u32_internal(PARAM_U32_ENUM(param))
#define param_get_i32(param)    param_get_i32_internal(PARAM_I32_ENUM(param))
#define param_get_u64(param)    param_get_u64_internal(PARAM_U64_ENUM(param))
#define param_get_i64(param)    param_get_i64_internal(PARAM_I64_ENUM(param))
#define param_get_f32(param)    param_get_f32_internal(PARAM_F32_ENUM(param))
//
#define param_check_u8(param, val)   param_check_u8_internal(PARAM_U8_ENUM(param), val)
#define param_check_i8(param, val)   param_check_i8_internal(PARAM_I8_ENUM(param), val)
#define param_check_u16(param, val)  param_check_u16_internal(PARAM_U16_ENUM(param), val)
#define param_check_i16(param, val)  param_check_i16_internal(PARAM_I16_ENUM(param), val)
#define param_check_u32(param, val)  param_check_u32_internal(PARAM_U32_ENUM(param), val)
#define param_check_i32(param, val)  param_check_i32_internal(PARAM_I32_ENUM(param), val)
#define param_check_u64(param, val)  param_check_u64_internal(PARAM_U64_ENUM(param), val)
#define param_check_i64(param, val)  param_check_i64_internal(PARAM_I64_ENUM(param), val)
#define param_check_f32(param, val)  param_check_f32_internal(PARAM_F32_ENUM(param), val)
//
static inline void param_set_u8_internal(param_u8_t param, uint8_t val) { PARAM_SET(&PARAM_VAL_VAR_NAME(u8)[param], &PARAM_INFO_VAR_NAME(u8)[param], val); }
static inline void param_set_i8_internal(param_i8_t param, int8_t val) { PARAM_SET(&PARAM_VAL_VAR_NAME(i8)[param], &PARAM_INFO_VAR_NAME(i8)[param], val); }
static inline void param_set_u16_internal(param_u16_t param, uint16_t val) { PARAM_SET(&PARAM_VAL_VAR_NAME(u16)[param], &PARAM_INFO_VAR_NAME(u16)[param], val); }
static inline void param_set_i16_internal(param_i16_t param, int16_t val) { PARAM_SET(&PARAM_VAL_VAR_NAME(i16)[param], &PARAM_INFO_VAR_NAME(i16)[param], val); }
static inline void param_set_u32_internal(param_u32_t param, uint32_t val) { PARAM_SET(&PARAM_VAL_VAR_NAME(u32)[param], &PARAM_INFO_VAR_NAME(u32)[param], val); }
static inline void param_set_i32_internal(param_i32_t param, int32_t val) { PARAM_SET(&PARAM_VAL_VAR_NAME(i32)[param], &PARAM_INFO_VAR_NAME(i32)[param], val); }
static inline void param_set_u64_internal(param_u64_t param, uint64_t val) { PARAM_SET(&PARAM_VAL_VAR_NAME(u64)[param], &PARAM_INFO_VAR_NAME(u64)[param], val); }
static inline void param_set_i64_internal(param_i64_t param, int64_t val) { PARAM_SET(&PARAM_VAL_VAR_NAME(i64)[param], &PARAM_INFO_VAR_NAME(i64)[param], val); }
static inline void param_set_f32_internal(param_f32_t param, float32_t val) { PARAM_SET(&PARAM_VAL_VAR_NAME(f32)[param], &PARAM_INFO_VAR_NAME(f32)[param], val); }
//
static inline uint8_t param_get_u8_internal(param_u8_t param) { return PARAM_GET(&PARAM_VAL_VAR_NAME(u8)[param]); }
static inline int8_t param_get_i8_internal(param_i8_t param) { return PARAM_GET(&PARAM_VAL_VAR_NAME(i8)[param]); }
static inline uint16_t param_get_u16_internal(param_u16_t param) { return PARAM_GET(&PARAM_VAL_VAR_NAME(u16)[param]); }
static inline int16_t param_get_i16_internal(param_i16_t param) { return PARAM_GET(&PARAM_VAL_VAR_NAME(i16)[param]); }
static inline uint32_t param_get_u32_internal(param_u32_t param) { return PARAM_GET(&PARAM_VAL_VAR_NAME(u32)[param]); }
static inline int32_t param_get_i32_internal(param_i32_t param) { return PARAM_GET(&PARAM_VAL_VAR_NAME(i32)[param]); }
static inline uint64_t param_get_u64_internal(param_u64_t param) { return PARAM_GET(&PARAM_VAL_VAR_NAME(u64)[param]); }
static inline int64_t param_get_i64_internal(param_i64_t param) { return PARAM_GET(&PARAM_VAL_VAR_NAME(i64)[param]); }
static inline float32_t param_get_f32_internal(param_ft32_t param) { return PARAM_GET(&PARAM_VAL_VAR_NAME(f32)[param]); }
//
static inline bool param_check_u8_internal(param_u8_t param, uint8_t val) { return PARAM_CHECK(&PARAM_INFO_VAR_NAME(u8)[param], val); }
static inline bool param_check_i8_internal(param_i8_t param, int8_t val) { return PARAM_CHECK(&PARAM_INFO_VAR_NAME(i8)[param], val); }
static inline bool param_check_u16_internal(param_u16_t param, uint16_t val) { return PARAM_CHECK(&PARAM_INFO_VAR_NAME(u16)[param], val); }
static inline bool param_check_i16_internal(param_i16_t param, int16_t val) { return PARAM_CHECK(&PARAM_INFO_VAR_NAME(i16)[param], val); }
static inline bool param_check_u32_internal(param_u32_t param, uint32_t val) { return PARAM_CHECK(&PARAM_INFO_VAR_NAME(u32)[param], val); }
static inline bool param_check_i32_internal(param_i32_t param, int32_t val) { return PARAM_CHECK(&PARAM_INFO_VAR_NAME(i32)[param], val); }
static inline bool param_check_u64_internal(param_u64_t param, uint64_t val) { return PARAM_CHECK(&PARAM_INFO_VAR_NAME(u64)[param], val); }
static inline bool param_check_i64_internal(param_i64_t param, int64_t val) { return PARAM_CHECK(&PARAM_INFO_VAR_NAME(i64)[param], val); }
static inline bool param_check_f32_internal(param_f32_t param, float32_t val) { return PARAM_CHECK(&PARAM_INFO_VAR_NAME(f32)[param], val); }

#endif // PARAM_H_



///////////////////////////////////////////////////////////////////////
// param_cfg.h
///////////////////////////////////////////////////////////////////////
#ifndef PARAM_CFG_H_
#define PARAM_CFG_H_

// NOTE: type is specified with macro name
#define PARAM_LIST                                  \
    PARAM_U8(MY_PARAM_A, 100, 0, 100, 12)           \
    PARAM_I32(MY_PARAM_B, 200, -123, 123, 0)        \
    PARAM_U16(MY_PARAM_C, 300, 1000, 2000, 1000)    \
    PARAM_F32(MY_PARAM_D, 400, -1.f, 5.f, 3.3f)

#endif // PARAM_CFG_H_



///////////////////////////////////////////////////////////////////////
// param.c
///////////////////////////////////////////////////////////////////////
#include "param.h"

// Define all parameters in a single enum so that we can detect duplicates
// for example if same named parameter was declared as U32 and U8 parameter
// this will trigger compiler error
// This enum is not part of API and is not used for anything other then detecting
// parameter duplicates
#define PARAM_ANY_ENUM(name)    PARAM_ENUM(ANY, name)
typedef enum
{
#define PARAM_U8(name, ...)  PARAM_ANY_ENUM(name),
#define PARAM_I8(name, ...)  PARAM_ANY_ENUM(name),
#define PARAM_U16(name, ...) PARAM_ANY_ENUM(name),
#define PARAM_I16(name, ...) PARAM_ANY_ENUM(name),
#define PARAM_U32(name, ...) PARAM_ANY_ENUM(name),
#define PARAM_I32(name, ...) PARAM_ANY_ENUM(name),
#define PARAM_U64(name, ...) PARAM_ANY_ENUM(name),
#define PARAM_I64(name, ...) PARAM_ANY_ENUM(name),
#define PARAM_F32(name, ...) PARAM_ANY_ENUM(name),
    PARAM_LIST
#undef PARAM_U8
#undef PARAM_I8
#undef PARAM_U16
#undef PARAM_I16
#undef PARAM_U32
#undef PARAM_I32
#undef PARAM_U64
#undef PARAM_I64
#undef PARAM_F32
} param_any_t;

// Define parameter value and info arrays for all types
//
// if we make these variables globals then we can have static inline API functions in header file
// otherwise if these variables are static then the API functions must be extern
//
// u8
const PARAM_INFO_VAR_NAME(u8)[] =
{
#define PARAM_U8(name, id_, min_, max_, def_)   [PARAM_U8_ENUM(name)] = {.id = id_, .min = min_, .max = max_, .def = def_},
    PARAM_LIST
#undef PARAM_U8
};
PARAM_VAL_VAR_NAME(u8)[] =
{
#define PARAM_U8(name, id_, min_, max_, def_)   [PARAM_U8_ENUM(name)] = def_,
    PARAM_LIST
#undef PARAM_U8
};
// i8
const PARAM_INFO_VAR_NAME(i8)[] =
{
#define PARAM_I8(name, id_, min_, max_, def_)   [PARAM_I8_ENUM(name)] = {.id = id_, .min = min_, .max = max_, .def = def_},
    PARAM_LIST
#undef PARAM_I8
};
PARAM_VAL_VAR_NAME(i8)[] =
{
#define PARAM_I8(name, id_, min_, max_, def_)   [PARAM_I8_ENUM(name)] = def_,
    PARAM_LIST
#undef PARAM_I8
};
// u16
const PARAM_INFO_VAR_NAME(u16)[] =
{
#define PARAM_U16(name, id_, min_, max_, def_)   [PARAM_U16_ENUM(name)] = {.id = id_, .min = min_, .max = max_, .def = def_},
    PARAM_LIST
#undef PARAM_U16
};
PARAM_VAL_VAR_NAME(u16)[] =
{
#define PARAM_U16(name, id_, min_, max_, def_)   [PARAM_U16_ENUM(name)] = def_,
    PARAM_LIST
#undef PARAM_U16
};
// i16
const PARAM_INFO_VAR_NAME(i16)[] =
{
#define PARAM_I16(name, id_, min_, max_, def_)   [PARAM_I16_ENUM(name)] = {.id = id_, .min = min_, .max = max_, .def = def_},
    PARAM_LIST
#undef PARAM_I16
};
PARAM_VAL_VAR_NAME(i16)[] =
{
#define PARAM_I16(name, id_, min_, max_, def_)   [PARAM_I16_ENUM(name)] = def_,
    PARAM_LIST
#undef PARAM_I16
};
// u32
const PARAM_INFO_VAR_NAME(u32)[] =
{
#define PARAM_U32(name, id_, min_, max_, def_)   [PARAM_U32_ENUM(name)] = {.id = id_, .min = min_, .max = max_, .def = def_},
    PARAM_LIST
#undef PARAM_U32
};
PARAM_VAL_VAR_NAME(u32)[] =
{
#define PARAM_U32(name, id_, min_, max_, def_)   [PARAM_U32_ENUM(name)] = def_,
    PARAM_LIST
#undef PARAM_U32
};
// i32
const PARAM_INFO_VAR_NAME(i32)[] =
{
#define PARAM_I32(name, id_, min_, max_, def_)   [PARAM_I32_ENUM(name)] = {.id = id_, .min = min_, .max = max_, .def = def_},
    PARAM_LIST
#undef PARAM_I32
};
PARAM_VAL_VAR_NAME(i32)[] =
{
#define PARAM_I32(name, id_, min_, max_, def_)   [PARAM_I32_ENUM(name)] = def_,
    PARAM_LIST
#undef PARAM_I32
};
// u64
const PARAM_INFO_VAR_NAME(u64)[] =
{
#define PARAM_U64(name, id_, min_, max_, def_)   [PARAM_U64_ENUM(name)] = {.id = id_, .min = min_, .max = max_, .def = def_},
    PARAM_LIST
#undef PARAM_U64
};
PARAM_VAL_VAR_NAME(u64)[] =
{
#define PARAM_U64(name, id_, min_, max_, def_)   [PARAM_U64_ENUM(name)] = def_,
    PARAM_LIST
#undef PARAM_U64
};
// i64
const PARAM_INFO_VAR_NAME(i64)[] =
{
#define PARAM_I64(name, id_, min_, max_, def_)   [PARAM_I64_ENUM(name)] = {.id = id_, .min = min_, .max = max_, .def = def_},
    PARAM_LIST
#undef PARAM_I64
};
PARAM_VAL_VAR_NAME(i64)[] =
{
#define PARAM_I64(name, id_, min_, max_, def_)   [PARAM_I64_ENUM(name)] = def_,
    PARAM_LIST
#undef PARAM_I64
};
// f32
const PARAM_INFO_VAR_NAME(f32)[] =
{
#define PARAM_F32(name, id_, min_, max_, def_)   [PARAM_F32_ENUM(name)] = {.id = id_, .min = min_, .max = max_, .def = def_},
    PARAM_LIST
#undef PARAM_F32
};
PARAM_VAL_VAR_NAME(f32)[] =
{
#define PARAM_F32(name, id_, min_, max_, def_)   [PARAM_F32_ENUM(name)] = def_,
    PARAM_LIST
#undef PARAM_F32
};


///////////////////////////////////////////////////////////////////////
// main.c
///////////////////////////////////////////////////////////////////////
#include "param.h"

void main(void)
{
    // We don't have to add ePARAM_ prefix, instead just use parameter name as defined in param_cfg.h
    //
    param_set_u8(MY_PARAM_A, 30);
    float32_t val = param_get_f32(MY_PARAM_D);

    if (!param_check_i32(MY_PARAM_B, 1000))
    {
        printf("error");
    }
}

//*********************
// Drawbacks:
//   1. messy code with a lot #define #undef due to X macros when declaring enums and arrays
//   2. relies on GCC compiler extension to have 0 length arrays (in case no parameters for some specific type)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions