LINUX.ORG.RU

[gcc/c] необычные макросы


1

1

знаю есть возможность использовать макросы с переменным числом аргументов, типа:

#define msg( format, ... ) printf( format, args )

можно ли во время препроцессинга узнать количество указанных аргументов ?


Ответ на: комментарий от AptGet

> есть костыль

занятное решение, попробую заюзать. спасибо за подсказку.

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

> А зачем тебе это?

нужно что-то наподобие variadic макроса с генерацией различного кода в зависимости от числа аргументов.

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

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

хех, получилось, что было нужно! ещё раз благодарю за подсказку! (:

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

#include <stdio.h>

#define func(...) PP_NARG_(__VA_ARGS__,PP_RSEQ_N())(__VA_ARGS__)
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( _1, _2, _3,N,...) N
#define PP_RSEQ_N() func3,func2,func1,func0

#define func0() printf( "this is obsolete function!\n" )
#define func1(a) printf( "func1( %d )\n", a )
#define func2(a,b) printf( "func2( %d, %d )\n", a,b )
#define func3(a,b,c) printf( "func3( %d, %d, %d )\n", a,b,c )


int main( int c, char **a ) {

	func(1);
	func(1,2);
	func(1,2,3);

// compilation error
//	func();

	return 1;

}

думаю, кому-нибудь еще пригодится.

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

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

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

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

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

Будет время, обязательно посмотрю куда лучшие языки... а пока C/ASM + многопоточность!

ЗЫ: Изврат - это фабрика классов на PHP...

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

еще вариант

# ifndef DEBUG
#  define log_err(fmt,...)  syslog(LOG_ERR, __FILE__":"fmt, ##__VA_ARGS__)
#  define log_perr(str)     syslog(LOG_ERR, __FILE__"%s: %s", str, strerror(errno))
#  define log_crit(fmt,...) syslog(LOG_CRIT, __FILE__":"fmt, ##__VA_ARGS__)
#  define log_warn(fmt,...) syslog(LOG_WARNING, __FILE__":"fmt, ##__VA_ARGS__)
#  define log_info(fmt,...) syslog(LOG_INFO, __FILE__":"fmt, ##__VA_ARGS__)
# else /* DEBUG */
#  define log_err(fmt,...)  syslog(LOG_ERR, __FILE__":%s:%d:"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#  define log_perr(str)     syslog(LOG_ERR, __FILE__":%s:%d: %s: %s",__FUNCTION__, __LINE__, str, strerror(errno))
#  define log_crit(fmt,...) syslog(LOG_CRIT, __FILE__":%s:%d:"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#  define log_warn(fmt,...) syslog(LOG_WARNING, __FILE__":%s:%d:"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#  define log_info(fmt,...) syslog(LOG_INFO, __FILE__":%s:%d:"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
# endif /* DEBUG */
Skolotovich ★★★
()
Ответ на: комментарий от Skolotovich

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

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

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

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

> Эдакая перегрузка по количеству аргументов.

ага, именно.
+ параметры по умолчанию в C
+ инлайн реализация функций, наподобие open(2), mq_open(2), clone(2).
+ небольшая оптимизация функций наподобие futex(2)

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

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

Я вот не вижу серьезного различия от

func1(a);
func2(a, b);

и

func(a);
func(a, b);

кроме как снижения читаемости и тонну макросов (которые еще к тому же вряд ли переносимы).

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

ну, gcc / icc съели код без каких либо подозрений на его некорректность, а большего мне сейчас и не нужно. но, если и понадобится вдруг, то выход из ситуации думаю найдется... например, использовать внешний препроцессор для редкого компилятора.

количество макросов зависит от того, как ими воспользоваться. и их будет меньше чем в тех же kernel-headers.

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

в общем случае — это кусок позикса.

а вот

debug_stdout (msg);
debug_stderr (msg);

лучше, чем

debug (msg);
debug (msg, stderr);

В си нету тайп рефлекшена, не стоит извращаться. Субъективно это может и красиво, но только в программах размером меньше тысячи строк.

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

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

Вот это можно для тех кто в танке на пальцах?

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

Итить. Реализация перегрузки имён средствами препроцессора. Си, как и всегда, торт.

Хотя изврат, конечно, редкостный.

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

Вот это можно для тех кто в танке на пальцах?

например есть futex(2):

int futex(int *uaddr, int op, int val, const struct timespec *timeout,int *uaddr2, int val3);

согласно ману, четыре последних аргумента используются не всегда.

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

	.code
	.globl futex
futex:
	movq %rdi, %rdi	# 1st argument
	movq %rsi, %rsi	# 2nd argument
	movq %rdx, %rdx	# 3rd argument
	movq %rcx, %rcx	# 4th argument
	movq %r8, %r8		# 5th argument
	movq %r9, %r9		# 6th argument
	.
	.
	.
	retq

тогда в C делаем следующее:

int futex(int *uaddr, int op, ...);
futex(a,b);
futex(a,b,c);
futex(a,b,c,d);
futex(a,b,c,d,f);
futex(a,b,c,d,f,g);

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

вот в этом случае и приходит на помощь «перегрузка» в зависимости от количества аргументов функции:

#include <stdio.h>

/* common macroses */
#define __inline_ static __inline__ __attribute__((__always_inline__))

#define __rseqn( s ) __##s##9_, __##s##8_, __##s##7_, __##s##6_,\
	__##s##5_, __##s##4_, __##s##3_, __##s##2_, __##s##1_, __##s##0_
#define __argsn( a, b, c, d, e, f, g, h, i, j, n, ... ) n
#define __funcn( ... ) __argsn( __VA_ARGS__ )

/* futex specific macros */
#define futex( ... ) __funcn(__VA_ARGS__,__rseqn(futex))(__VA_ARGS__)

/* futex implementation */
__inline_ int __futex1_( int a, int b )
{ printf("futex(a,b)\n"); }

__inline_ int __futex2_( int a, int b, int c )
{ printf("futex(a,b,c)\n"); }

__inline_ int __futex3_( int a, int b, int c, int d )
{ printf("futex(a,b,c,d)\n"); }

__inline_ int __futex4_( int a, int b, int c, int d, int e )
{ printf("futex(a,b,c,d,e)\n"); }

__inline_ int __futex5_( int a, int b, int c, int d, int e, int f )
{ printf("futex(a,b,c,d,e,f)\n"); }

/* futex usage */
int main( int c, char *e ) {

	futex(1,2);
	futex(1,2,3);
	futex(1,2,3,4);
	futex(1,2,3,4,5);
	futex(1,2,3,4,5,6);

	return 1;

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

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

Опять же, ты выхлоп gcc изучал? Может он так и делает.

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

> переносимость летит в тартары
в gcc и icc, всё ок. до других компиляторов у меня ещё руки не добрались. ради интереса можно попробовать этот трюк ещё в Open64, OSS и Clang, хотя не обязательно, мне такая переносимость точно не понадобится.

... Может он так и делает.

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

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

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

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

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

и это охренеть как важно для скорости, особенно учитывая тормоза сисколла

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

> и это охренеть как важно для скорости, особенно учитывая тормоза сисколла
конечно, это не краеугольный камень, но дополнительная возможность компилятору лучше оптимизировать код вне функции... ну и плюс, я это говорил лишь что-бы показать имеющиеся возможности, прежде чем перейти к инлайну... а с сисколлами, да, придется поужаться. например, бывает, что вместо futex(2) можно использоваться обычный spinlock.

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