Можно ли объявить функцию в заголовочном файле с неизвестным типом, который указан только в файле '. c'? - PullRequest
0 голосов
/ 10 января 2020

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

Можно ли объявить функцию в заголовочном файле .h с неизвестным типом параметров , который будет указан только в файле. c?

c ++ Шаблоноподобное решение не решит мой случай, так как я не хочу, чтобы другие типы параметров были возможны В качестве входных данных моей функции определение рассматривает только один случай, и я бы предпочел не иметь эту опцию. Я просто хочу, чтобы объявление в заголовке игнорировало тип параметра, но просто сообщало другим файлам о существовании этой функции. Это выполнимо или я должен пересмотреть мою проблему?

Детали реализации: функция была объявлена ​​ stati c в файле. c, и я хотел удалить stati c, чтобы иметь возможность использовать его в другом месте, включая соответствующий заголовок file.h.

Ответы [ 5 ]

3 голосов
/ 10 января 2020

Ваш комментарий:

Возможно, детали реализации дадут больше понимания. Я работаю над https://github.com/bitcoin-core/secp256k1, пытаюсь включить функцию * stati c static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) non-stati c и объявить ее в заголовочном файле с тем же именем без перемещения всех необходимых включений, связанных с к параметрам type.

Это проблематично c, поскольку в указанной кодовой базе typedef называет псевдоним анонимными структурами (typedef struct { ... } secp256k1_context;, et c.).

Если они псевдонимы помеченные структуры (typedef struct tag typedef_name; или typedef struct tag {...} typedef_name;), вы можете просто сделать:

//forward-declare the structs (assuming tag = typedef_name)
struct secp256k1_context;
struct secp256k1_ge;
struct secp256k1_pubkey;
//use struct tag instead of typedef_name
int secp256k1_pubkey_load(const struct secp256k1_context* ctx, 
                          struct secp256k1_ge* ge, 
                          const struct secp256k1_pubkey* pubkey);

, но это не так, поэтому вам нужно включить определения типов параметров. (Как отмечает Влад из Москвы , вы можете использовать объявление без прототипа в этом конкретном случае, но тогда вы потеряете безопасность типов.)

Это яркий пример того, почему typedef s для анонимных агрегатов как части API плохая идея.

Лучше оставить struct как часть API или использовать typedef s для структур, которые предсказуемо помечены, например, typedef struct file_tp file_tp; (если бы stdio.h сделал это, нам не пришлось бы #include <stdio.h> каждый раз, когда мы хотели просто передать указатель на файл, но вместо этого он представлял бы необъявляемый FILE, который требует включения stdio.h даже там, где их можно избежать.)

1 голос
/ 10 января 2020

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

Например

int my_function();

Из стандарта C (6.7.6.3 Деклараторы функций (включая прототипы))

3 Список идентификаторов в объявителе функции, который не является частью определения этой функции, должен быть пустым.

и

… Пустой список в объявителе функции, который не является частью определения этой функции, указывает, что не предоставляется информация о количестве или типах параметров.

учтите, что в этом случае у вас будут некоторые ограничения, такие как продвижение аргумента по умолчанию

Также, возможно, вам следует рассмотреть вариант c в качестве альтернативы.

0 голосов
/ 10 января 2020
static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge,
                                 const secp256k1_pubkey* pubkey)

Поскольку вы имеете дело только с указателями на структуры, вам просто нужны определения типов и неполные определения структур, которые вы можете взять из файлов .h, где они определены, чтобы дать компилятору достаточно контекста для объявления. Это легко для тех, кто является typedefs структур, например, поместите эту строку над объявлением функции:

typedef struct secp256k1_context_struct secp256k1_context;

Безопасно сделать одинаковое переопределение, так что это будет нормально работать с полными заголовками или без них.

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

#ifndef SECP256K1_H
typedef void secp256k1_pubkey;
#endif

#ifndef SECP256K1_GROUP_H
typedef void secp256k1_ge;
#endif

(Вы можете использовать настоящее определение secp256k1_pubkey, но значение _ge зависит от поля, которые могут варьироваться, и я думаю, что наличие достаточного контекста для выбора правильных полей - это то, чего вы пытались избежать в первую очередь.)

Это немного по сравнению с agile, несмотря на изменения в библиотеке, и требует Вы включаете заголовки secp256k1 перед этим файлом, если они вообще есть. (В противном случае вы получите ошибки компилятора: запишите здесь в комментарии о том, что вы сделали, так что, если кто-то найдет это в будущем, он будет знать, как это исправить.)

Тогда у вас достаточно контекст для определения функции в вашем собственном заголовке. Обратите внимание, что любой вызывающий код, который не просто пропускает эти структуры откуда-то еще, вероятно, все равно будет нуждаться в полных определениях структуры: почему бы просто не поместить это объявление функции в новый .h, который в любом случае требует включения полных заголовков, что только нужно указать несколько c мест?

0 голосов
/ 10 января 2020

Вы можете использовать любую функцию без параметров:
int my_function () ; или
определение функции с параметрами void *
int my_function (void * p1, void * p2 ...) ;

См. Комментарий Санджит Сараванарадж

Это хорошая идея? Конечно, нет.

0 голосов
/ 10 января 2020

Единственная причина объявления прототипа функции состоит в том, чтобы сообщить другому коду, какие параметры он принимает, и какова функция возвращаемого типа. Так что ваша идея (ИМО) не имеет никакого смысла

...