LINUX.ORG.RU

СИ макросы понеслось


0

0

#define CLEANUPBLK(name) \
int __##name##_cleanup_code; \
{

#define CLEANUPBEG(name, val) \
} \
__##name##_cleanup_clean: \
__##name##_cleanup_code = val; \
__##name##_cleanup_select_clean: \
switch (__##name##_cleanup_code) \
{ \
  case val:

#define CLEANUPNEXT(name, val) \
  case val:

#define CLEANUPEND(name) \
  default: \
    goto __##name##_cleanup_break; \
    goto __##name##_cleanup_clean; \
    goto __##name##_cleanup_select_clean; \
} \
__##name##_cleanup_break:

#define CLEANUPALL(name) \
goto __##name##_cleanup_clean

#define CLEANUPBRK(name) \
goto __##name##_cleanup_break

#define CLEANUPSEL(name, val) \
__##name##_cleanup_code = val; \
goto __##name##_cleanup_select_clean

int main()
{
  CLEANUPBLK()
  {
     if (!init1())
       CLEANUPBRK();

     if (!init2())
       CLEANUPSEL(, 1);

     if (!init3())
       CLEANUPSEL(, 2);

     if (!init4())
       CLEANUPSEL(, 3);
     
     if (!some_func())
       CLEANUPALL();

     some_func2();
  }
  CLEANUPBEG(, 0)
  {
     clean4();
  }
  CLEANUPNEXT(, 3)
  {
     clean3();
  }
  CLEANUPNEXT(, 2)
  {
     clean2();
  }
  CLEANUPNEXT(, 1)
  {
     clean1();
  }
  CLEANUPEND();

  return 0;
}

Интересно узнать кто как решает данную проблему в языке си? init - берет ресурс, clean - освобождает ресурс.
PS: Раньше на си серьезно не писал, а писал на с++ и что то слишком много макросов начал использовать.

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

#ifndef _CLEANUPBLK_H
#define _CLEANUPBLK_H

#define CLEANUPBLK(name) \
__##name##_cleanupblk:

#define CLEANUPBEG(name) \
__##name##_cleanupall:

#define CLEANUPNEXT(name, val) \
__##name##_cleanup_##val:

#define CLEANUPEND(name) \
goto __##name##_cleanupend; \
goto __##name##_cleanupblk; \
goto __##name##_cleanupall; \
__##name##_cleanupend:

#define CLEANUPALL(name) \
goto __##name##_cleanupall

#define CLEANUPBRK(name) \
goto __##name##_cleanupend

#define CLEANUPSEL(name, val) \
goto __##name##_cleanup_##val

#endif // _CLEANUPBLK_H

Только goto

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

Тепер пользователю будет труднее допустить ошибку:

#ifndef _CLEANUPBLK_H
#define _CLEANUPBLK_H

#define CLEANUPBLK(name) \
goto  __CLEANUPBLK_##name; \
goto __CLEANUPBEG_##name; \
goto __CLEANUPEND_##name; \
__CLEANUPBLK_##name:

#define CLEANUPBEG(name) \
goto __CLEANUPBEG_##name; \
goto  __CLEANUPBLK_##name; \
goto __CLEANUPEND_##name; \
__CLEANUPBEG_##name:

#define CLEANUPNEXT(name, val) \
__CLEANUPNEXT_##name##_##val:

#define CLEANUPEND(name) \
goto __CLEANUPEND_##name; \
goto __CLEANUPBLK_##name; \
goto __CLEANUPBEG_##name; \
__CLEANUPEND_##name:

#define CLEANUPALL(name) \
goto __CLEANUPBEG_##name

#define CLEANUPBRK(name) \
goto __CLEANUPEND_##name

#define CLEANUPSEL(name, val) \
goto __CLEANUPNEXT_##name##_##val

#endif // _CLEANUPBLK_H

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

Кажется я нашел:

#ifndef _CLEANUPBLK_H
#define _CLEANUPBLK_H

#define CLEANUPBLK(name) \
goto  __CLEANUPBLK_##name; \
goto __CLEANUPBEG_##name; \
goto __CLEANUPEND_##name; \
__CLEANUPBLK_##name: \
{

#define CLEANUPBEG(name) \
} \
goto __CLEANUPBEG_##name; \
goto  __CLEANUPBLK_##name; \
goto __CLEANUPEND_##name; \
__CLEANUPBEG_##name: \
{

#define CLEANUPNEXT(name, val) \
} \
__CLEANUPNEXT_##name##_##val: \
{

#define CLEANUPEND(name) \
} \
goto __CLEANUPEND_##name; \
goto __CLEANUPBLK_##name; \
goto __CLEANUPBEG_##name; \
__CLEANUPEND_##name:

#define CLEANUPALL(name) \
goto __CLEANUPBEG_##name

#define CLEANUPBRK(name) \
goto __CLEANUPEND_##name

#define CLEANUPSEL(name, val) \
goto __CLEANUPNEXT_##name##_##val

#endif // _CLEANUPBLK_H

Devix
() автор топика

1. Можно поизвращаться с setjmp/longjmp, эмулировать на них исключения
2. RAII, http://en.wikipedia.org/wiki/RAII
В Си это будет автоматические переменные, на стеке, блок { auto int x = ...} по которому автоматически автоматические переменные самоуничтожатся
3. Может, что-то можно придумать с GCC расширением про адресуемые метки, "labels as values":
---------------8<---------------------
typedef void *Inst;
void engine()
{
static Inst program[] = { &&add /* ... */ };
Inst *ip = program;
int *sp;
goto *ip++; /* dispatch first VM inst. */
add:
sp[1]=sp[0]+sp[1];
sp++;
goto *ip++; /* dispatch next VM inst. */
}
====================8<===============
Figure 1: Direct threaded code using GNU C’s “labels as values”

то есть, у тебя будет код вроде:


(подставляется в что-то вроде:)

CLEANUP(LB1,1)
{ auto STATUS result1=init1( ... );
if (!result1) {goto LB1}
...
LB1:
clean1();
}

static Inst labels[] = { NULL, &&LB1, &&LB2 /* ... */ };

CLEANUP(LB1,1)
{ auto STATUS result1=init1(..);
if (!result1) {goto labels[1]};
...
LB1:
clean1();
}

resultN, labels[N] (а не *curlabel++) -- чтобы сохранялась прозрачность абстракции макры, чтобы можно было вкладывать их друг в друга (CLEANUP в CLEANUP).
В принципе, CLEANUP можно обернуть другой макрой, которая будет это N корректно вести по вложенности

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

У меня их тоже можно вкладывать, но прийдется вызывать все макры с именем блока. CLEANUPBLK(main) { CLEANUPBLK(test) { } CLEANUPBEG(test) { } CLEANUPEND(test) } CLEANUPBEG(main) { ... } CLEANUPEND(main)

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

У меня их тоже можно вкладывать, но прийдется вызывать все макры с именем блока.
CLEANUPBLK(main)
{
  CLEANUPBLK(test)
  {
  }
  CLEANUPBEG(test)
  {
  }
  CLEANUPEND(test)
}
CLEANUPBEG(main)
{
 ...
}
CLEANUPEND(main)

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

соответственно, идея в том, что при хороших макрах в духе DRY (Don't Repeat Yourself) эти LBN и N в static Inst labels[] = { NULL, ...} и CLEANUP(LB##N,N) не будут создаваться руками, а какой-то другой макрой, чтобы нельзя было сделать дурацкую ручную ошибку.

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

а resultN тоже организовать в массив. Тогда можно проимитировать исключения, выдать диагностику result[N] по которой видно, где что успело проинициализироваться, а где нет.

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

Спасибо тебе за помощь вот результат поддерживаются вложения:

#ifndef _CLEANUPBLK_H
#define _CLEANUPBLK_H

#include <errno.h>

#define CLEANUPBLK(name) \
do \
{ \
void *__cleanupblklabel = &&__cleanupblk_##name; \
goto __cleanupblk_##name##_do; \
__cleanupblk_##name: break; \
__cleanupblk_##name##_do:

#define CLEANUPBEG \
errno = 0; \
} \
while (0);  \
switch (errno) \
{ \
	case -1: \
		break; \
	case 0: \
	{

#define CLEANUPNEXT(val) \
	} \
	case val: \
	{

#define CLEANUPEND \
	} \
}

#define CLEANUPALL \
errno = 0; \
goto *__cleanupblklabel

#define CLEANUPBRK \
errno = -1; \
goto *__cleanupblklabel

#define CLEANUPSEL(val) \
errno = val; \
goto *__cleanupblklabel

#endif // _CLEANUPBLK_H

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

.h

#ifndef _CLEANUPBLK_H
#define _CLEANUPBLK_H

extern int __cleanupblk_exit_code;

#define CLEANUPBLK(name) \
do \
{ \
void *__cleanupblk_break = &&__cleanupblk_##name##_break; \
goto __cleanupblk_##name##_do; \
__cleanupblk_##name##_break: break; \
__cleanupblk_##name##_do:

#define CLEANUPBEG \
__cleanupblk_exit_code = 0; \
} \
while (0);  \
switch (__cleanupblk_exit_code) \
{ \
	case -1: \
		break; \
	case 0: \
	{

#define CLEANUPNEXT(val) \
	} \
	case val: \
	{

#define CLEANUPEND \
	} \
}

#define CLEANUPALL \
{ \
__cleanupblk_exit_code = 0; \
goto *__cleanupblk_break; \
}

#define CLEANUPBRK \
{ \
__cleanupblk_exit_code = -1; \
goto *__cleanupblk_break; \
}

#define CLEANUPSEL(val) \
{ \
__cleanupblk_exit_code = val; \
goto *__cleanupblk_break; \
}

#endif // _CLEANUPBLK_H

.c
#include "cleanupblk.h"

int __cleanupblk_exit_code;

Исправил маленькие недочеты, кажется тепер все. Я счастлив :))))

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

