@@ -1790,6 +1790,42 @@ inline std::enable_if_t<traits::supported && !traits::featured> decode_nohead(
17901790 " ' v=" + std::to_string (code_v)+ " cannot decode v=" + std::to_string (struct_v) +
17911791 " minimal_decoder=" + std::to_string (struct_compat));
17921792}
1793+
1794+ // compile-time checker for struct_v to detect mismatch of declared
1795+ // decoder version with actually implemented blocks like "struct_v < 100".
1796+ // it addresses the common problem of forgetting to bump the version up
1797+ // in decoder's `DECODE_START` (or `DENC_START`) when adding a new
1798+ // schema revision.
1799+ template <__u8 MaxV>
1800+ struct StructVChecker
1801+ {
1802+ struct CheckingWrapper {
1803+ consteval CheckingWrapper (__u8 c) : c(c) {
1804+ consteval_assert (
1805+ c <= MaxV,
1806+ " checking against higher version than declared in DECODE_START" );
1807+ }
1808+ __u8 c;
1809+ };
1810+ // we want the implicit conversion to take place but with lower
1811+ // rank than the CheckingWrapper-conversion during struct_v cmps.
1812+ template <class T =void > operator __u8 () {
1813+ return v;
1814+ }
1815+ // need the wrapper as the operator cannot be consteval; otherwise
1816+ // it couldn't have accessed the non-constexpr `v`.
1817+ auto operator <=>(CheckingWrapper c) {
1818+ return v <=> c.c ;
1819+ }
1820+ auto operator ==(CheckingWrapper c) {
1821+ return v == c.c ;
1822+ }
1823+ auto operator !=(CheckingWrapper c) {
1824+ return v != c.c ;
1825+ }
1826+ __u8 v;
1827+ };
1828+
17931829#define DENC_HELPERS \
17941830 /* bound_encode */ \
17951831 static void _denc_start (size_t & p, \
@@ -1857,39 +1893,39 @@ inline std::enable_if_t<traits::supported && !traits::featured> decode_nohead(
18571893// Due to -2 compatibility rule we cannot bump up compat until U____ release.
18581894// SQUID=19 T____=20 U____=21
18591895
1860- #define DENC_START (v , compat, p ) \
1861- __u8 struct_v = v; \
1896+ #define DENC_START (_v , compat, p ) \
1897+ StructVChecker<_v> struct_v{_v}; \
18621898 __u8 struct_compat = compat; \
18631899 char *_denc_pchar; \
18641900 uint32_t _denc_u32; \
18651901 static_assert (CEPH_RELEASE >= (CEPH_RELEASE_SQUID /* 19*/ + 2 ) || compat == 1 ); \
1866- _denc_start (p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32); \
1902+ _denc_start (p, &struct_v. v , &struct_compat, &_denc_pchar, &_denc_u32); \
18671903 do {
18681904
18691905// For the only type that is with compat 2: unittest.
1870- #define DENC_START_COMPAT_2 (v , compat, p ) \
1871- __u8 struct_v = v; \
1906+ #define DENC_START_COMPAT_2 (_v , compat, p ) \
1907+ StructVChecker<_v> struct_v{_v}; \
18721908 __u8 struct_compat = compat; \
18731909 char *_denc_pchar; \
18741910 uint32_t _denc_u32; \
18751911 static_assert (CEPH_RELEASE >= (CEPH_RELEASE_SQUID /* 19*/ + 2 ) || compat == 2 ); \
1876- _denc_start (p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32); \
1912+ _denc_start (p, &struct_v. v , &struct_compat, &_denc_pchar, &_denc_u32); \
18771913 do {
18781914
18791915// For osd_reqid_t which cannot be upgraded at all.
18801916// We used it to communicate with clients and now we cannot safely upgrade.
1881- #define DENC_START_OSD_REQID (v , compat, p ) \
1882- __u8 struct_v = v; \
1917+ #define DENC_START_OSD_REQID (_v , compat, p ) \
1918+ StructVChecker<_v> struct_v{_v}; \
18831919 __u8 struct_compat = compat; \
18841920 char *_denc_pchar; \
18851921 uint32_t _denc_u32; \
18861922 static_assert (compat == 2 , " osd_reqid_t cannot be upgraded" ); \
1887- _denc_start (p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32); \
1923+ _denc_start (p, &struct_v. v , &struct_compat, &_denc_pchar, &_denc_u32); \
18881924 do {
18891925
18901926#define DENC_FINISH (p ) \
18911927 } while (false ); \
1892- _denc_finish (p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32);
1928+ _denc_finish (p, &struct_v. v , &struct_compat, &_denc_pchar, &_denc_u32);
18931929
18941930
18951931// ----------------------------------------------------------------------
0 commit comments