Main Content

MISRA C:2023 Rule 23.7

A generic selection that is expanded from a macro should evaluate its argument only once

Since R2024a

Description

Rule Definition

A generic selection that is expanded from a macro should evaluate its argument only once.

Rationale

Because the controlling argument of a generic selection is not evaluated, inconsistently evaluating the expressions in the association list can lead to unexpected results. For example, if evaluating the macro argument has a side effect and it is evaluated inconsistently in the association list, then the side effect might not be invoked for certain selections.

If your generic selection is expanded from a macro, evaluate the macro argument in the association list once regardless of which association is selected. For consistent evaluation of the argument, evaluate the argument outside of the generic selection association list.

Polyspace Implementation

Polyspace® reports a violation of this rule if a generic selection is expanded from a macro and at least one expression in the association list does not evaluate the macro argument.

If none of the expressions in the association list evaluates the argument, Polyspace does not report a violation. As an exception, this rule is not violated if all expressions in the association list are constant expressions.

Troubleshooting

If you expect a rule violation but do not see it, refer to Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

In this example, the generic selections do not evaluate the macro argument for all associations. For example, the macro argument is not evaluated for the char association in the macro NONCOMPLIANT1. Polyspace reports defects on these macros.

#include <stdint.h>

void getInt(int);
void getChar(char);
void getDefault(void);

// Macro argument isn't evaluated in case of 'char'
#define NONCOMPLIANT1(X) _Generic((X), int : (X), char : 0 ) /* Noncompliant */ 

// Macro isn't evaluated in case of 'int'
#define NONCOMPLIANT2(X) _Generic((X), int : 1, default : (X)) /* Noncompliant */ 

// Macro argument isn't evaluated in the default case.
#define NONCOMPLIANT3(X) _Generic((X), int : getInt(X), char : getChar(X), default : getDefault )/* Noncompliant */ 

void foo(int i) {
	NONCOMPLIANT1(0);
	NONCOMPLIANT1(i);
	NONCOMPLIANT2(0);
	NONCOMPLIANT3(0);
}

This example shows generic selections that are compliant with the rule.

  • The macro COMPLIANT1 does not evaluate its argument in the association list. Polyspace does not report a violation for this case.

  • The macro COMPLIANT2 evaluates the macro argument once outside the association list. This is the usage recommended by MISRA™. The rule checker does not report a violation for this case.

  • As an exception, the rule checker does not report a violation if all the expressions in the association list are constant expressions, as shown in the macro EXCEPTION.

#include <stdint.h>

void doInt(void);
void doChar(void);
void doDefault(void);

void getInt(int);
void getChar(char);
void getDefault(void);

// Macro argument is never evaluated in the _Generic cases.
#define COMPLIANT1(X) _Generic((X), int : doInt, char : doChar, default: doDefault ) /* Compliant */ 

// Recommended standard usage.
#define COMPLIANT2(X) _Generic((X), int : getInt, char: getChar) (X) /* Compliant */ 

#define EXCEPTION(P) _Generic((P), int const* : 1 , int volatile* : 2 , int* : 3 , default: -1 )

void foo() {
	COMPLIANT1(0);
	COMPLIANT2(0);
}

void exceptionDemo(int const *p) {
	_Static_assert(EXCEPTION(p) == 1, "must be const");
}

Check Information

Group: Generic Selections
Category: Advisory
AGC Category: Advisory

Version History

Introduced in R2024a