Каков правильный ANSI-совместимый способ создания pthread из функции без параметров в C с использованием clang-10+?

У меня есть функция без параметров в C. Clang-16 предлагает мне избегать объявления функции без прототипа, но pthread_create больше не работает с прототипом, у которого нет параметров. Это сценарий:

Файл main.c пытается создать pthread из функции thread_function:

// main.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "./thread.h"

int main(void) {
  pthread_t thread_id;
  pthread_create(&thread_id, NULL, thread_function, NULL);
  pthread_join(thread_id, NULL);
  return EXIT_SUCCESS;
}

Функция thread_function объявлена ​​в заголовочном файле thread.h:

// thread.h
#ifndef _THREAD_H_
#define _THREAD_H_

void *thread_function();

#endif  // _THREAD_H_

И определяется в файле thread.c:

// thread.c
#include <stdio.h>

void *thread_function() {
  printf("Hi\n");
  return NULL;
}

Я скомпилировал программу с помощью clang-16:

$ clang-16 main.c thread.c -o thread -Wpedantic
In file included from main.c:4:
././thread.h:4:22: warning: a function declaration without a prototype is deprecated in all versions of C [-Wstrict-prototypes]
void *thread_function();
                     ^
                      void
1 warning generated.
thread.c:3:22: warning: a function declaration without a prototype is deprecated in all versions of C [-Wstrict-prototypes]
void *thread_function() {
                     ^
                      void
1 warning generated.

Чтобы исправить предупреждения, я применил оба предложенных исправления:

// thread.h
#ifndef _THREAD_H_
#define _THREAD_H_

void *thread_function(void);

#endif  // _THREAD_H_

А также в thread.c:

// thread.c
#include <stdio.h>

void *thread_function(void) {
  printf("Hi\n");
  return NULL;
}

Теперь я больше не могу скомпилировать программу:

$ clang-16 main.c thread.c -o thread -Wpedantic
main.c:8:36: error: incompatible function pointer types passing 'void *(void)' to parameter of type 'void * _Nullable (* _Nonnull)(void * _Nullable)' [-Wincompatible-function-pointer-types]
  pthread_create(&thread_id, NULL, thread_function, NULL);
                                   ^~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/pthread.h:334:31: note: passing argument to parameter here
                void * _Nullable (* _Nonnull)(void * _Nullable),
                                            ^
1 error generated.

Я изменил подпись функции на:

// thread.h
#ifndef _THREAD_H_
#define _THREAD_H_

void *thread_function(void *);

#endif  // _THREAD_H_

А также:

// thread.c
#include <stdio.h>

void *thread_function(void *) {
  printf("Hi\n");
  return NULL;
}

Затем он компилируется, но больше не ANSI-совместим:

$ clang-16 main.c thread.c -o thread -Wpedantic
thread.c:3:29: warning: omitting the parameter name in a function definition is a C2x extension [-Wc2x-extensions]
void *thread_function(void *) {
                            ^
1 warning generated.

Если я изменю подпись на void *thread_function(void *args), это больше не будет «функция без параметров». Как я могу создать pthread из функции без параметров и при этом быть ANSI-совместимым?

это больше не «функция без параметров». Правильный. Есть ли у вас особые требования по предоставлению функции, которая не соответствует требуемому типу?

Gerhardh 11.04.2024 09:33

Что значит «ANSI-совместимый»? На самом деле для потоков не существует стандарта ANSI. Под «ANSI» вы имеете в виду C89? Или ПОСИКС?

Lundin 11.04.2024 09:34

@Gerhardh Требование заключалось в том, чтобы предоставление функции с фиктивным параметром вызывало предупреждение о неиспользуемой переменной.

Hamid Rouhani 11.04.2024 11:53

Это не требование, а простой факт (в зависимости от компилятора). Вы имеете в виду, что требуется избегать предупреждения о «неиспользуемой переменной»? Эту проблему можно легко решить, не нарушая договоров. Описание API (включая правильную сигнатуру функции) — это договор, которому вы должны следовать.

Gerhardh 11.04.2024 11:56

@Лундин Ты прав! На самом деле мое предположение об API POSIX для потоков было ошибочным. Я не проверял точный аргумент на страницах руководства. Моей целью было написать в точном соответствии со стандартом POSIX и обеспечить обратную совместимость со старыми версиями C.

Hamid Rouhani 11.04.2024 12:03

@Gerhardh Да, я хотел избежать использования ненужных переменных, но я не знал, что определение POSIX для pthread_create требует аргумента для функции.

Hamid Rouhani 11.04.2024 12:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
97
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Если вы хотите обеспечить совместимость ANSI с clang, вам нужно передать либо -std=c89, либо -ansi.

Вы можете явно отбросить аргумент, «приведя его к void». Поскольку вам абсолютно необходимо назвать аргумент, чтобы удовлетворить -Wpedantic, чтобы это действительно было действительным C (это совершенно другая история, когда компиляторы считают пропуск имени параметра приемлемым, если, например, -Wpedantic -Werror. Спасибо Джону Боллинджеру за указание на это), вам придется использовать этот.

void *thread_function(void *arg) {
    (void) arg;
    printf("Hello");
    return NULL;
}

Это компилируется с -ansi -Wall -Wpedantic -Wextra -Werror. Обозреватель компилятора ссылка.

Опять же, предупреждение относительно безымянных параметров не имеет никакого отношения конкретно к «ANSI C». Итак, используйте -ansi или -std=c89 тогда и только тогда, когда вы знаете, что делаете, и действительно требуете соответствия C89/ANSI. В противном случае вам, вероятно, просто нужен последний стандарт ISO, поддерживаемый вашим компилятором (см. man clang и найдите -std), или тот, который используется вашей командой и т. д.

В определении функции вам необходимо назвать все параметры, чтобы удовлетворить ограничениям языка C. Если вы опустите имена параметров в определении функции, результат будет недопустимым C, даже если вы сможете найти компилятор, который его принимает.

John Bollinger 15.04.2024 18:22

Ах, ладно, я этого не знал. Я сам никогда не опускаю имена параметров, поэтому никогда не беспокоился по этому поводу слишком сильно. Я отредактирую свой ответ.

loonatick 15.04.2024 18:58
Ответ принят как подходящий

Pthreads всегда ожидал указатель на функцию типа void* func (void*) независимо от того, какой компилятор или стандарт C вы используете. Если вы когда-либо делали что-то еще, вы делали это неправильно.

void *thread_function(); — недопустимая функция обратного вызова потока во всех версиях C и POSIX. Аналогично, void *thread_function(void) ничего не улучшает, поскольку это по-прежнему неправильный тип функции.

Последнее сообщение компилятора, которое вы получаете, связано с тем, что определение функции (в отличие от объявления/прототипа) без явных имен параметров является чепухой. Чтобы быть совместимым с ISO C (забудьте о «ANSI»), вам нужно дать ему имя:

void *thread_function (void* param)

Теперь, если вы не передаете функции какие-либо параметры, вы можете просто привести ее к void, чтобы отключить предупреждения: (void)param;. В качестве альтернативы в предстоящем C23 параметр можно будет объявить как [[maybe_unused]] void* param.

Если я изменю подпись на void *thread_function(void *args), это больше не будет «функция без параметров».

Этого никогда не было. Pthreads требуют функцию с параметром void*, точка.

Программист не может решить, какие типы функций здесь использовать. Правильные типы для использования — это те, которые перечислены в API библиотеки: man pthread_create:

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

Как вы прекрасно знаете, void *thread_function(); не является неправильным в C2011 и более ранних версиях. Он объявляет функцию, принимающую неопределенное количество аргументов. Программа, использующая такую ​​функцию, как аргумент start_routine для pthread_create, является корректной. Если хотите, называйте это плохим стилем, но не вводите людей в заблуждение относительно того, что является действительным, а что нет.

zwol 12.04.2024 02:01

@zwol Как сказал clang в вопросе ОП: «объявление функции без прототипа не рекомендуется во всех версиях C». Да, C позволяет вам писать всевозможные мусорные программы с несуществующей типобезопасностью, если вы настаиваете... действительно, вы можете присвоить void* (*) ()void* (*)(void*) до C23, но это не значит, что вам следует это делать.

Lundin 12.04.2024 08:28

Я никогда не говорил, что кто-то должен делать то, что делает код ОП. Я только хочу, чтобы вы пересмотрели свой ответ, чтобы признать различие между «код ОП - плохой стиль» и «код ОП неверен». В C2011 и более ранних версиях первое верно, а второе — нет.

zwol 12.04.2024 14:48

Как я могу создать pthread из функции без параметров и при этом быть ANSI-совместимым?

На самом деле это не так уж и сложно, просто вызовите свою функцию с помощью функции перехода.

void *thread_function() {
  printf("Hi\n");
  return NULL;
}
void *thread_function_jump(void *arg) {
  (void)arg; // To silence some compilers unused arguments warnings.
  printf("Hi\n");
  return thread_function();
}
int main(void) {
  pthread_t thread_id;
  pthread_create(&thread_id, NULL, thread_function_jump, NULL);
  pthread_join(thread_id, NULL);
  return EXIT_SUCCESS;
}

Кроме того:

#define _THREAD_H_

Не «совместим». Идентификаторы, начинающиеся с _, за которым следует заглавная буква, зарезервированы, и нам не следует их использовать. Чтобы обеспечить совместимость, вам следует сменить защиту заголовка.

Это по-прежнему выдает предупреждение о неиспользуемом параметре, о котором и был вопрос.

stark 11.04.2024 15:02

Извините, вопрос в том, что это такое. Предупреждения не делают код ANSI несовместимым.

KamilCuk 11.04.2024 15:18

Другие вопросы по теме