Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.134.104.173] |
|
Сообщ.
#1
,
|
|
|
Здравствуйте!
Внезапно столкнулся с проблемкой. Объявил класс со статическими константами: class foo { public: static const int alpha; static const int beta; }; const int foo::alpha = _XXX_SIMBOL_ALPHA; const int foo::beta = _XXX_SIMBOLBETA; Ну и тут чудеса. В функции main (из main.obj) switch( val ) { case foo::alpha: case foo::beta: break; default: break; } Компилятор ms vs 2015 сказал, что foo::alpha не является константой! Но когда я перенёс этот switch в конструктор foo (в foo.obj), то всё чудесно отработало. Что тут можно сделать? Что тут можно поправить? Как сказать switch, что у меня действительно константы? В инете наткнулся на описание схожих ситуаций. Вроде бы банальность. А решения нет. Проскакивала мысль на счёт constexpr. Но компилятор хочет, чтоб я ему сразу предоставил значение. Чего-то там как-то не так. |
Сообщ.
#2
,
|
|
|
Несколько странный вопрос. Ибо: компилирует, скажем, компилятор такой левый файл с таким левым switch'ем, а вы опосля взяли и переправили значение. В итоге - несогласованность.
|
Сообщ.
#3
,
|
|
|
Цитата Eric-S @ Что тут можно сделать? Использовать enum или попробовать инициализировать поля прямо в объявлении класса static const int beta = _XXX_SIMBOLBETA; |
Сообщ.
#4
,
|
|
|
Цитата Славян @ Несколько странный вопрос. Ибо: компилирует, скажем, компилятор такой левый файл с таким левым switch'ем, а вы опосля взяли и переправили значение. В итоге - несогласованность. Замечание правомочно. Но ведь я и enum class могу изменить, с тем же успехом. Понятно, что такая ситуация возможно. Я о ней знаю. Но неужели ничего нельзя поделать? |
Сообщ.
#5
,
|
|
|
Если ты хочешь инить в другой единице трансляции, то компилятор правильно ругается и действительно ничего с этим не сделать, т. к. это не константа а константная переменная, что не одно и то же.
Добавлено Цитата Eric-S @ Но ведь я и enum class могу изменить, с тем же успехом. Не можешь. |
Сообщ.
#6
,
|
|
|
Цитата shm @ Использовать enum или попробовать инициализировать поля прямо в объявлении класса static const int beta = _XXX_SIMBOLBETA; Ох. Хорошо бы. Но эти константы, тянутся из одной страшненькой библиотеки. И я хотел бы скрыть эту пакость. Но сейчас подумываю на счёт промежуточного enum. В том смысле, что значение перечисления будет автоматом. А при необходимости, получить константу, внутри будет switch, который конвертнёт константу enum в константу int. int to_int( foo_enum val ) { switch( val ) { case foo_enum::alpha: return _XXX_SIMBOL_ALPHA; case foo_enum::beta: return _XXX_SIMBOL_BETA; } } Добавлено Цитата shm @ компилятор правильно ругается и действительно ничего с этим не сделать, т. к. это не константа а константная переменная, что не одно и то же. Ругается правильно. Справедливо, правомочно и логично. Но неужели нет ни одного внятного трюка для укращения? |
Сообщ.
#7
,
|
|
|
Цитата Eric-S @ Ох. Хорошо бы. Но эти константы, тянутся из одной страшненькой библиотеки. И я хотел бы скрыть эту пакость Ну а зачем ты выносишь тогда эти константы наружу? Сделай обертки для функций из этой библиотеки и все. Добавлено А вообще, это ж макросы. Просто скопируй их оттуда к себе и не парься. |
Сообщ.
#8
,
|
|
|
Цитата Eric-S @ Объявил класс со статическими константами: Скрытый текст class foo { public: static const int alpha; static const int beta; }; const int foo::alpha = _XXX_SIMBOL_ALPHA; const int foo::beta = _XXX_SIMBOLBETA; Ну и тут чудеса. В функции main (из main.obj) switch( val ) { case foo::alpha: case foo::beta: break; default: break; } Компилятор ms vs 2015 сказал, что foo::alpha не является константой! а у меня все компилирует пробовал vs2008 щас протестил в vs2010-vs2015 все компилирует или я не правильно понял? Эриксон проправь меня, а то я с ума сойду |
Сообщ.
#9
,
|
|
|
Цитата Cfon @ Эриксон проправь меня, а то я с ума сойду Ты разделил единицы трансляции? Должно быть два объектных файла: foo.obj и main.obj. Добавлено Цитата Олег М @ Ну а зачем ты выносишь тогда эти константы наружу? Сделай обертки для функций из этой библиотеки и все. Как раз пишу статическую lib, в качестве обёртки. Но в частности, эти константы нужны пользователю, для обработки callback функции. Пользователь пишет функцию. Регистрирует её в моей обёртке. Затем библиотека дёргает эту функцию. А пользователь должен обработат сообщение через switch. Можно конечно if. Всё же функция простенькая, не какой-то там WinProc. Добавлено Цитата Олег М @ А вообще, это ж макросы. Просто скопируй их оттуда к себе и не парься. Угу. Ну да... А хорошо ли так делать? Подход гарантировано работающий сечас. Но в будущем ломабельный. И просто неэтичный. Добавлено Цитата shm @ Цитата Eric-S @ Но ведь я и enum class могу изменить, с тем же успехом. Не можешь. Не... Я в том смысле, что если я изменю enum в одном файле, то придётся перекомпилировать все файлы, где используется этот enum. |
Сообщ.
#10
,
|
|
|
Цитата Eric-S @ Я в том смысле, что если я изменю enum в одном файле, то придётся перекомпилировать все файлы, где используется этот enum. Иии? |
Сообщ.
#11
,
|
|
|
Цитата Славян @ Несколько странный вопрос. Ибо: компилирует, скажем, компилятор такой левый файл с таким левым switch'ем, а вы опосля взяли и переправили значение. В итоге - несогласованность. Цитата Eric-S @ Замечание правомочно. Но ведь я и enum class могу изменить, с тем же успехом. Понятно, что такая ситуация возможно. Я о ней знаю. Но неужели ничего нельзя поделать? Значит всё равно придётся перекомпилировать! Единственная сложность в дополнительной проверки изменений. Добавлено Цитата shm @ Иии? Да, нет. Ничо. Это я так, причетаю. Понятно же, что с enum, компилятору легче перепроверить кто когда изменился, поскольку enum открыт всем ветрам, опубликованный в хидере. |
Сообщ.
#12
,
|
|
|
Цитата Eric-S @ Угу. Ну да... А хорошо ли так делать? Подход гарантировано работающий сечас. Но в будущем ломабельный. И просто неэтичный В общем случае – нехорошо. А в частном – оцениваешь, стоит ли эта библиотека прилагаемых усилий, да еще в runtime. Чаще бывает, что проще скопировать значения. |
Сообщ.
#13
,
|
|
|
Цитата Олег М @ В общем случае – нехорошо. Потом будет лениво переделывать. Да и пользоватся странным интерфейсом стрёмно. Так что, если делать, то максимально хорошо. А в противном случае, проще юзать библиотеку, без всяких обёрток. |
Сообщ.
#14
,
|
|
|
Цитата Eric-S @ Потом будет лениво переделывать. Да и пользоватся странным интерфейсом стрёмно. Так что, если делать, то максимально хорошо. Проблема в том, что ты пытаешься перенести в runtime решение задачи - сравнение и сопоставление констант - которая может и должна решаться в compile-time. В данном случае, если тебе очень не хочется выносить наружу заголовок твоей библиотеки, можно просто сделать свой enum или свои константы совпадающие по значению с библиотечными. И проверять где-нибудь их соответствие при помощи static_assert. |
Сообщ.
#15
,
|
|
|
Вообщем я сделал свой enum class, с альтернативными кодами.
И две функции конвертирующие туда и обратно, через тот же самый switch. Заодно, в секции default, воткнул throw invalid_argument. Отработало всё нормально. Жаль, что моя версия компилятора не может это сделать constexpr, а в остальном результат устраивает. Но если у кого появятся альтернативные решения, прошу отписаться! Добавлено Цитата Олег М @ можно просто сделать свой enum или свои константы совпадающие по значению с библиотечными. И проверять где-нибудь их соответствие при помощи static_assert. Да! Это тоже хорошее, правильное решение. Проверять через static_assert. А приводить через reinterpret_cast? Добавлено Сейчас коммит оформлю, чтоб сохранить изменения с вариантом runtime. И попробую переделать с вариантом compile-time. |
Сообщ.
#16
,
|
|
|
Цитата Eric-S @ Проверять через static_assert. А приводить через reinterpret_cast? Как хочешь. А их нужно вообще приводить, там разве не int? Добавлено Кстати, насчёт runtime - вот ты скомпилировал новую версию, константы у тебя, допустим, разошлись - и как ты это отслеживаешь? |
Сообщ.
#17
,
|
|
|
Цитата Олег М @ Как хочешь. А их нужно вообще приводить, там разве не int? Изначально у меня действительно был int. Но стремясь к типобезопастности, правильнее сделать enum class. Именно его я сейчас и сделал. Эм-эм-эм... Какой вопрос хороший, с подковырочкой-то! Ну ладно. Сознаюсь, что сделал не идиально и не полноценно. Но пока хватало. 1. в makefile добавил зависимость с cpp файлом, в котором были объявлены константы. 2. после разных приключений и интересной идеи с хабра, в ответственных местах, создаю константу версии: enum{ __version_num = 1 }; Ну а в местах, которые зависят, проставил static_assert( 1 == component_name::__version_id, "update component_name" ); Остаётся своевременно обновлять номер версии. Но по факту это актуально лишь для контроля в методах за изменениями сложных полей класса. 3. с динамическими компонентами. Ну, когда всё совсем сложно. Обычно для отдельной dll. Там действительно остаётся только run-time. Создаю функцию инициализации библиотеки: #define current_major_version 2 #define current_minor_version 86 void _component_initialize( int major_version, int minor_version ) { if( current_major_version != major_version ) { throw invalid_version( "conflict major version" ); } if( current_minor_version <= major_version ) { throw invalid_version( "conflict minor version" ); } } А там где библиотека юзается, то дёргаю эту функцию через макрос. #define component_initialize() _component_initialize( current_major_version, current_minor_version ); Если библиотека обновилась, а прога хочет старую, то упс и обломс. Трюк, вообщем-то прокатывает и для статических библиотек. Но прописывать такое лениво. Проще прописать зависимость в makefile. |
Сообщ.
#18
,
|
|
|
Цитата Eric-S @ Но по факту это актуально лишь для контроля в методах за изменениями сложных полей класса. По факту, у тебя получился код, который просто преобразует одно число в другое, константы в рантайме. Больше никакой осмысленной нагрузки не несёт. Контроль версий здесь поможет слабо. Нужен контроль именно этих констант. Добавлено Я бы сделал примерно так ---h-file #define RETCODES \ ITEM(XXX_SIMBOL_ALPHA, 1) \ ITEM(XXX_SIMBOLBETA, 2) \ enum { #define ITEM(name, value) _##name = value, RETCODES #undef ITEM } ----cpp-file #include <somelib.h> #define ITEM(name, value) static_assert(_##name == name); RETCODES #undef ITEM |
Сообщ.
#19
,
|
|
|
Цитата Олег М @ По факту, у тебя получился код, который просто преобразует одно число в другое, константы в рантайме. Угу. Вот именно! Единственное, что оправдывает такой изврат, так добавившаяся проверка на корректность кода и exception. Добавлено Цитата Олег М @ Нужен контроль именно этих констант. Да, спасибо. Идею понял раньше, из предыдущего комментария. Тем более уже нечто такое делал. Я сейчас чаи гоняю. До кода ещё не добрался, чтоб попробовать проверку. Ах да, ваш трюк с именами констант, у меня не сработает, ибо реальные названия разные. Мне ихние встроенные названия не нравятся от слова ообще. А ещё у меня возникает конфликт имён с уже существующими сущностями. |
Сообщ.
#20
,
|
|
|
Цитата Eric-S @ Ах да, ваш трюк с именами констант, у меня не сработает, ибо реальные названия разные. Мне ихние встроенные названия не нравятся от слова ообще. А ещё у меня возникает конфликт имён с уже существующими сущностями. А ты добавь ещё один параметр в ITEM(), своё имя. Тогда сразу будет видно что чему соответствует, и не потребует подключения библиотеки, где не нужно |
Сообщ.
#21
,
|
|
|
Цитата Олег М @ А ты добавь ещё один параметр в ITEM(), своё имя. Тогда сразу будет видно что чему соответствует, и не потребует подключения библиотеки, где не нужно Собственно так и сделал. Решение очевидное. Но всё равно спасибо. В enum class, значение констант проставил руками. Их всего три, так что не сломался. А вот в cpp-файле сделал проверку, через макросы со static_assert. Так же, не стал выкидывать уже существующие функции конвертации. Просто препроцессором отключил runtime switch. А в альтернативной ветке сделал return static_cast<>() Вроде всё работает правильно. И даже не слишком страшно. Единственное, меня лично напрягают проставленные значения констант в enum. Ну да ладно. Они двадцать лет не менялись. Надеюсь, что потом тоже не поменяются. |
Сообщ.
#22
,
|
|
|
Это чисто заморочки студийного компилера, gnu c++ от 03 стандарта и далее - не ругается. Проверял.
Вариант ... замени это: Цитата Eric-S @ class foo { public: static const int alpha; static const int beta; }; const int foo::alpha = _XXX_SIMBOL_ALPHA; const int foo::beta = _XXX_SIMBOLBETA; на это: namespace foo { static const int alpha = _XXX_SIMBOL_ALPHA; static const int beta = _XXX_SIMBOLBETA; }; В остальном работать должно с тем же синтаксисом ... Но на студии я проверить не могу, нет ее у меня. |
Сообщ.
#23
,
|
|
|
Цитата JoeUser @ Это чисто заморочки студийного компилера, gnu c++ от 03 стандарта и далее - не ругается. Проверял. Студия, есть студия. Колемся. Плачем. Но продолжаем есть. Тем более имеются другие плюшки. Цитата JoeUser @ Вариант ... замени это: Я уже пошёл иным путём, через enum class и ручного набивания констант. Цитата JoeUser @ на это... Фишка того класса была ещё в том, что он исполнял роль обёртки значения. Но я попрощался с ним. Цитата JoeUser @ В остальном работать должно с тем же синтаксисом ... Но на студии я проверить не могу, нет ее у меня. Угу. Наверняка будет работать. Спасибо за отклик. Но фишечка статических полей, как раз была в том, чтоб спрятать значения констант в отдельном файле. А я похоже, откажусь от всех тех заморочек. Как не крути, а enum class, даёт строгую статическую типизацию. И попутных с ним наворотов кода гораздо меньше. |
Сообщ.
#24
,
|
|
|
Цитата Eric-S @ Но фишечка статических полей, как раз была в том, чтоб спрятать значения констант в отдельном файле. Ну вариантов этого решения - масса. Я не думаю, что это цель. Цитата Eric-S @ даёт строгую статическую типизацию А вот это более существенно - что дает ощутимый профит в плане проверок во время сборки. Хотя, скромно вангую) это, имхо, жертва перфекционизму ))) Ошибаться право имею) |
Сообщ.
#25
,
|
|
|
Цитата JoeUser @ на это: Это практически то же самое, что я предложил. Но он фактически хочет, чтобы содержимое хидера можно было бы менять из какого-то модуля. Добавлено Eric-S, уж если тебе так хочется забивать константы в переменные, то откажись от switch, а если параметров много, то заюзай unorderd_map: ощутимой разницы по быстродействию быть не должно. А в целом я бы подлкючил явно заголовочник той либы и не парился. |
Сообщ.
#26
,
|
|
|
Цитата shm @ Это практически то же самое, что я предложил. Практически "да". За исключением того, что инициализация в описании класса стала доступна по синтаксису несколько позже. А нэймспейсы стары как гуано мамонтов) |
Сообщ.
#27
,
|
|
|
Цитата shm @ а если параметров много, то заюзай unorderd_map: ощутимой разницы по быстродействию быть не должно. Речь идёт о двух маленьких группах констант. В первом enum у меня всего три пункта. Во втором enum их пять. И всё! Больше нет констант. Добавлено Цитата JoeUser @ А вот это более существенно - что дает ощутимый профит в плане проверок во время сборки. Хотя, скромно вангую) это, имхо, жертва перфекционизму ))) Она самая. Давно уже стремлюсь к максимальной строгости. А те константы меня раздражали, как раз своей нетипизированностью. Там, в либе, есть например приблизительно такая функция: int set_mode( int x, int y ); А юмор в том, что в параметр x она просит одну из констант первой группы (всё остальное приводит к ошибке) в runtime). В параметр y просит сочетание флагов из вторых констант. И возвращает тоже некое значение состоящее из флагов второй группы. Теперь же получилось очень строго и однозначно. group_y set_mode( group_x x, group_y y ); И хрен кто чего подсунет левого. Только если вооружится static_cast. Добавлено Цитата shm @ если тебе так хочется забивать константы в переменные, то откажись от switch Так в том-то и дело, что оформляю константу... А компилятор считает её переменной. Я вот и спрашивал, как оформить константу так, чтоб она была именно константой. |
Сообщ.
#28
,
|
|
|
Цитата Eric-S @ как оформить константу так, чтоб она была именно константой constexpr? |
Сообщ.
#29
,
|
|
|
Цитата Алексей_Л @ Цитата Eric-S @ как оформить константу так, чтоб она была именно константой constexpr? Подумал ещё до того, как создавал данную тему. И даже упомянул об этом! Цитата Eric-S @ Проскакивала мысль на счёт constexpr. Но компилятор хочет, чтоб я ему сразу предоставил значение. Чего-то там как-то не так. Вообще, у меня constexpr ведёт себя как-то странно. Видимо недаделан. Если в функции есть оператор if, то ошибка компиляции. Если тело функции в другой единице трансляции, то ошибка линковки. Ну и с этим полем static говорит, что его нужно инициализировать! |
Сообщ.
#30
,
|
|
|
Цитата Eric-S @ Вообще, у меня constexpr ведёт себя как-то странно. Видимо недаделан. Если в функции есть оператор if, то ошибка компиляции. А не проще сделать, чтоб твои функции, вместо того чтоб возвращать код ошибки выдавали exception? Тогда все эти проблемы просто не возникнут |
Сообщ.
#31
,
|
|
|
Цитата Олег М @ А не проще сделать, чтоб твои функции, вместо того чтоб возвращать код ошибки выдавали exception? Тогда все эти проблемы просто не возникнут Олег, ты о чём? Причём тут код ошибки или исключения? Я писал о constexpr, ошибках компиляции и ошибках линковки. Вообще, очень интересно, с чего ты взял, что мои функции возвращают коды ошибок? Может быть, ты меня с кем-то перепутал? |
Сообщ.
#32
,
|
|
|
Не знаю. Просто немного потерял нить – зачем это все вообще нужно?
|
Сообщ.
#33
,
|
|
|
Цитата Олег М @ Не знаю. Просто немного потерял нить – зачем это все вообще нужно? Сейчас сделал три разных enum. Два из enum используются в качестве битовых флагов, для конфигурирования. Примерно так: enum class config { none = 0x0000, alpha = 0x0010, beta = 0x0020, ... }; someclass obj0( config::nothing ); someclass obj1( config::alpha | config::beta ); Для них я перегрузил побитовые операторы. Третий enum используется в нескольких разных местах. В нём лежат идентификаторы. enum class msg_category{ first = 0x0000, second = 0x0001, ... }; 1. для конфигурирования. message_handler_guard mhg( msg_category::first, &funcname_proc ); 2. для отправки сообщений. dispatch::send( msg_category::first, "hello, world!" ); 3. для приёма сообщений void funcname_proc( int id, char* message ) { switch( static_cast< msg_category >( id ) ) { case msg_category::first: cout << message << endl; break; default: throw invalid_message( id ); } } У меня только простые классы-обёртки, поверх библиотечных функций. Библиотека использует свои идентификаторы. Переделывать библиотеку не могу и не хочу. Но в итоге, приходится обходится именно с теми константами, которые использует библиотека. Иначе, если придумаю свои константы, библиотека не поймёт пользователя, который работает через мою обёртку, а пользователь не поймёт библиотеку. |