2013-03-06 2 views
8

Я пишу мультиплатформенный код, которому нужно использовать указатель на setjmp/sigsetjmp. Обычно это было бы так просто, как делатьMacro hell: независимый от платформы указатель на setjmp/sigsetjmp

#include <setjmp.h> 
void * sigsetjmp_p = sigsetjmp; 

Однако, ISO и POSIX состояние, setjmp/sigsetjmp может быть определена как макрос, и в самом деле, что это происходит в моей коробке Линукс. Вот отрывок из /usr/include/setjmp.h:

# define sigsetjmp(env, savemask)  __sigsetjmp (env, savemask) 

Проблема заключается в том, что, так как я не передавая аргументы sigsetjmp, макрос не расширяется и равнину sigsetjmp символ не определен в LIBC. Я надеялся, что смогу использовать макрос «черной магии» для извлечения имени «__sigsetjmp», но до сих пор я потерпел неудачу.

Другим вариантом будет использование __sigsetjmp, но это подразумевает проверку расширения для каждой поддерживаемой платформы, что я не хочу делать (отсюда и причина этого вопроса).

PS: Я ненавижу макросы.

Примечание:

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

#include <setjmp.h> 
int equals_sigsetjmp(void *p) 
{ 
void * sigsetjmp_p = sigsetjmp; 
return p == sigsetjmp_p; 
} 

Edit:

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

На практике все платформы, которые я знаю, реализуют его как функцию.

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

Я ценю ссылки на стандарты, но я хотел бы получить практический ответ, а не пурист один.

+2

Btw., Литье указателя функции на 'void *' также не переносится. –

+0

Существует настоящая причина для этого, но довольно долго объяснять (это связано с обертыванием функций). Поверьте мне, мне нужен этот указатель :) – fons

+0

@larsmans true, я просто хотел предоставить упрощенный пример. Предоставление правильного типа p, хотя и не меняет проблему. – fons

ответ

2

Вы не можете сделать это переносимо, поскольку стандарт явно запрещает это (§7.13):

Не определено setjmp ли макрос или идентификатор, объявленный с внешней связью. Если определение макроса подавлено для доступа к фактической функции (...) , поведение не определено.

На что POSIX добавляет, что

sigsetjmp() функция должна быть эквивалентна функции setjmp()

с некоторыми исключениями, которые не имеют отношения здесь.

Что касается вашего замечания

все платформы я знаю реализовать как функцию

В GCC, setjmp является встроенным, и я верю в Clang, так sigsetjmp. (Хотя библиотека C может иметь функцию версии, а также.)

+0

Спасибо за ссылку на стандарт. Вы правы, я не должен полагаться на фактическую функцию, но, к сожалению, мне нужно. И на практике это не приведет к неопределенному поведению для всех поддерживаемых нами платформ. – fons

+0

@fons: если у вас ограниченный набор платформ для поддержки, и вы хотите выполнить черную магию, тогда решение состоит в том, чтобы поддерживать список '# ifdef', который обрабатывает интересующие вас случаи. Но будьте осторожны, поведение по-прежнему не определено «на практике», потому что новая версия компилятора или библиотеки может разорвать вашу программу. Вы эффективно привязываетесь к версии компилятора, версии библиотеки и набору флагов компилятора, которые заставляют вашу программу работать. –

+0

Можете ли вы показать доказательство того, что 'setjmp' является встроенным в GCC? Я ясно вижу, что он объявлен как функция (без макросов) в /usr/include/sejmp.h. 'extern int setjmp (jmp_buf __env) __THROWNL;' – fons

-1

За то, что вы говорите, что вы хотите, я хотел бы сделать что-то вроде:

#include <setjmp.h> 
#ifdef sigsetjmp 
#define AVOID_FUNCTION_MACRO /* expand to nothing */ 
int sigsetjmp AVOID_FUNCTION_MACRO (sigjmp_buf env, int savesigs) { 
    log_fatal_error("Can't call sigsetjmp via pointer, its a macro!"); 
    abort(); 
} 
#endif 

Это даст вам sigsetjmp функцию, которую вы можете взять адрес из, но вы не можете на самом деле назвать это ...

+0

Я не уверен, что я следую. Разве этот код не будет просто расширяться до 'int sigsetjmp (sigjmp_buf env, int savesigs) {...}'? Тогда я мог бы получить адрес функции 'sigsetjmp', который мы только что определили, но он будет отличаться от адреса' __sigsetjmp', чего я хочу. – fons

+0

@fons: это идея. Функция '__sigsetjmp' является лишь одной конкретной реализацией sigsetjmp - другие решения на основе макросов делают что-то другое. Если sigsetjmp является макросом, вы не можете (в общем) получить указатель на функцию, который вы можете вызвать, так как даже если есть функция, он, вероятно, имеет массированные аргументы (поэтому макрос нужен). –

+0

Ну, это не то, что я искал, но спасибо :) – fons

0

Как насчет:

void my_sigsetjmp(sigjmp_buf env, int savesigs) 
{ 
    sigsetjmp(env, savesigs); 
} 

Затем используйте адрес & my_sigsetjmp