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.

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.