Вызов функции C без предварительного объявления - PullRequest
0 голосов
/ 09 мая 2020

Краткая версия: я хотел бы объявить функцию в том же операторе, который ее вызывает. Синтаксис, который я ищу, выглядит примерно так:

// foo is undeclared in this file, and implemented in another file
int main() {
    void* p = (cast_to_function_that_receivs_ints_and_returns_pointer)foo(1,2);
}

Длинная версия:

Следующий код создает очевидное предупреждение implicit declaration и ошибку undefined reference из-за вызов foo:

// a.c
int main() {
    void* p = foo(1,2);
}

Я добавляю следующий файл в компиляцию, чтобы решить undefined reference:

// b.c
void* foo(int a, int b) {
    return (void*)0xbadcafe;
}

Теперь я хотел бы решить implicit declaration. Обычное решение - изменить a.c на #include объявление в foo или объявить его, например:

// a.c
void* foo(int a, int b);
int main() {
    void* p = foo(1,2);
}

Но я бы предпочел не объявлять foo , а изменение строки, вызывающей foo, аналогично синтаксису указателей функций или примеру, который я опубликовал в «коротких версиях». Возможно ли это вообще?

Предположим, я владею C и у меня есть веская мотивация - я хотел бы «переопределить» поведение foo, перекомпилировав его с -Dfoo=bar.

Ответы [ 3 ]

2 голосов
/ 09 мая 2020

Итак, если я правильно понимаю, ваша мотивация заключается в том, что у вас есть существующий код, который выглядит как

p = bar(1,2);

, и вы хотите определить макросы, чтобы он вместо этого вызывал foo(1,2). Но вы не хотите изменять исходный файл, чтобы включить объявление foo - вы хотите делать все с помощью определений макросов командной строки. Я правильно понял?

Поскольку вы отметили это , возможно, вы хотите рассмотреть нестандартные расширения g cc для языка C. Если это так, вы можете сделать это с помощью выражений операторов g cc , которые также поддерживаются clang и i cc. Определите bar для расширения до выражения, содержащего блок, который объявляет foo и значение которого является указателем на foo. То есть:

#define bar ({ extern void *foo(int, int); foo; })

Или из командной строки:

gcc -D'bar=({ extern void *foo(int, int); foo; })'  call_bar.c

Попробуйте на Godbolt .

Это имеет

Вариантом было бы определение макроса bar(a,b) с двумя аргументами, где соответствующее выражение оператора фактически вызывает foo:

gcc -D'bar(a,b)=({ extern void *foo(int, int); foo((a), (b)); })' call_bar.c

, но это не удастся, если исходный код попытается вызвать p = (bar)(a,b) или пытается взять адрес bar.

Я не знаю никакого способа получить такой точный эффект в стандартном C. Но другой подход - создать файл заголовка, содержащий объявление foo, а затем использовать -include, чтобы «вставить» его в верхнюю часть исходного файла:

gcc -include declare_foo.h -Dbar=foo call_bar.c

This isn ' Технически это то, о чем вы просили, потому что на каком-то уровне это подразумевает объявление foo «заранее», но это все равно может помочь решить вашу проблему. В этом случае все стандартно C, но мы перенесли «непереносимость» из кода в процесс сборки.


С другой стороны, если желаемая замена на bar это что-то достаточно простое, чтобы вставить макрос, например, постоянный возврат в вашем примере, тогда вы можете вырезать посредника foo и просто определить макрос:

gcc -D'bar(a,b)=((void *)0xbadcafe)' call_bar.c
1 голос
/ 09 мая 2020

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

Скомпилируйте файл mock_foo. c и вместо этого свяжите объектный файл с программой из foo. c.

Другой подход - всегда вызывать только через определение макроса:

#ifdef MOCK_FOO
  #define (FOO(a, b) mock_foo(a, b))
#else
  #define (FOO(a, b) foo(a, b)
#endif

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

0 голосов
/ 09 мая 2020

Вы можете преобразовать функцию так, как вы ее называете:

void *p = ((void *(*)(int, int))foo)(1, 2);

Это уродливо, я не вижу для этого веской причины, но вы можете.

...