.. _sec-feature-model: Feature model ============= The feature model defines a set of features and the allowed (or valid) combinations of these features. A ProFeat feature model follows the usual hierarchical structure and may contain additional cross-tree constraints. Feature definitions ------------------- A *feature* is introduced by a ``feature`` block, as shown in the following example:: feature Worker endfeature The *root feature*, i.e., the root of the feature model tree, is defined using the ``root feature`` block:: root feature endfeature .. note:: The root feature definition does not include a name. Decomposition ------------- A feature may have one or more child features (or subfeatures), i.e., the feature is decomposed into subfeatures. A decomposition defines the constraints between a parent feature and its subfeatures. In the following example, the root feature is decomposed into the subfeatures ``Producer``, ``Consumer`` and ``Buffer``:: root feature all of Producer, Consumer, Buffer; endfeature feature Producer endfeature feature Consumer endfeature feature Buffer endfeature .. note:: Each feature referenced in a feature decomposition must be defined using a feature block. A feature may by referenced by multiple other features. In this case multiple instances of the feature are created. This means that a feature which is referenced multiple times is not shared, but copied. The ``all of`` decomposition states that all of the referenced features must be part of the feature combination in case the parent feature is part of the feature combination. In the above example, the root feature is the parent feature. Since the root feature is contained in every feature combination, so are the ``Producer``, ``Consumer`` and ``Buffer`` features. The following table lists all decomposition operators. =================== ====================================================================== Decomposition If the parent feature is contained in the feature combination, then... =================== ====================================================================== ``all of`` all subfeatures must be contained. ``one of`` exactly one subfeatures must be contained. ``some of`` at least one subfeatures must be contained. ``[<n> .. <m>] of`` at least ``n`` and at most ``m`` subfeatures must be contained. =================== ====================================================================== .. note:: The ``all of``, ``one of`` and ``some of`` decompositions are also referred to as ``AND``, ``XOR`` and ``OR`` by some feature model formalisms. A subfeature can be *optional*, which means it may or may not be included in the feature combination. The ``optional`` keyword has a higher priority than the feature decomposition. Consider the following example:: root feature all of Producer, Consumer, optional Buffer; endfeature Here, the ``all of`` decomposition states that all three subfeatures must be included, but since ``Buffer`` is marked optional, there are two allowed feature combinations: one that includes ``Buffer``, and one that does not. .. _sec-multi-features: Multi-features -------------- ProFeat supports *multi-features*, i.e., features that can appear multiple times in a feature combination. Multi-features are defined by specifying the number of instances, as shown in the following example:: feature Consumers some of Consumer[3]; endfeature feature Consumer // ... endfeature In this example, the ``Consumers`` feature contains 3 ``Consumer`` subfeatures. .. note:: The number of instances can be any constant :ref:`expression <sec-expressions>`. The individual feature instances are referenced by indexing. Thus, the subfeatures of ``Consumers`` are ``Consumer[0]``, ``Consumer[1]`` and ``Consumer[2]`` (multi-features are 0-indexed). If the ``optional`` keyword is applied to a multi-feature, then all feature instances are optional, independently from each other. The decomposition operator using a group cardinality (``[<n> .. <m>] of``) counts the number of multi-feature instances and not the multi-feature itself. Consider the following example:: root feature [2..3] of Consumer[3]; endfeature This feature model specifies 4 feature combinations. One where all ``Consumer`` instances are included, and three where two of the ``Consumer`` instances are selected. Aliasing -------- Sometimes a feature should be appear multiple times as a subfeature in the same parent feature, but without making it a multi-feature. However, each subfeature listed in a decomposition must be unique. Therefore, a feature may be renamed upon referencing:: feature Consumers one of Consumer as FirstConsumer, Consumer as SecondConsumer; endfeature Here, the ``Consumer`` feature appears twice as a subfeature. To make the feature instances unique, they are renamed using the ``as`` keyword. Thus, ``Consumers`` has the subfeatures ``FirstConsumer`` and ``SecondConsumer`` that are both instances of the ``Consumer`` feature. .. note:: The ``as`` keyword can also be used for multi-features. Then, the number of subfeatures is specified after the alias name, for instance ``Consumer as FastConsumer[3]``. Qualified names --------------- A reference to a feature instance may not always be unambiguous. Consider the following example:: root feature all of A, B, C[2]; endfeature feature A all of X; endfeature feature B all of X; endfeature feature C all of Y; endfeature feature X endfeature feature Y endfeature In the above example, there are two instances of the ``X`` feature. Thus, a reference to ``X`` is ambiguous. The ambiguity can be resolved by qualifying the feature instance name with its parent feature instance. Using the familiar dot notation, the two ``X`` instances are referenced by ``A.X`` and ``B.X``. Similarly, there are two instances of the ``Y`` feature, namely ``C[0].Y`` and ``C[1].Y``. A *fully qualified name* is anchored on the root feature. For example, the fully qualified name of the second ``X`` instance is ``root.B.X``. Constraints ----------- In addition to the constraints specified by the feature decomposition, cross-tree constraints can be specified. A constraint is a Boolean expression over the features in the feature model. If it evaluates to ``true`` for a given feature combination, then this feature combination is allowed (or valid). Consider the following example:: root feature all of Producer, Consumers, Buffer, Fast; constraint active(Fast) => active(Consumer[0]) & active(Consumer[1]); endfeature feature Consumers some of Consumer[2]; endfeature This feature model specifies that both ``Consumer[0]`` and ``Consumer[1]`` must be contained in the feature combination if ``Fast`` is contained. The ``active`` function returns ``true`` if a given feature is part of the feature combination. .. note:: Constraints can appear in any feature block. There are no restrictions regarding the location of constraints in the feature model. However, it is good practice to specify constraints as local as possible. Constraints must hold even after :ref:`dynamic feature switches <sec-controller>`. To specify constraints that should only hold in the initial state, the ``initial`` keyword is used (``initial constaint ...``). Attributes ---------- ProFeat has support for *feature attributes* (sometimes called numerical features). Feature attributes are part of the feature combination. An attribute is defined within a ``feature`` block:: feature Consumer speed : [0 .. 5]; endfeature In the above example, the ``Consumer`` feature has the attribute ``speed``. An attribute can have any variable type: bounded integer, ``bool`` or an ``array`` type. Attributes can also be constrained using feature constraints, as shown in the following example:: feature Consumers all of Consumer[2]; constraint Consumer[0].speed + Consumer[1].speed < 7; endfeature feature Consumer speed : [0 .. 5]; endfeature The constraint states that the combined ``speed`` of the ``Consumer`` features must be less than 7. .. _sec-parametrization-feature: Parametrization --------------- Feature definitions can be parametrized to facilitate reuse and avoid code duplication. The parameters of a feature are listed after the feature name:: feature Consumer(max_speed) speed : [0 .. max_speed]; endfeature The parameters can be used anywhere in the feature block, including :ref:`module <sec-modules>` references, :ref:`rewards <sec-rewards>`, constraints and attributes (as shown above). Arguments for a parametrized feature are provided when instantiating the feature:: root feature all of Consumer(3) as Slow, Consumer(5) as Fast; endfeature Here, the ``Consumer`` feature is instantiated twice with different argument values. Note that the ``as`` keyword must be used here to given the instances different names.