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

    Fields

    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