CLMETD(modbus_rtu, bool, run, (self, bool support_restart))
{
	bool result = false;
	void *old_int_handler;
	void *old_pipe_handler;
	tcp_serv_t tcp_serv;
	rt_t rt;
	rt_timer_t rt_timer;
	sigset_t old_sigset;
	
	self->clockid = clocktype2clockid(self->clock_type, NULL);

	CLEANUPBLK()
	{
		if (support_restart && (old_int_handler = signal(SIGINT, modbus_rtu_stop_handler)) == SIG_ERR)
		{
			SYSLOG_ERRNO_FL(LOG_ERR, "signal error");
			CLEANUPBRK;
		}

		if ((old_pipe_handler= signal(SIGPIPE, SIG_IGN)) == SIG_ERR)
		{
			SYSLOG_ERRNO_FL(LOG_ERR, "signal error");
			CLEANUPSEL(1);
		}

		if (!modbus_rtu_ports_open(self))
			CLEANUPSEL(2);

		if (!tcp_serv_start(&tcp_serv, self->listen_port, self->max_connection, POLLIN | POLLOUT | POLLERR,
							true, self, NULL, modbus_rtu_client_handler, modbus_rtu_pre_handler, NULL))
			CLEANUPSEL(3);

		if (!rt_enable(&rt, schedpolicy2int(self->sched_policy, NULL),
					   self->rt_sched_priority, PREFAULT_STACK_SIZE))
			CLEANUPSEL(4);

		if (!rt_timer_create(&rt_timer, self->clockid, SIGALRM, modbus_rtu_alarm_handler))
			CLEANUPSEL(5);

		if (!rt_timer_sigblock(&rt_timer, &old_sigset))
			CLEANUPSEL(6);

		struct itimerspec itspc;
		itspc.it_value.tv_sec = 1;
		itspc.it_value.tv_nsec = 0;
		itspc.it_interval.tv_sec = 1;
		itspc.it_interval.tv_nsec = 0;

		if (!rt_timer_start(&rt_timer, &itspc))
			CLEANUPALL;

		// do work
		modbus_rtu_work = 1;
		while (modbus_rtu_work == 1)
		{
			if (!rt_timer_sigsuspend(&rt_timer, &old_sigset))
				break;

			int count = tcp_serv_poll(&tcp_serv, tcp_serv.active_conn == 0 ? -1 : 0);
			if (count == -1)
				break;
			if (count > 0)
				SYSLOG_FL(LOG_ERR, "poll not full handling, not handlied socket count = %d",, count);
		}
		if (modbus_rtu_work != 1)
			result = true;
	}
	CLEANUPBEG
	{
		rt_timer_sigunblock(&rt_timer, &old_sigset);
	}
	CLEANUPNEXT(6)
	{
		rt_timer_destroy(&rt_timer);
	}
	CLEANUPNEXT(5)
	{
		rt_disable(&rt);
	}
	CLEANUPNEXT(4)
	{
		tcp_serv_stop(&tcp_serv);
	}
	CLEANUPNEXT(3)
	{
		modbus_rtu_ports_close(self);
	}
	CLEANUPNEXT(2)
	{
		if (signal(SIGPIPE, old_pipe_handler) == SIG_ERR)
			SYSLOG_ERRNO_FL(LOG_ERR, "signal error");
	}
	CLEANUPNEXT(1)
	{
		if (support_restart && signal(SIGINT, old_int_handler) == SIG_ERR)
			SYSLOG_ERRNO_FL(LOG_ERR, "signal error");
	}
	CLEANUPEND;

	return result;
} // CLMETD(modbus_rtu, bool, run)

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

Вопервых goto может прыгнуть хот куда, а c моими инструкциями(CLEANUPALL, CLEANUPBRK, CLEANUPSEL) мы ограничены тем блоком в котором мы их вызываем. Да и при использовании вложенных блоков гораздо трудьнее допустить ошибки с именами меток.

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

Да, но использовать мне удобно.

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

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

Ты хочешь сказать, то ошибиться с циферкой в CLEANUPSEL сложнее, чемс читабельным именем метки в goto? Да ты робот, по ходу.

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

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

> 1. void *__cleanupblk_break = &&__cleanupblk_##name##_break;
..
> 2. goto *__cleanupblk_break; \


сдаётся, goto "label as value" тут не очень нужен.

1. что будет, если попадутся вложенная часть 1.?
то есть, не A.1 A.2 B.1 B.2, а A.1 B.1 B.2 A.2 ?
(B.1 перезатирает внутреннюю переменную в макре A.1)

2. помни, что это GCC-расширение, и код становится немобильным на другие С компиляторы.

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

> enum или #define должны помочь не роботам.

Количество писанины растет, а выгод как-то не прибавляется %) Насчет блочной дисциплины твоих макросов - перепутать местами CLEANUPSEL мложно, так ведь? И компилятор этого не заметит, как и в случае с goto.

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

