«Локальные» метки в C и реализация таблицы переходов - PullRequest
4 голосов
/ 21 января 2020

Я пытаюсь создать таблицу переходов на основе макросов в C.

Вот пример кода:

#include "stdio.h"

#define GOTO(X) static void* caseArg[] = {&&I0, &&R0, &&S0, &&F0, &&G0, &&H0}; \
    goto *caseArg[X];

#define FINISH() goto caseEnd;

int main(int argc, char** argv) {

    GOTO(1);

    I0: printf("in I0\n"); FINISH();
    R0: printf("in R0\n"); FINISH();
    S0: printf("in R0\n"); FINISH();
    F0: printf("in R0\n"); FINISH();
    G0: printf("in R0\n"); FINISH();
    H0: printf("in R0\n"); FINISH();

    caseEnd:;

}

Возможные метки (I0, R0, et c) должны быть одинаковыми.

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

Чего я хочу достичь:

int main(int argc, char** argv) {

     { // scope 1 

        GOTO(1);

        I0: printf("in I0\n"); FINISH();
        R0: printf("in R0\n"); FINISH();
        S0: printf("in R0\n"); FINISH();
        F0: printf("in R0\n"); FINISH();
        G0: printf("in R0\n"); FINISH();
        H0: printf("in R0\n"); FINISH();

        caseEnd:;

    }

    { // scope 2

        GOTO(4);

        I0: printf("in I0\n"); FINISH();
        R0: printf("in R0\n"); FINISH();
        S0: printf("in R0\n"); FINISH();
        F0: printf("in R0\n"); FINISH();
        G0: printf("in R0\n"); FINISH();
        H0: printf("in R0\n"); FINISH();

        caseEnd:;

    }

}

Есть идеи? Любой возможный обходной путь?

Ответы [ 4 ]

8 голосов
/ 21 января 2020

Вам необходимо расширение __label__ (по крайней мере, в g cc, clang и small cc), которое позволяет размещать метки в блоке.

Метки должны быть объявлены в самом начале блока с

__label__ I0, R0, S0, F0, G0, H0;

(Смежный __label__ I0; __label__ R0; ... или сочетание двух форм также работает.).

Если не объявлен локальный объем с __label__ , C метки охватываются функцией включения.

Ваш пример с __label__:

#include "stdio.h"

#define GOTO(X) static void* const caseArg[] = {&&I0, &&R0, &&S0, &&F0, &&G0, &&H0}; \
    goto *caseArg[X];

#define FINISH() goto caseEnd;

#define DECL_LBLS() __label__ I0, R0, S0, F0, G0, H0, caseEnd

int main(int argc, char** argv) {

    {   DECL_LBLS();

        GOTO(2);

        I0: printf("in I0\n"); FINISH();
        R0: printf("in R0\n"); FINISH();
        S0: printf("in S0\n"); FINISH();
        F0: printf("in F0\n"); FINISH();
        G0: printf("in G0\n"); FINISH();
        H0: printf("in H0\n"); FINISH();
        caseEnd:;
    }

    {   DECL_LBLS();

        GOTO(1);

        I0: printf("in I0\n"); FINISH();
        R0: printf("in R0\n"); FINISH();
        S0: printf("in S0\n"); FINISH();
        F0: printf("in F0\n"); FINISH();
        G0: printf("in G0\n"); FINISH();
        H0: printf("in H0\n"); FINISH();
        caseEnd:;
    } 
}

https://gcc.godbolt.org/z/63YSkG

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

1 голос
/ 21 января 2020

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

#include "stdio.h"

// Helpers
#define CONCAT(a, b) CONCAT2(a, b)
#define CONCAT2(a, b) a ## b

// Label redirection
#define I0 CONCAT(PREFIX, I0)
#define R0 CONCAT(PREFIX, R0)
#define S0 CONCAT(PREFIX, S0)
#define F0 CONCAT(PREFIX, F0)
#define G0 CONCAT(PREFIX, G0)
#define H0 CONCAT(PREFIX, H0)
#define caseEnd CONCAT(PREFIX, caseEnd)

#define GOTO(X) static void* caseArg[] = {&&I0, &&R0, &&S0, &&F0, &&G0, &&H0}; \
    goto *caseArg[X];

#define FINISH() goto caseEnd;

int main(int argc, char** argv) {

     { // scope 1 
        #define PREFIX SCOPE1
        GOTO(1);

        I0: printf("in I0\n"); FINISH();
        R0: printf("in R0\n"); FINISH();
        S0: printf("in R0\n"); FINISH();
        F0: printf("in R0\n"); FINISH();
        G0: printf("in R0\n"); FINISH();
        H0: printf("in R0\n"); FINISH();

        caseEnd:;
        #undef PREFIX

    }

    { // scope 2
        #define PREFIX SCOPE2
        GOTO(4);

        I0: printf("in I0\n"); FINISH();
        R0: printf("in R0\n"); FINISH();
        S0: printf("in R0\n"); FINISH();
        F0: printf("in R0\n"); FINISH();
        G0: printf("in R0\n"); FINISH();
        H0: printf("in R0\n"); FINISH();

        caseEnd:;
        #undef PREFIX
    }

}
0 голосов
/ 21 января 2020

Рассмотрите возможность добавления параметра scope в ваш макрос.

Это может привести к чему-то вроде этого:

#include "stdio.h"

#define GOTO(scope,X) static void* caseArg[] = {&&scope##_I0, &&scope##_R0, &&scope##_S0, &&scope##_F0, &&scope##_G0, &&scope##_H0}; \
    goto *caseArg[X];

#define FINISH(scope) goto scope##_caseEnd;

int main(int argc, char** argv)
{
  {
    GOTO(SCOPE_1, 1);

    SCOPE_1_I0: printf("in I0\n"); FINISH(SCOPE_1);
    SCOPE_1_R0: printf("in R0\n"); FINISH(SCOPE_1);
    SCOPE_1_S0: printf("in R0\n"); FINISH(SCOPE_1);
    SCOPE_1_F0: printf("in R0\n"); FINISH(SCOPE_1);
    SCOPE_1_G0: printf("in R0\n"); FINISH(SCOPE_1);
    SCOPE_1_H0: printf("in R0\n"); FINISH(SCOPE_1);

    SCOPE_1_caseEnd:;
  }
  {
    GOTO(SCOPE_2, 3);

    SCOPE_2_I0: printf("in I0\n"); FINISH(SCOPE_2);
    SCOPE_2_R0: printf("in R0\n"); FINISH(SCOPE_2);
    SCOPE_2_S0: printf("in R0\n"); FINISH(SCOPE_2);
    SCOPE_2_F0: printf("in R0\n"); FINISH(SCOPE_2);
    SCOPE_2_G0: printf("in R0\n"); FINISH(SCOPE_2);
    SCOPE_2_H0: printf("in R0\n"); FINISH(SCOPE_2);

    SCOPE_2_caseEnd:;
  }
}

Это не оптимально, но это будет работать в каждом компиляторе.

Обратите внимание, что в случае, если вызываемые блоки имеют тот же шаблон, что и в примере, показанном в вопросе, вы можете даже определить дополнительный макрос, который будет вызываться как FULL_MACRO(scope,X), так как все GOTO и FINISH звонки можно будет параметризировать.

0 голосов
/ 21 января 2020

Метки как значения могут использоваться только в том случае, если они скомпилированы с использованием gcc, так как это расширение g cc.

Так что он работает на 100% нормально.

https://godbolt.org/z/jQvssU

enter image description here

Ответ Второй вопрос - да, вы можете использовать разные функции: https://godbolt.org/z/aoA3XQ

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...