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 subfeatures must be contained. |
|
exactly one subfeatures must be contained. |
|
at least one subfeatures must be contained. |
|
at least |
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.
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 expression.
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
dynamic feature switches. 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.
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 module references, 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.