LINUX.ORG.RU

красивый способ проверить совместимость прототипа ф-и и typedef-а указателя на эту ф-ю.

 , , , ,


2

7

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

★★★★★

Ну так можно просто попробовать инициализировать переменную с таким typedef такой функцией

int test_func(int a, int b, int c)
{
  return a+b+c;
}

typedef int (*func_ptr)(int, int, int);

typedef int (*func_ptr_wrong)(int, int);

func_ptr a = test_func;
func_ptr_wrong b = test_func;
И будет:
prog.c:20:20: warning: initialization of 'func_ptr_wrong' {aka 'int (*)(int,  int)'} from incompatible pointer type 'int (*)(int,  int,  int)' [-Wincompatible-pointer-types]
   20 | func_ptr_wrong b = test_func;
      |                    ^~~~~~~~~
Или нужно ошибку компиляции получить?

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)

указатель протягивается через много слоев API как void

void* это стиратель типа, после этого уже не проверить консистентность…

fsb4000 ★★★★★
()
Ответ на: комментарий от SZT

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

Или нужно ошибку компиляции получить?

ворнинга больше чем достаточно

cvv ★★★★★
() автор топика
Последнее исправление: cvv (всего исправлений: 1)
Ответ на: комментарий от t184256

Меня озадачивает ваш русский

cvv ★★★★★
() автор топика
Ответ на: комментарий от cvv

К сожалению, он нигде не использовал void*.

Поставленная задача вообще не решаемая, если указатель на функцию был преведен к void*.

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

trex6 ★★★★★
()
Ответ на: комментарий от LamerOk

Ок, сейчас у меня приблизительно вот так:

typedef int (*func_ptr_t)(int, int, int);

int test_func(int a, int b, int c);

int test_func(int a, int b, int c)
{
  return a+b+c;
}

моей фантазии не хватает как можно обьявить прототип test_func() через test_func_t.

Я пробовал вот такое, но как и ожидалось это не работает:

typedef int (*func_ptr_t)(int, int, int);

func_ptr_t test_func;

int test_func(int a, int b, int c)
{
  return a+b+c;
}
cvv ★★★★★
() автор топика
Ответ на: комментарий от trex6

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

указатели проверять не нужно. Нужно проверить соответствие прототипа и тайпдеф-а. SZT предложил это сделать путем введения дополнительного неиспользуемого указателя на ф-ю, что делает то что нужно, тоесть генерит ворнинг, но слишком топорно.

cvv ★★★★★
() автор топика
Ответ на: комментарий от cvv

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

Ну можно так, с _Generic и _Static_assert:

int test_func(int a, int b, int c)
{
  return a+b+c;
}

typedef int (*func_ptr)(int, int, int);

typedef int (*func_ptr_wrong)(int, int);

#define FUNC_CMP_T(a,b) _Generic( a, b: 1, default: 0)

_Static_assert (FUNC_CMP_T(test_func, func_ptr), "error test_func, func_ptr");

_Static_assert (FUNC_CMP_T(test_func, func_ptr_wrong), "error: test_func, func_ptr_wrong");

SZT ★★★★★
()
#include <type_traits>
#include <iostream>

using namespace std;

typedef int (*f_t)(int);

int f(int);
int g(int,int);
void h(int);

int main() {
    cout << boolalpha;
    cout << is_same<f_t, decltype(&f)>::value << endl;
    cout << is_same<f_t, decltype(&g)>::value << endl;
    cout << is_same<f_t, decltype(&h)>::value << endl;
}
$ g++ is_same.cxx && ./a.out 
true
false
false
anonymous
()
Ответ на: комментарий от cvv

моей фантазии не хватает как можно обьявить прототип test_func() через test_func_t.

А не надо через typedef на указатель, можно же сделать typedef самой функции:

void f(int i1,int i2) {(void) i1; (void) i2;}
void f1(int i1, int i2, int i3){(void) i1; (void) i2; (void)i3;}

typedef void func_t(int,int);

