C11 и вычисление постоянного выражения в метках переключателя

после этого вопроса Почему gcc не разрешает const int как выражение case?, в основном то же, что и Какие продвинутые типы используются для сравнения выражений switch-case? или Есть ли способ использовать постоянный массив с постоянным индексом в качестве метки case-переключателя в C?.

По первой ссылке я попытался заменить:

case FOO:                       // aka 'const int FOO = 10'

с участием :

case ((int) "toto"[0]):         // can't be anything *but* constant

Который дает :

https://ideone.com/n1bmIb -> https://ideone.com/4aOSXR = работает на C ++

https://ideone.com/n1bmIb -> https://ideone.com/RrnO2R = не работает на C

Я не совсем понимаю, поскольку строка «toto» не может быть чем-либо кроме константой, это даже не переменная, она лежит в пустоте памяти компилятора. Я даже не играю с нечеткой логикой 'const' языка C (которая на самом деле означает «только для чтения, а не константа, чего вы ожидали?»), Проблема либо в «доступе к массиву», либо в «ссылках на указатели» "в постоянное выражение, которое не вычисляется в C, но неплохо работает в C ++.

Я ожидал использовать этот «трюк», чтобы использовать HASH_MACRO (str) для генерации уникальных значений меток case из ключевого идентификатора, оставляя в конечном итоге компилятор выдавать ошибку в случае коллизии из-за найденных аналогичных значений меток.

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

Вопрос в том, есть ли «расширение» для C11, которое позволяет этой штуке «toto» работать в GCC, CLANG и ... MSVC? Я не хочу идти по пути C ++ (форвардные объявления typedef больше не работают) и потому, что встроенные вещи (отсюда и вычисление хэша во время компиляции для пространственно-временного искажения).

Есть ли промежуточный язык «C +», который более «разрешительный» и «понимающий» внедрен немного лучше, например, -Praise the Lords- «перечисляет как члены битового поля», среди других приятных вещей, которых мы не можем иметь (из-за отсутствия - стандарты реальности развиваются как улитки под солнцем пустыни)?

#provemewrong, #changemymind, #norustplease


person Kochise    schedule 24.05.2018    source источник
comment
Я не уверен, почему это было отклонено - особенно без комментариев. На самом деле это интересный вопрос, на который требуется некоторое размышление и знание стандарта C. Тот, кто еще не знает, почему это не работает, не будет знать, где искать ответ.   -  person Andrew Henle    schedule 24.05.2018
comment
Ответ TL; DR будет: потому что определение целочисленного постоянного выражения в C некорректно. В C ++ это исправили.   -  person Lundin    schedule 24.05.2018
comment
@Lundin: они могли бы исправить C с тех пор, как стандарт развился, некоторые компиляторы добавили расширения. Я имею в виду, что это настолько элементарно, что я не могу понять, что они отказались от этого в C. Это не ракетостроение, и затем мы должны выкопать сокровищницу метапрограммирования препроцессора, чтобы смоделировать желаемый эффект. Или нет. Почему что-то вроде M4 или даже более легкая макро-оценка, такая как производная от LISP, не включена в предварительную обработку или даже в компилятор? Я имею в виду, сколько раз мне приходилось полагаться на этап предварительной обработки для сортировки массивов, в то время как constexp qsort (myshit) должен был помочь.   -  person Kochise    schedule 24.05.2018
comment
@AndrewHenle Я заметил, что пользователь, почти не имеющий репутации, очень быстро получает отрицательное голосование, в то время как пользователи с более высокой репутацией [~ ›2k] - даже за вопрос, который я задаю себе - быстро получают голосование« за ». Я проголосовал за этот вопрос, потому что считаю, что он хорош и что-то еще по сравнению с некоторыми вопросами для начинающих.   -  person Peter VARGA    schedule 24.05.2018
comment
@Al Bundy: спасибо, я не часто использую stackoverflow, так как считаю его ... переполненным базовыми вопросами. С другой стороны, у меня довольно хороший послужной список на Codeproject, так что мне часто везет там. Но поскольку они больше специализируются на C ++ или C #, а мой вопрос касается конкретно C, я пришел сюда. И меня не волнуют ragedownvotes, это не влияет на мою жизнь, при условии, что такие хорошие парни, как вы, более продуманы и конструктивны.   -  person Kochise    schedule 24.05.2018
comment
@AndrewHenle Я не голосовал против него, но если бы я это сделал, я бы не проголосовал против него из-за низкой репутации, а из-за всей личной разглагольствования во второй половине и из-за отсутствия надлежащего обоснования того, почему это нужно быть именно таким.   -  person Antti Haapala    schedule 24.05.2018
comment
@Antti Haapala Поскольку константный строковый литерал следует рассматривать как константу, поскольку строки и обращения к массиву уже являются частью стандарта C, это сбивает с толку, что они недоступны из любого места в пределах языка. Из-за отсутствия ортогональности с этим довольно сложно справиться. Я имею в виду, я просто хотел перенести трюк с макросами C ++ на C, и это простое ограничение не позволяет мне этого сделать. Некоторые вещи должны были развиваться равномерно между C и C ++, чтобы сохранить некоторую согласованность между этими двумя, но кажется, что они все больше и больше расходятся на два совершенно разных, одинаковых по внешнему виду воплощения. Вздох.   -  person Kochise    schedule 25.05.2018
comment
В общем, вопросы и комментарии Stack Overflow - не лучшее место для манифеста. Предлагаю вам написать об этом статью в другом месте.   -  person Antti Haapala    schedule 25.05.2018
comment
Добавление ссылки на возможное решение, но не: stackoverflow.com/questions/6190963/   -  person Kochise    schedule 25.05.2018
comment
@Antti Haapala Сам по себе не манифест, но его стоит прочитать blog.robertelder.org/ Кстати, спасибо за помощь и решение, которое вы предоставили, хотя это не меняет мою проблему.   -  person Kochise    schedule 25.05.2018
comment
Проблемы, близкие к моим, для справки: stackoverflow.com/questions/20121278/ и stackoverflow.com/questions/45848866/   -  person Kochise    schedule 25.05.2018