не надо им вообще имена давать вручную.
CLEANUPBLK()
{ {
CLEANUPBRK;
CLEANUPSEL(1);
{ CLEANUPSEL(2);
}

CLEANUPSEL(3);

CLEANUPSEL(4);
CLEANUPSEL(5);

CLEANUPSEL(6);

..
CLEANUPALL;

// do work
CLEANUPBEG
CLEANUPNEXT(6)
CLEANUPNEXT(5)
CLEANUPNEXT(4)
CLEANUPNEXT(3)
CLEANUPNEXT(2)
CLEANUPNEXT(1)
CLEANUPEND;

return result;
} // CLMETD(modbus_rtu, bo


чёткий паттерн 12345677654321. Вот в макре другой его и посчитай, оберни эти CLEANUPSEL(N)/CLEANUPNEXT(N) в какой-то стек.

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

>1. что будет, если попадутся вложенная часть 1.?
то есть, не A.1 A.2 B.1 B.2, а A.1 B.1 B.2 A.2 ?
(B.1 перезатирает внутреннюю переменную в макре A.1)

Я основывался на этом принципе:
{
  void *label <------
                    |
  ...----------------
  {                 |
    void *label<--  |
                 |  |
    goto *label---  |
  }                 |
  goto *label -------
}
если они верен то значит должно быть все впорядке

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

>чёткий паттерн 12345677654321. Вот в макре другой его и посчитай, оберни эти CLEANUPSEL(N)/CLEANUPNEXT(N) в какой-то стек.

Да для данного конкретного случая скорее всего сделаю какой нибудь макрос.

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

На всякий случай замечу что этот исходник я переделал как только сделал данные макросы и это еще не конечный результат.

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

> Я просто хочу выяснить нужны они мне или нет.

Если они вообще кому-то и нужны, то только тебе одному, ибо ты их автор. Все остальные пользуются структурным goto для случая отдельной функции, пулом (в стиле APR, например) для стека вызовов, или конечным автоматом. А реализовать нормальные "локальные исключения" тебе не позволит ограниченность препроцессора Си.

tailgunner ★★★★★
()

У меня только один вопрос. goto -- переход безусловный? В чем тогда смысл нескольких последовательных goto в макросе CLEANUPEND?

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

Сначало лишние goto ипользовал чтобы компилятор не кричал что есть неиспользованные метки, а потом для чтобы компилятор предупреждал когда я забуду одно из слов (CLEANUPBLK, CLEANUPBEG, CLEANUPNEXT, CLEANUPEND)

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

m4 не подойдёт? Более мощный препроцессор. Код сейчас не приведу, не
 помню. Но общая идея - можно сделать блок кода аргументом макроса. 
Т.е., задать что-то на входе и на выходе. 

Также нужно будет помнить, что нельзя внутри блока пользоваться 
longjmp (или как оно там называется). А goto и return, возможно, 
стоит перекрыть. 

В применении это будет выглядеть примерно так:

PROTECTED_BLOCK({ код инициализации 1},
  { 
  защищённый код уровня 1; 
  return;  // перекрыт, чтобы выполнять раскрутку
  PROTECTED_BLOCK({ код инициализации 2},
                    {защищённый код уровня 2},
                    {откат уровня 2})
  },
{ откат уровня 1 })

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

>:))) Вот это торкнуло :) Оно зачем а? Коллег врагами делать чтоли?

Откуда вы такие беретесь? :)))

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

По его словам я бы не сказал что он мне коллега

Devix
() автор топика

Я так и не понял до конца, что тебе надо, но видимо проще всего здесь юзать гнутое расширение ({ })

#define WITH(res, arg, block) { res* resource=init_res(arg); if(!res) goto end; block free_res(res*); end: }

Использование:

WITH(my_res, 123, 
  ({ 
     int i=0; 
     while( i<100 ) {
        WITH( res2, 456, ({  че-то еще })
        ... ну и так далее
     }
  })
)

И еще, чтобы везде ставить ; использовать известный трюк do{}while(0);

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

Кстати, интел это расширение держит, насчет меток как целых чисел -- не знаю.

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