-
Notifications
You must be signed in to change notification settings - Fork 28
Architecture: Value classes
Our value classes all follow a similar pattern of common methods:
- A
registerMetadata
method to register the class with the metatype system. - Stringification methods to generate a string representation of an object.
- Comparison operators for comparing two objects of the same class.
- An overload of
qHash
to generate a hash of an object. - Methods to marshal and unmarshal an object as a DBus argument.
- Methods to serialize to and from a JSON structure.
- A method to get the icon representing the object.
- Methods to access the properties of the object by an index.
The most common case, in which the methods are implemented using the Meta classes system, is supported by deriving the class from CValueObject
, which is a convenience class template which derives from all the most commonly used mixin classes, using multiple inheritance.
Any of our value objects can be stored in a CVariant
, and the canonical methods of the stored object can be accessed through the methods of CVariant
. This is facilitated by a meta type system built on top of Qt's meta type system, using BlackMisc::registerMetaValueType()
and duck typing. This allows value objects to be treated polymorphically while retaining value semantics and without introducing virtual methods in the public API. It is not necessary for all of the canonical methods to be present in an object stored in a CVariant
; it is a runtime error to attempt to use a method of a CVariant
storing an object which doesn't support that method.
Sometimes it doesn't make sense for a particular value class to implement one or more of the canonical methods. In these cases, instead of deriving from CValueObject
, a value class can derive directly from the specific mixin classes which it needs. This allows for extensive code reuse through inheritance without violating the Liskov
substitution and without coupling together unrelated responsibilities. All of the mixin classes, as well as CValueObject
, use the CRTP pattern.
Some of the mixins require their template argument type to satisfy particular concepts. This is documented in their Doxygen class documentation. The concepts required when deriving from CValueObject
are the union of all the required concepts of the mixins which CValueObject
uses.
Occasionally, we have value classes which represent specializations of other value classes, and therefore derive from those other value classes. For example, CHeading
derives from CAngle
, and CAltitude
derives from CLength
. In these cases, there is a base class which derives from certain mixins, and a derived class which derives from the base class and some other mixins. A base and a derived class might derive from some of the same mixins. For example, CAngle
derives from Mixin::MetaType<CAngle>
and CHeading
derives from Mixin::MetaType<CHeading>
. In these cases, it is necessary to use a using declaration to disambiguate which methods should be inherited in the derived class.
In this example, the definition of CHeading
has a using Mixin::MetaType<CHeading>::registerMetadata;
to tell the compiler that it wants to inherit the registerMetadata
method of Mixin::MetaType<CHeading>
, not the registerMetadata
method of CAngle
. To reduce duplication of these using
declarations, there are macros provided with the mixin classes to automatically introduce the appropriate declarations into a derived class.
- Home
- API documentation (Doxygen generated)
- Future of swift
- Style and Coding Standard
- Release Checklist
- Build swift
- Run swift as a developer
- Knowledgebase
- External resources
- Open Research Questions
- Aviation
- Programming
- Simulation
- Architecture