deCONZ C++ API v2.6.1
Loading...
Searching...
No Matches
deCONZ::ZclAttributeId_t Class Reference

A strong typed ZCL attribute identifier. More...

#include <zcl.h>

Public Member Functions

constexpr ZclAttributeId_t ()
 Default constructor, with value = 0.
 
constexpr ZclAttributeId_t (quint16 id)
 Explicit constructor to init by value.
 
constexpr operator quint16 () const
 Explicit quint16 conversion operator, needs static_cast<quint16>().
 
template<class T >
 ZclAttributeId_t (T)=delete
 Don't allow construction via implicit integer conversion.
 
constexpr bool operator== (ZclAttributeId_t other) const
 Equal operator, requires strong type.
 
constexpr bool operator!= (ZclAttributeId_t other) const
 Not equal operator, requires strong type.
 

Related Symbols

(Note that these are not member symbols.)

DECONZ_DLLSPEC QDataStream & operator<< (QDataStream &ds, ZclAttributeId_t id)
 Writes ZclAttributeId_t as quint16 to the QDataStream.
 
DECONZ_DLLSPEC QDataStream & operator>> (QDataStream &ds, ZclAttributeId_t &id)
 Reades ZclAttributeId_t as quint16 from the QDataStream.
 

Detailed Description

A strong typed ZCL attribute identifier.

See also
Zigbee Strong Types
Since
v2.6.1

This wrapper around quint16 data type as ZCL attribute identifier for code correctness. It helps to prevent common hard to catch programming errors. The class is very strict and enforces a type safe programming style.

Problems

Following issues where observed in the past and are hard to spot and debug.

quint8 at1 = 0x8001; // does compile, at best with a warning
quint32 at2 = 0x0002; // does compile, no warning
stream << at1; // 0x01 instead of 0x8001
stream << at2; // 0x00000001 instead of 0x0001
stream << (quint8)0x0001; // 0x01 instead of 0x0001
stream << (quint16)at1; // at1 is already wrong type, won't be catched here either

The strong typed ZclAttributeId_t catches all those errors at compile time.

Example: Construction

ZclAttributeId_t at0(ONOFF_CLUSTER_ATTRID_ONOFF); // ok, preferred, no magic numbers
ZclAttributeId_t at1(0x0001_atid); // ok, using literal for ZclAttributeId_t
ZclAttributeId_t at2(quint16(0x0000)); // ok
ZclAttributeId_t at3(static_cast<quint16>(0x0001)); // ok
ZclAttributeId_t at4(0x0002); // compile error, no conversion from int
quint16 good1 = 0x0000;
ZclAttributeId_t good2 = 0x0001_atid;
quint8 bad1 = 0x0003;
quint32 bad2 = 0x0004;
ZclAttributeId_t at5(good1); // ok
ZclAttributeId_t at6(good2); // ok
ZclAttributeId_t at7(bad1); // compile error, no conversion from quint8
ZclAttributeId_t at8(bad2); // compile error, no conversion from quint32
A strong typed ZCL attribute identifier.
Definition zcl.h:533

Example: Assignment

ZclAttributeId_t at1(ONOFF_CLUSTER_ATTRID_ONOFF);
ZclAttributeId_t at2(0x0001_atid);
at1 = ZclAttributeId_t(quint16(0x0005)); // ok
at1 = at2; // ok
at1 = 0x0005_atid; // ok
at1 = 0x0005; // compile error
at1 = quint16(0x0005); // compile error due explicit constructor
constexpr ZclAttributeId_t()
Default constructor, with value = 0.
Definition zcl.h:538

Example: Write attribute to stream

//#define ONOFF_CLUSTER_ATTRID_ONOFF quint16(0x0000) // ok, strong typed define
#define ONOFF_CLUSTER_ATTRID_ONOFF 0x0000_atid // ok, and better, using literal for ZclAttributeId_t
QDataStream stream;
ZclAttributeId_t at(ONOFF_CLUSTER_ATTRID_ONOFF);
stream << (quint8) at; // programmer mistake, won't be detected by compiler (don't use C style cast!)
stream << static_cast<quint8>(at); // compile error no valid conversion from ZclAttributeId_t to quint8
stream << at; // ok, preferred, because operator<< for QDataStream
stream << ZclAttributeId_t(ONOFF_CLUSTER_ATTRID_ONOFF); // ok, because operator<< for QDataStream
stream << static_cast<quint16>(at); // ok, static_cast<quint16> allowed

Important: Always use C++ static_cast<> instead C style cast to catch invalid conversions at compile time. Note when using ZclAttributeId_t properly, casts aren't needed at all.

Why use verbose ugly code?

In the following example (1) and (2) do the same and (1) seems easier to reason about at first.

quint16 at1 = 0x0005; // (1)
ZclAttributeId_t at2(quint16(0x0005)); // (2)

at2 looks verbose and ugly and it should be! When using numbers directly, it's a code smell for magic values. Searching for 0x0005 when looking for a certain attribute is not going well.

Better: Use strong typed defines or C++11 enum classes with type quint16 or the C++ literal <number>_atid.

//#define ONOFF_CLUSTER_ATTRID_ONOFF quint16(0x0000) // ok, strong typed define
#define ONOFF_CLUSTER_ATTRID_ONOFF 0x0000_atid // ok, and better, using literal for ZclAttributeId_t
ZclAttributeId_t at1(ONOFF_CLUSTER_ATTRID_ONOFF); // ok
ZclAttributeId_t at1(0x0000_atid); // ok, but prefer defines or enums

Summary

  • No data type errors possible.
  • No casts needed.
  • No magic numbers, ZclAttributeId_t as well as attribute defines are searchable.

The documentation for this class was generated from the following file: