Search code examples
c++cumlprecompilerhapsody

How can compile-time conditions be represented in UML activity diagrams?


I'm working with a legacy C code which I need to document in UML. There's no immediate requirement to use those UML diagrams for synthesis, but there is a desire to go in that direction in the future.

Now, the code is riddled with features which can be enabled or disabled at compile time:

#if(FEATURE_X == ON)
    deal_with_x();
#endif

Since there's no way to distinguish between compile-time and run-time conditions in UML (is there?), I end up using the same decision block for both, which means my diagrams really represent the following code:

if(FEATURE_X == ON) {
    deal_with_x();
}

While I expect the compiler to eliminate the call when feature X is disabled, this is not quite the same code for at least two reasons:

  • deal_with_x() has to be defined even if feature X is disabled
  • static code analysis will complain about dead code

What is the right way to deal with the situation? Is there a UML feature I'm not aware of that could help? Or should I create separate activity diagrams for different configurations (quite a work)? Or should I rely on the compiler to eliminate unnecessary calls and avoid using precompiler directives altogether?

While my question is about C code and precompiler directives, I can see the same problem can arise with C++ templates, especially if static if gets introduced in the language.


Solution

  • Simply go for tagged values to describe that.

    This has nothing to do with any activity diagram. This is a pure static deployment thing. So you may create components which use different tagged values for different compilation.

    Compile-time conditions tell you how to generate your code. So this will go to some deployment part of your model. The activity diagram refer to behavior of your system. In case you have a target component which is compiled the one or other way and you show its different usages in activity diagrams you can signal this in various ways. One is a naming convention which is described somewhere else in the model or an accompanying documentation. Another way is to create requirements which state to create a common source and link those requirements to the activities and the later components.

    As a (personal) side note: usage (esp. over-usage) of compile time options makes your code hard to read up to unreadable. Indeed, each use of a compile time option will make the same source some completely different thing. So rather to start with "I want to have the same source for this function and therefore tend to use compiler-flags" go the other way. Concentrate on function and when it comes to deployment then eventually think of "optimization" towards using compiler flags. So actually, leave thought about it completely out of activity diagrams.