Ответы (3)


Не имеет значения, может ли это быть известно компилятору во время компиляции. Метка case должна иметь значение, которое является целочисленным постоянным выражением (C11 6.8.4.2p3).

  1. Выражение каждой case метки должно быть целочисленным константным выражением, и никакие два из константных выражений case в одном и том же операторе switch не должны иметь одинаковое значение после преобразования. В операторе switch может быть не более одной метки по умолчанию. (Любой заключенный оператор switch может иметь метку по умолчанию или выражения константы case со значениями, которые дублируют выражения констант case во включающем операторе switch.)

А определение целочисленного постоянного выражения: в C11 6.6p6:

  1. Целочисленное постоянное выражение должно иметь целочисленный тип и содержать только операнды, которые являются целочисленными константами, константами перечисления, символьными константами, sizeof выражениями, результатами которых являются целочисленные константы, _Alignof выражениями и плавающими константами, которые являются непосредственными операндами приведения типов. Операторы приведения в целочисленном постоянном выражении должны преобразовывать только арифметические типы в целочисленные типы, кроме как части операнда в оператор sizeof или _Alignof.

Поскольку "toto" не является ни одной из целочисленных констант, констант перечисления, символьных констант, констант sizeof, _Alignof выражений или констант с плавающей запятой, приведенных к целому числу; и этот список был указан в разделе ограничений стандарта, компилятор не должен передавать его без уведомления. (Даже соответствующий компилятор может успешно скомпилировать программу, но он должен диагностировать это как нарушение ограничения.)


Вы можете использовать цепочку ? : для преобразования индекса в символьную константу, т.е.

  x == 0 ? 't' 
: x == 1 ? 'o'
: x == 2 ? 't'
: x == 3 ? 'o'

Это можно записать в макрос.

