我需要编写一个自动机,我碰巧遇到了对计算goto的旧需求(ala fortran4 :)
我需要在一个可移植的ansi-C中进行编码。
我想远离“不要这么做”,远离longjmp/setjmp,远离嵌入式ASM(),远离非ansi-C扩展。
有人知道怎么做吗?
发布于 2017-05-07 22:15:33
正如我在评论中所说的,尽管你恳求不要使用goto以外的任何东西,但标准C没有什么可提供的。
适当地设计您的状态,并将指向它的指针传递给处理程序函数,以便它们进行修改。这样,处理程序就可以设置下一个要调用的函数。如下所示:
struct state;
typedef void state_func(struct state*);
#define NULL_ACTION_ADDRESS (state_func*)0
struct state {
state_func *action;
int value1;
int value2;
};
#define INIT_STATE { initial_action, -1, -1}
state_func initial_action;
state_func handle_a;
state_func handle_b;
int main(void) {
struct state s = INIT_STATE;
while(s.action != NULL_ACTION_ADDRESS) {
(*s.action)(&s);
}
return 0;
}
void initial_action(struct state* ps) {
ps->action = &handle_a;
}
void handle_a(struct state* ps) {
ps->action = &handle_b;
}
void handle_b(struct state* ps) {
ps->action = NULL_ACTION_ADDRESS;
}发布于 2017-05-08 12:11:44
我想我明白了,我回顾了关于这个主题的所有不同的主题,我开始同意没有ansi C解决方案的地方,但我找到了一种方法来做这件事,满足我的需求。我在stackoverflow上看到的所有解决方案都是基于‘获取’标签的地址,然后将其填充到一个表中,然后索引这个表并转到,这既是使用了gcc/clang非ansi扩展,也是使用了asm扩展。
今天晚上我又试了一次,得到了这个。
在名为cgoto.h的包含文件中,我有以下内容
#ifndef CGOTO_dcl
#define CGOTO_dcl(N) int CGOTO_##N
#define CGOTO_LE(l) l,
#define CGOTO_LG(l) case l:goto l;
#define CGOTO_def(N) \
if(0){typedef enum {N(CGOTO_LE)} N; CGOTO_##N: switch(CGOTO_##N)\
{N(CGOTO_LG) default:CGOTO_##N=0;goto CGOTO_##N;}}
#define CGOTO(N,i) CGOTO_##N=i; goto CGOTO_##N;
#endif用法是这样的
#include <stdio.h>
#include "cgoto.h"
int f(int x)
{ //...
CGOTO_dcl(gtb);
//...
# define gtb(L) L(l0) L(l1) L(l2)
CGOTO_def(gtb);
//...
CGOTO(gtb,x);
l0: printf("error\n");
return(0);
//...
l1:return(11);
l2:return(22);
l3:return(33);
}
int main()
{ printf("f(0)=%d f(1)=%d f(2)=%d,f(3)=%d\n",f(0),f(1),f(2),f(3));
}在这个实现中,跳转的成本是2个跳转和一个顺序的switch(),然后是可优化的。因此,与函数调用相比,这是合理的性能,性能略低于&&label解决方案,但代价是可移植性。
有了这个实现,标签代码(语义动作)不会被限制在switch()中,所以我们可以实现具有共享语义动作的跳转表。
索引被分配给一个本地goto_table_index,使得函数使用这个可重入的(多线程),尽管优化器可以完全删除这种临时分配。
跳转表中的第一个标签是“特殊的”(在这个实现中),因为它捕获了超出界限的索引,第一个标签是“错误”标签。如果你的代码是防弹的,也就是说,你不可能得到一个越界的索引,那么第一个标签就没有特定的语义。
CGOTO_dcl(gtb);声明跳转表'gtb‘自己的索引为一个自动整数,因此可重入。
# define gtb(L) L(l0) L(l1) L(l2)
CGOTO_def(gtb);定义一个名为gtb的跳转表,标签可以用L(标签)输入/删除,所以它非常方便,这本质上是象征性的,即标签是有意义的名称。在#define作为switch()的情况下,标签添加/取消通常意味着#define重新编号是一个问题。
可以将#define与CGOTO_def()分开,但将它们放在一起更有意义。尽管CGOTO_def()必须放在函数局部声明之后,因为它包含一个switch(),这是代码。
uniq跳转表可以在函数中的多个位置使用。
CGOTO(gtb,x);
...
CGOTO(gtb,y);一个标签可以在多个跳转表中输入
# define gtb1(L) L(l0) L(l1) L(l2)
CGOTO_def(gtb1);
# define gtb2(L) L(l0) L(l4) L(l5)
CGOTO_def(gtb2);总而言之,这看起来可能很丑陋,然而,跳转表的定义通过两行#定义和CGOTO_def()是可管理的、实用的、半高性能的和可移植的。
我们回到FTN4 :)
干杯,菲
https://stackoverflow.com/questions/43832336
复制相似问题