Стоит ли менять объявление C, чтобы добавить безопасность типов? - PullRequest
0 голосов
/ 19 февраля 2019

Мне интересно, разумно или целесообразно переопределять объявления заголовков для установки определенного типа.В предпочтении к чему-то вроде void *, который не добавляет безопасности типов.

Например, если у вас есть общая функция хранения, которая добавляет показание датчика в круговой буфер:

int add_reading(void *);

В порядкечтобы быть универсальным, функция должна быть определена как пустота *.Однако в заголовочном файле вы могли бы объявить функцию как:

int add_reading(my_reading_t *);

, что добавило бы степень безопасности типов по сравнению с указателем void.В универсальном заголовке вы можете установить тип с помощью #define, по умолчанию void.Таким образом, тип переопределения может быть определен непосредственно перед # include.

Это кажется ненужным хаком, но можно спорить то же самое и с непрозрачными указателями - используя opaque_type_t * вместо void *.Но это, по крайней мере, определенное поведение.Что мне интересно, если этот тип беспорядка вызывает UB (неопределенное поведение)?

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

Функция, объявленная с int add_reading(void *), несовместима с функцией, определенной с int add_reading(my_reading_t *).Поведение вызова функции, определенной с последним, с использованием идентификатора, объявленного с первым (или другого обозначения функции с этим типом), не будет определено стандартом C, согласно C 2018 6.5.2.2 9:

Если функция определена с типом, который не совместим с типом (выражения), на который указывает выражение, обозначающее вызываемую функцию, поведение не определено.

Согласно 6.7.6.1 2:

Для совместимости двух типов указателей оба должны иметь одинаковую квалификацию, и оба должны быть указателями на совместимые типы.

Очевидно, типы параметров void * и my_reading_t * не являются указателями на совместимые типы (предполагается, что my_reading_t является структурным типом, а не псевдонимом для void).

Согласно 6.7.6.3 15:

Для совместимости двух типов функций… соответствующие параметры должны иметь совместимые типы…

0 голосов
/ 19 февраля 2019

То, что вы предлагаете, выглядит как плохая идея.Если вы хотите повысить безопасность типов, чтобы она не могла скомпилироваться, если вы пытаетесь передать неправильный тип, вы можете попробовать использовать C11 _Generic.

int add_reading (void *);
#define ADD_READING_HELPER(X) _Generic((X), \
    my_reader_t *: add_reading((X)) \
)

int main(void) {
    my_helper_t good;
    printf("%d\n", ADD_READING_HELPER(&good)); // works because _Generic has a rule for dealing with (my_reader_t *)
    int bad;
    printf("%d\n", ADD_READING_HELPER(&bad)); // fails to compile because the _Generic does not have a rule for dealing with (int *)
}

int add_reading (void *arg) {
    // whatever the function does
}

По существу _Generic позволяет вамвыполнять различные действия в зависимости от типа управляющего выражения, передаваемого в него, который определяется во время компиляции.Здесь мы создаем правило для my_reader_t *, но не для других типов, поэтому попытка передать в _Generic что-либо, кроме my_reader_t *, помешает программе компилироваться, так как у нее нет никаких правилчто делать с этим типом.

...