LINUX.ORG.RU

Как сохранить и вызвать callback в void * указателе

 , , ,


0

2

Сабж актуален для epoll. В epoll_event.data.ptr можно сохранить указатель типа void *, который при наступлении события для файлового дескриптора будет сразу доступен и его не нужно искать.

Хочу сохранить в этом указателе адрес функции и потом эту функцию вызывать. С сохранением вроде бы всё понятно:

void event_handler() { … };
…
epoll_event.data.ptr = (void *)event_handler;

Наступившие события сохраняю в epoll_events:

epoll_wait(epoll_fd, epoll_events, EPOLL_MAX_EVENTS, -1);

Как теперь привести epoll_events[ready_fd_i].data.ptr к типу адрес функции вида „void event_handler()“?

void (*func_ptr)()  = (void (*)())data.ptr;
func_ptr();

вроде так.

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

> Как теперь привести epoll_events[ready_fd_i].data.ptr к типу адрес функции вида „void event_handler()“?

(void(*)())epoll_events[ready_fd_i].data.ptr

вызвать:

((void(*)())epoll_events[ready_fd_i].data.ptr)();

arsi ★★★★★
()

а если для с99, то примерно так:

void (*func)();
*(void **) (&func) = epoll_events[ready_fd_i].data.ptr;
func();
arsi ★★★★★
()
Ответ на: комментарий от arsi

*(void **) (&func) = epoll_events[ready_fd_i].data.ptr;

Омг, ачем такая конструкция? Да и в варианте waker второе приведение типов (после =) в Си не нужно (в С++ нужно, но С++ само по себе не нужно :-). То есть, самый читаемый вариант будет:

void (*func)(void) = ev->data.ptr;

func();

Плюс, ТСу: «void event_handler() { … };» в Си значит «void event_handler(...) { … };» (что имеет мало смысла), а не «void event_handler(void) { … };». Это так, на всякий случай, глаз зацепился.

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

> Омг, ачем такая конструкция?

маны читай. например:

man dlsym
           /* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
              would seem more natural, but the C99 standard leaves
              casting from "void *" to a function pointer undefined.
              The assignment used below is the POSIX.1-2003 (Technical
              Corrigendum 1) workaround; see the Rationale for the
              POSIX specification of dlsym(). */

           *(void **) (&cosine) = dlsym(handle, "cos");
arsi ★★★★★
()
Ответ на: комментарий от hexdump01010101

void event_handler() { … };" в Си значит «void event_handler(...) { … };

Спасибо, не знал.

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

например:

Ах, ну да. Указатель на ф-цию может быть != указателю на данные.

Но ваш пример ничего вроде не решает. Вы просто взяли и урезали его до void *, избавились от возможного warning'a (причем я повторяю: возможного, а не 100%-ого — undefined же). Но вы выстрелите себе в ногу, когда указатель на функцию станет действительно больше указателя на данные. :-)

Когда вы действительно встретите такую архитектуру, тогда вам компилятор и должен выдать 100% варнинг, или ошибку. А в вашем примере уже не выдаст, вы спрятали этот баг. :-)

Поэтому я бы так писать всё равно не стал. Если уж нужна такая кросс-платформенность, то лучше сделать свой fptr_t тип и ф-ции преобразования. А конструкция «*(void **) (&cosine) = dlsym(handle, „cos“);» это мина, просто избавление от варнинга. А в жизни работает лишь потому, что в posix'e указатель на функцию обязан быть равен (void *).

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

> Но ваш пример ничего вроде не решает.

мне, конечно, лестно слышать, что ты считаешь меня автором стандарта позикс, но это, к сожалению, не так :)

> А в жизни работает лишь потому, что в posix'e указатель на функцию обязан быть равен (void *).

и линукс этому требованию следует. или ты знаешь ещё одну ос, реализующую epoll, но не удовлетворяющую позиксу? ;)

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

мне, конечно, лестно слышать, что ты считаешь меня автором стандарта

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

Но инженер должен понимать, что он делает, и четко отделять пример, от реальной жизни.

Пример будет работать на всех компиляторах, и будет работать в жизни (так как POSIX сам и определяет чему равен указатель на функцию). Задача составителей стандарта выполнена. А у инженера задача другая, писать хороший код, читаемый (без ужасных кастингов за просто так), и который не будет стрелять по ногам, если что. :-)

и линукс этому требованию следует. или ты знаешь ещё одну ос, реализующую epoll, но не удовлетворяющую позиксу? ;)

Линукс не соответствует позиксу. ;-)

Но я говорю в том же ключе, что и вы — нужно быть прагматиком, а не формалистом.

С точки зрения здравого смысла, лучше не делать ту конструкцию, что приводит посикс в пример. По дефолту на всех реальных ОС и компиляторах приведение void * к функции не вызовет даже варнинга. А тот ужасный кастинг только вредит.

п.c.
На PowerPC 64, кстати говоря, указатель на функцию != указателю на данные. Но, чтобы не страдать маразмом, там приняли, что &function, возвращает указатель на указать на функцию. А *function, делает обратное. Вот так вот получается POSIX-совместимость.

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

> Вовсе не считаю.

тогда с какого перепугу пример из стандарта/мана стал моим? ;)

> Просто авторы стандарта руководствуются своими тараканами в голове,

ага, а ты своими, и считаешь их более достойными (ведь они твои, родные), и готов бороться за них до посинения, попутно подкидывая их другим :)

> Но инженер должен понимать, что он делает

ну последуй своему примеру уже :) код ТСа уже привязан к линуксу со всеми его нюансами (использованием линуксового epoll), а ты предлагаешь закрыть на это глаза и писать остальной код в т.ч. и под платформы, где линукса никогда не было и не будет, и при этом что-то говоришь о том, что инженер должен понимать, что делает? смешной ты :)

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

тогда с какого перепугу пример из стандарта/мана стал моим? ;)

Ну во-первых, я говорил про ваш оригинальный пример ТСу. А во-вторых, даже если вы привели пример из стандарта, и напечатали его тут в треде, то «ваш пример» все равно говорить допустимо, разве нет?

В любом случае, оригинальный ваш пример, и пример, приведенный вами из стандарта, есть суть одно и тоже.

ага, а ты своими, и считаешь их более достойными

Тут не в достоинстве дело. Я же привел аргумент почему именно не нужно использовать такую конструкцию:

- никакой выгоды в реальных условиях

- больше закорючек

- меньше понятности

- скрывает реальные баги переносимости

И я дал объяснение, почему именно стандарт делает кастинг. Все логично, разве нет? Если нет, то какие ваши аргументы, кроме как «это написано в примере к стандарту»? Мне интересно самому.

Также, вы говорили всё это в контексте C99. ТС явно не был уверен как лучше работать с указателями на функции. Ваш пример из POSIX был бы хорошим примером того, как работать с указателями на функцию в С99 (а не POSIX)? Мне кажется, что нет, потому как ваш пример явно небезопасен, и даже скроет ошибки. Мой пример будет безопасен, но на «экзотике» компилятор бы честно бы отрапортовал, что «вот-тут вот у вас платформо-специфичная фигулина, извольте посмотреть». Это безотносительно к POSIX.

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

Да где же я такое предлагал? Наоборот. Непереносимость и возможность выстрелить себе в ногу даже не осознавая этого, это лишь один из минусов, но не единственный. Меня прежде всего заинтересовала ужасная конструкция из кастов, она просто глаз мозолила. :-)

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