2014-08-27 2 views
3

Я пытаюсь включить UTF-8 идентификаторов в случае переключателя, но я получаю эту ошибку:Ограничения в случаях коммутатора в случае диапазона

Error: had 399 cases which is more than 256 cases in case range

D код:

switch(value) 
    { 
     case 'a': .. case 'z': 
     case 'A': .. case 'Z': 
     case 0xC0: .. case 0x24F: 

Почему компилятор налагает такое ограничение? для оптимальных целей? могу ли я его преодолеть?

+0

Каков код, выполняемый в каждом случае? –

+0

в противном случае таблица переходов становится слишком большой. –

+0

Переключайте 'switch' на диапазоны с помощью операторов' if', поэтому вы имеете только 64 оператора 'case' для' switch'. –

ответ

5

Выполнение этого как ответа, чтобы получить больше места.

Ограничение вы видите здесь не в спецификации http://dlang.org/statement и относится только к CaseRangeStatements в компиляторе: SRC/DMD/statement.c линии 3437 в версии у меня есть:

if (lval - fval > 256) 
{ error("had %llu cases which is more than 256 cases in case range", lval - fval); 
    lval = fval + 256; 
} 

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

 switch(value) 
{ 
    case 'a': .. case 'z': 
    case 'A': .. case 'Z': 
    case 0xC0: .. case 0x14F: // this compiles 
    case 0x150: .. case 0x24F: // since it is broken up 

Затем вы обращаетесь с ними так же.

Источник компилятор не говорит, почему он имеет такую ​​проверку, но история GitHub говорит, что это было совершено в ответ на эту ошибку: https://issues.dlang.org/show_bug.cgi?id=3139

[s] Так что это деталь реализации в компиляторе, чтобы избежать бесконечный цикл. [/ s]

EDIT: на самом деле проверка 256 была до этого, я неправильно прочитал патч, Дон добавил к нему чек. Похоже, что 256 штук предшествует github, поэтому я не знаю, почему он был специально добавлен, но я все еще довольно уверен, что он связан с этим циклом и проблемами памяти, а также с деталями и ошибками компилятора.

+0

Разбить его на несколько частей - это хорошее решение. Благодарю. Я сам буду изучать, почему. Я думал, что компилятор dmd действительно генерирует таблицу перехода как можно более возможную (например, оптимизацию C), в противном случае она генерирует код так же, как и if-else-if. Хотя я не проверял сборку. – Jack

+0

Кроме того, я собираюсь прочитать вашу книгу D. :) – Jack

+0

(я забыл принять ваш ответ, извините) – Jack

0

Коммутатор D может содержать до 256 корпусов. Вы должны изменить весь способ сделать это.

if (value >= 'a' && value <= 'z') 
{ 
    // ... 
} 
else if (value >= 'A' && value <= 'Z') 
{ 
    // ... 
} 
else if (value >= 0xC0 && value <= 0x24F) 
{ 
    // ... 
} 

Или как это, если вы хотите обрабатывать все случаи одинаково.

if (value >= 'a' && value <= 'z' || value >= 'A' && value <= 'Z' || value >= 0xC0 && value <= 0x24F) 
{ 
    // ... 
} 
2

Альтернативный способ избавиться от ограничения - использовать массив делегата и выбрать, какой метод вызывать с использованием символа в качестве селектора. Тогда ваш переключатель будет выглядеть примерно так:

void main(string args[]) 
{ 
    alias caseProc = void delegate(); 
    caseProc[1024] callTable; 

    void numberProc(){} 
    void latinProc(){} 
    void otherProc(){} 

    for(auto i = 0; i < callTable.length; i++) 
    { 
     if (i >= '0' && i <= '9') 
      callTable[i] = &numberProc; 
     else if ((i >= 'a' && i <= 'z') | (i >= 'A' && i <= 'Z')) 
      callTable[i] = &latinProc; 
     else 
      callTable[i] = &otherProc; 
    } 
} 
+0

Это будет иметь значительную потерю производительности. – Bauss

+0

вместо (не помню, что генерируется для вычисления offs), тогда 'JMP offs' у вас есть что-то вроде MOV reg, CharValue'; 'ADD reg, tableOffset'; (необязательные параметры params); 'CALL reg'. Немного медленнее, но если коммутатор не используется в критическом для производительности месте, то вы можете себе это позволить. ANd, очевидно, таблица должна быть инициализирована в конструкторе ... не каждый раз, когда он используется ... –