int main(int argc, char *argv[])
{
	func_t *p1;
	p1 = f;
	func_t *p2 = &f;
	int (*p3)(int,int,int) = p1; // warning
	p3 = p2;  // warning again
	return 0;
}

Вот чего ты не сможешь сделать, так это определить тело функции с помощью typedef. Но можешь выцепить тип самой функции через gcc-изм typeof.

LamerOk ★★★★★
()

через много слоев API как void

тайпдеф

Либо трусы сымай, либо крестик надевай. Ну или на жаву переходи (даже не знаю куда из первых двух это отнести).

pon4ik ★★★★★
()
Ответ на: комментарий от LamerOk

Собственно:

void f(int i1,int i2) {(void) i1; (void) i2;}
void f1(int i1, int i2, int i3){(void) i1; (void) i2; (void)i3;}

typedef typeof(f) func_t;

int main(int argc, char *argv[])
{
	func_t *p1;
	p1 = f;
	func_t *p2 = &f;
	int (*p3)(int,int,int) = p1; // warning
	p3 = p2;  // one more warning
	return 0;
}

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

А не надо через typedef на указатель, можно же сделать typedef самой функции:

typedef void func_t(int,int);

раньше такое не компилилось. Как я отстал от прогресса.

cvv ★★★★★
() автор топика
Последнее исправление: cvv (всего исправлений: 1)

Никак, ну создай глобальную переменную куда закидывай указатель приведённый к void а после прохода его по закоулкам кода сравнивай не похерило ли его там. Но это хрень чисто отловить место где его какойто чёрт перезаписывает . Не важно через сколько там слоёв что идёт если указатель на данные (или функцию) где-то в процессе может меняться (за исключением перезаписи в NULL если сброс какой) то что-то в коде не так. Если у тебя просто указатель гуляет по всему коду и в нужных местах явно приводится к типу а дальше опять гуляет как void то забей с ним всё нормально.

тайпдеф и прототип/ф-я всегда консистентны

Будут всегда, если ты из *void явно приводишь хоть к прототипу хоть к тайпдефу.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от cvv

моей фантазии не хватает как можно обьявить прототип test_func() через test_func_t.

Осилить язык. Прочитать что такое функция, что такое тип-функция и прочее. Хотя судя по стилю говна осиливать что-то ты не желаешь/можешь.

Я пробовал вот такое, но как и ожидалось это не работает:

Убеги *-говно. И напиши нормальный, правильный тип и всё заработает.

xiomar_georgios
()
typedef void(void_function)();

typedef int(int_function_int_int)(int, int);

int sum(int a, int b)
{
  return a + b;
}

int main()
{
  int_function_int_int* foo = &sum;
  void*                 bar = &sum;
  void_function*        baz = &sum;

  (void)foo;
  (void)bar;
  (void)baz;
}
$ gcc-8 -Wall -Wextra -Wpedantic func_ptr.c
func_ptr.c: In function ‘main’:
func_ptr.c:13:31: warning: ISO C forbids initialization between function pointer and ‘void *’ [-Wpedantic]
   void*                 bar = &sum;
                               ^
func_ptr.c:14:31: warning: initialization of ‘void (*)()’ from incompatible pointer type ‘int (*)(int,  int)’ [-Wincompatible-pointer-types]
   void_function*        baz = &sum;
  1. Нефиг приводить ссылку на функция к void* (т.е к ссылке на данные). В общем случае они могут находиться в разных адресных пространствах разной размерности.
  2. Нефиг допускать компилироваться код с сотнями варнингов.
AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Нефиг приводить ссылку на функция к void* (т.е к ссылке на данные). В общем случае они могут находиться в разных адресных пространствах разной размерности.

Вообще да, но обычно на это забивают. В стандарте POSIX, если почитать описание функции dlsym, которая возвращает void * и может вернуть как указатель на данные, так и на функции, написано такое: https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html

Note that conversion from a void * pointer to a function pointer as in:

fptr = (int (*)(int))dlsym(handle, "my_function");
is not defined by the ISO C standard. This standard requires this conversion to work correctly on conforming implementations.

SZT ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.