person Antti Haapala    schedule 24.05.2018
comment
это не может быть объявлено в макросе, поскольку HASH_MACRO (str) генерирует что-то вроде этого: case ((((('\ 0' == toto [0])? ((cnU32) 0): toto [0] + ( (('\ 0' == toto [0 +1])? (65599ULL): toto [0 +1] + ((('\ 0' == toto [0 +1 +1])? (65599ULL): toto [0 +1 +1] + ((('\ 0' == toto [0 +1 +1 +1])? (65599ULL): toto [0 +1 +1 +1] + ((('\ 0 '== toto [0 +1 +1 +1 +1])? (65599ULL): toto [0 +1 +1 +1 +1] + (((' \ 0 '== toto [0 +1 + 1 +1 +1 +1])? (65599ULL): toto [0 +1 +1 +1 +1 +1] + ((('\ 0' == toto [0 +1 +1 +1 +1 + 1 +1])? (65599ULL): toto [0 +1 +1 +1 +1 +1 +1] + ... - person Kochise; 24.05.2018
comment
Вам нужно указать отдельные символы в качестве аргументов. - person Antti Haapala; 24.05.2018
comment
Вам нужно указать отдельные символы в качестве аргументов. -> Нет, просто нет. Мы находимся в 2018 году, некоторые вещи должны были быть улучшены за последние 40 лет, и обработка строк в препроцессоре и компиляторе должна быть единым целым. Даже Python3 улучшился в этом отношении. - person Kochise; 24.05.2018
comment
Вы можете обсудить это с комитетом по стандартам C. - person Christian Gibbons; 24.05.2018
comment
@Christian Gibbons: только если нужно выиграть Нобелевскую премию. Нет, серьезно, я имею в виду, что эту функцию C ++ не удастся перенести на C. Это было бы слишком очевидно. И если бы я когда-либо пытался, то не раньше C39. - person Kochise; 24.05.2018
comment
@Kochise Изменение этого параметра меняет фундаментальный способ компиляции некоторых вещей. Дело не в том, что функции переносятся на C, а в том, что C имеет определенное поведение. - person Thomas Jager; 24.05.2018
comment
@Thomas Jager Я не хочу, чтобы унаследованное поведение C было изменено наизнанку, мне просто нужно, чтобы препроцессор и компилятор C использовали строку и массив [x] в оценке константных выражений, а не только целочисленные константы. Это добавление функции, а не изменение того, что уже работает, на что-то еще. Ничего особенного. - person Kochise; 25.05.2018
comment
В скомпилированном двоичном файле C один и тот же строковый литерал const char * даже не всегда будет одним и тем же адресом памяти, обычно он будет ... но вы можете установить необычные флаги компилятора или связать отдельные объектные файлы и ветер с разными буквальными адресами для того, что выглядит как одна и та же строка. Я предполагаю, что это может быть одним из оправданий того, что стандарт не позволяет использовать буквальные строки в case операторах; для защиты компилятора от компоновщика и наоборот, и, к сожалению, разработчики C не предусмотрели последствий для метапрограммирования. - person szmoore; 11.05.2021
comment
@szmoore: Да, наверное. С тех пор я двинулся дальше. Решение заключается в использовании компилятора C ++ в качестве компилятора C, поскольку они также добавили назначенные инициализаторы (хотя и с дополнительными ограничениями, такими как порядок инициализации). - person Kochise; 19.07.2021

"toto[0]" не является целочисленным константным выражением, поскольку C определяет этот термин:

6.6 Constant expressions
...
6    An integer constant expression117) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof or _Alignof operator.
117) An integer constant expression is required in a number of contexts such as the size of a bit-field member of a structure, the value of an enumeration constant, and the size of a non-variable length array. Further constraints that apply to the integer constant expressions used in conditional-inclusion preprocessing directives are discussed in 6.10.1.

Онлайн-черновик C 2011 г.

person John Bode    schedule 24.05.2018

Проблема, с которой вы столкнулись, заключается в том, что в C «toto» представляет собой массив символов. Конечно, он постоянен в памяти, но это по-прежнему просто массив. Оператор [] индексирует массив (от указателя). При желании вы могли бы отредактировать скомпилированный двоичный файл и заменить строку «toto» на что-то другое. В некотором смысле время компиляции неизвестно. Это эквивалентно выполнению:

char * const ___string1 = "toto";
...
case ((int) ___string1[0]):

(Это немного принудительно и избыточно, но это просто для демонстрации)

Обратите внимание, что типом элементов строкового литерала является char, а не const char.

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

person Thomas Jager    schedule 24.05.2018
comment
Проблема в том, что я специально не хочу, чтобы строка toto была где-нибудь в моем двоичном файле. Мне просто это не нужно, мне просто нужен уникальный идентификатор (здесь метка case) без необходимости поддерживать большое перечисление, которое, с другой стороны, является C-совместимым, поскольку считается целочисленной константой. - person Kochise; 24.05.2018
comment
@Kochise Это не то, как работает C. Вы говорите ему, что хотите, чтобы этот массив char существовал. Каждый раз, когда вы используете строковый литерал, вы действительно используете указатель. Перечисления и их именованные значения по сути являются определениями целочисленных типов и # определениями целочисленных констант. Они совсем не особенные. - person Thomas Jager; 24.05.2018
comment
Тогда скажите мне, если я ошибаюсь, но строковые литералы являются частью стандарта C, препроцессор может даже сопоставить их (Hello World!), Так что их можно просканировать. Опять же, доступ к массиву также является частью языка, я не понимаю, почему он недоступен из препроцессора или даже во время компиляции для обработки константных выражений. Я имею в виду, это не значит, что целевых функций вообще нет. И то, что C был сделан таким образом, не должно препятствовать его развитию и упрощению процесса разработки, вместо того, чтобы оставаться в темных веках компьютерных языков. - person Kochise; 25.05.2018
comment
Сопоставление строк - это функция препроцессора. Это нормально, потому что, поскольку строки являются указателями, размещение двух строк рядом - это не то, что вы можете скомпилировать. Препроцессор не заботится о том, что вы находитесь в корпусе коммутатора, это не его работа. - person Thomas Jager; 25.05.2018
comment
Да, хорошо, тогда скажите мне, почему вы не можете явно указывать вещи в препроцессоре (например, '((char) toto + 2)' или 'toto [2]'), но препроцессор может сделать это неявно, как в упомянутой вами сортировке? Подобные вещи доступны в C ++ 11, но не в C11, хотя и не нацелены на конкретные вещи C ++ (классы, шаблоны, что угодно) и должны были быть обратно перенесены в C11, чтобы, по крайней мере, иметь иллюзию согласованности между двумя языками. Я знаю, я жалуюсь, но то, что такие комитеты по нормализации собираются регулярно и не могут решать реальные проблемы, это ... - person Kochise; 25.05.2018