Как заставить printf показывать значения переменных перечислимого типа? Например:
typedef enum {Linux, Apple, Windows} OS_type;
OS_type myOS = Linux;
и мне нужно что-то вроде
printenum(OS_type, "My OS is %s", myOS);
который должен отображать строку «Linux», а не целое число.
Полагаю, сначала мне нужно создать массив строк с индексированием значений. Но я не знаю, лучший ли это способ сделать это. Это вообще возможно?
c++
c
preprocessor
ansi-c
psihodelia
источник
источник
Ответы:
На самом деле нет красивого способа сделать это. Просто настройте массив строк, проиндексированных перечислением.
Если вы делаете много вывода, вы можете определить оператор <<, который принимает параметр перечисления и выполняет поиск за вас.
источник
Наивное решение, конечно же, - написать функцию для каждого перечисления, которая выполняет преобразование в строку:
Однако это катастрофа для обслуживания. С помощью библиотеки Boost.Preprocessor, которую можно использовать как с кодом C, так и с кодом C ++, вы можете легко воспользоваться преимуществами препроцессора и позволить ему сгенерировать эту функцию для вас. Макрос генерации выглядит следующим образом:
Первый макрос (начинающийся с
X_
) используется вторым внутри. Второй макрос сначала генерирует перечисление, затем генерируетToString
функцию, которая принимает объект этого типа и возвращает имя перечислителя в виде строки (эта реализация, по очевидным причинам, требует, чтобы перечислители отображали уникальные значения).В C ++ вы могли бы вместо этого реализовать
ToString
функцию какoperator<<
перегрузку, но я думаю, что будет немного проще требовать явного "ToString
" для преобразования значения в строковую форму.В качестве примера использования ваше
OS_type
перечисление будет определено следующим образом:Хотя макрос сначала выглядит так, как будто это большой объем работы, а определение
OS_type
выглядит довольно странно, помните, что вам нужно написать макрос один раз, а затем вы можете использовать его для каждого перечисления. Вы можете добавить к нему дополнительные функции (например, преобразование строковой формы в перечисление) без особых проблем, и это полностью решает проблему обслуживания, поскольку вам нужно указать имена только один раз, когда вы вызываете макрос.Затем перечисление можно использовать, как если бы оно было определено обычным образом:
Фрагменты кода в этом посте, начинающиеся со
#include <boost/preprocessor.hpp>
строки, можно скомпилировать в том виде, в котором они опубликованы, для демонстрации решения.Это конкретное решение предназначено для C ++, поскольку в нем используется специфичный для C ++ синтаксис (например, нет
typedef enum
) и перегрузка функций, но было бы просто заставить это работать и с C.источник
(Windows)
на, а(Windows, 3)
затем заменить наBOOST_PP_SEQ_ENUM
соответственно написанныйBOOST_PP_SEQ_FOR_EACH
. У меня нет такого удобного примера, но я могу написать его, если хотите.Это блок препроцессора
Определение перечисления
Звоните с помощью
Взято отсюда . Как это круто ? :)
источник
Используйте
std::map<OS_type, std::string>
и заполните его перечислением в качестве ключа и строковым представлением в качестве значений, тогда вы можете сделать это:источник
Проблема с перечислениями C в том, что это не собственный тип, как в C ++. Перечисление в C - это способ сопоставить идентификаторы с целыми значениями. Только то. Вот почему значение перечисления взаимозаменяемо с целочисленными значениями.
Как вы правильно догадались, хороший способ - создать сопоставление между значением перечисления и строкой. Например:
источник
enum
это типы в C. Константы интегрального типа перечисления относятся к типу,int
а не кenum
типу, через который они определены, возможно, вы хотели сказать. Но я вообще не понимаю, при чем тут вопрос.Я объединил решения Джеймса , Ховарда и Эдера и создал более общую реализацию:
Полный код написан ниже (используйте "DEFINE_ENUM_CLASS_WITH_ToString_METHOD" для определения перечисления) ( онлайн-демонстрация ).
источник
Этот простой пример сработал для меня. Надеюсь это поможет.
источник
Вы пробовали это:
stringify()
Макрос может быть использован , чтобы превратить любой текст в коде в строку, но только точный текст в скобках. Здесь нет разыменования переменных, макроподстановок или каких-либо других действий.http://www.cplusplus.com/forum/general/2949/
источник
Здесь много хороших ответов, но я подумал, что некоторые люди найдут мой полезный. Мне это нравится, потому что интерфейс, который вы используете для определения макроса, настолько прост, насколько это возможно. Это также удобно, потому что вам не нужно включать какие-либо дополнительные библиотеки - все это поставляется с C ++ и даже не требует действительно поздней версии. Я извлекал фрагменты из разных мест в Интернете, поэтому я не могу поверить в это все, но я думаю, что это достаточно уникально, чтобы заслужить новый ответ.
Сначала создайте файл заголовка ... назовите его EnumMacros.h или что-то в этом роде и вставьте в него следующее:
Затем в своей основной программе вы можете сделать это ...
Где вывод будет >> Значение «Apple»: 2 из 4
Наслаждайтесь!
источник
Предполагая, что ваше перечисление уже определено, вы можете создать массив пар:
Теперь вы можете создать карту:
Теперь вы можете использовать карту. Если ваше перечисление изменилось, вы должны добавить / удалить пару из массива pair []. Я думаю, что это самый элегантный способ получить строку из enum в C ++.
источник
bimap
в случае, если кто-то хочет проанализировать имена и превратить их в перечисления (например, из файла XML).Для C99 есть
P99_DECLARE_ENUM
в Р99 , что позволяет просто объявить ,enum
как это:а затем используйте
color_getname(A)
для получения строки с названием цвета.источник
Вот мой код на C ++:
источник
Немного поздно на вечеринку, но вот мое решение на C ++ 11:
источник
error: ‘hash’ is not a class template
->#include <functional>
Я предпочитаю минимизировать как повторяющийся набор текста, так и сложные для понимания макросы, а также избегать введения макроопределений в общее пространство компилятора.
Итак, в заголовочном файле:
а реализация cpp:
Обратите внимание на #undef макроса, как только мы закончим с ним.
источник
Мое решение, не использующее ускорение:
И вот как это использовать
источник
Еще один опаздывающий на вечеринку, используя препроцессор:
(Я просто ввел номера строк, чтобы было легче говорить.) Строки 1–4 - это то, что вы редактируете, чтобы определить элементы перечисления. (Я назвал его «макросом списка», потому что это макрос, который составляет список вещей. @Lundin сообщает мне, что это хорошо известный метод, называемый X-макросами.)
Строка 7 определяет внутренний макрос, чтобы заполнить фактическое объявление перечисления в строках 8-11. Строка 12 отменяет определение внутреннего макроса (просто чтобы отключить предупреждение компилятора).
Строка 14 определяет внутренний макрос, чтобы создать строковую версию имени элемента перечисления. Затем в строках 15-18 генерируется массив, который может преобразовать значение перечисления в соответствующую строку.
Строки 21–27 генерируют функцию, которая преобразует строку в значение перечисления или возвращает NULL, если строка не соответствует ни одному.
Это немного громоздко с точки зрения обработки 0-го элемента. Я действительно работал над этим в прошлом.
Я признаю, что этот метод беспокоит людей, которые не хотят думать, что сам препроцессор может быть запрограммирован на написание кода за вас. Думаю, это наглядно демонстрирует разницу между удобочитаемостью и удобством обслуживания . Код сложен для чтения, но если в перечислении несколько сотен элементов, вы можете добавлять, удалять или переупорядочивать элементы и при этом быть уверенным, что сгенерированный код не содержит ошибок.
источник
#define TEST_1 hello #define TEST_2 world
then,typedef enum { TEST_1, TEST_2 } test_t;
а затем создать таблицу поиска строк, в которой используется макрос Stringify:const char* table[]= { STRINGIFY(TEST_1), STRINGIFY(TEST_2), };
уже есть несколько ответов, намекающих на похожие решения. Намного читабельнее.Вот метод Old Skool (который широко использовался в gcc) с использованием только препроцессора C. Полезно, если вы создаете дискретные структуры данных, но вам нужно поддерживать согласованный порядок между ними. Записи в mylist.tbl, конечно, можно расширить до чего-то гораздо более сложного.
test.cpp:
И затем mylist.tbl:
источник
В c ++ вот так:
источник
из http://www.codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C и после
вставка
Прекрасно работает, если значения в перечислении не повторяются.
Пример кода для преобразования значения перечисления в строку:
Пример кода для прямо противоположного:
источник
Спасибо, Джеймс, за ваше предложение. Это было очень полезно, поэтому я реализовал другой способ, чтобы внести свой вклад.
источник
Чтобы расширить ответ Джеймса, кому-то нужен пример кода для поддержки определения enum со значением int, у меня также есть это требование, поэтому вот мой способ:
Первый - это макрос внутреннего использования, который используется FOR_EACH:
И вот макрос определения:
Поэтому, используя его, вы можете писать так:
который расширится до:
Основная идея состоит в том, чтобы определить SEQ, каждый элемент которого является TUPLE, чтобы мы могли добавить значение для члена enum. В цикле FOR_EACH проверьте размер элемента TUPLE, если размер равен 2, разверните код до KEY = VALUE, иначе просто оставьте первый элемент TUPLE.
Поскольку входной SEQ на самом деле является TUPLE, поэтому, если вы хотите определить функции STRINGIZE, вам может потребоваться сначала предварительно обработать входные перечислители, вот макрос для выполнения этой работы:
Макрос
DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ
сохранит только первый элемент в каждом TUPLE, а затем преобразует его в SEQ, теперь измените код Джеймса, вы получите полную мощность.Моя реализация, возможно, не самая простая, поэтому, если вы не найдете чистого кода, мой для вашей справки.
источник
Чистое и безопасное решение в чистом стандарте C:
Вывод
обоснование
Решая основную задачу «иметь константы перечисления с соответствующими строками», здравомыслящий программист выдвинет следующие требования:
Первое требование, а может быть и второе, может быть выполнено с помощью различных беспорядочных макро-решений, таких как печально известный трюк с «x macro» или другие формы макро-магии. Проблема с такими решениями заключается в том, что они оставляют вас с совершенно нечитаемым беспорядком загадочных макросов - они не соответствуют третьему требованию выше.
Единственное, что здесь нужно, это фактически иметь таблицу поиска строк, к которой мы можем получить доступ, используя переменную enum в качестве индекса. Такая таблица, естественно, должна напрямую соответствовать enum и наоборот. При обновлении одного из них необходимо обновить и другой, иначе он не будет работать.
Пояснение к коду
Предположим, у нас есть перечисление вроде
Это можно изменить на
С тем преимуществом, что эти макроконстанты теперь можно использовать где-нибудь еще, например, для создания таблицы поиска строк. Преобразование константы препроцессора в строку может быть выполнено с помощью макроса "stringify":
И это все. Используя
hello
, мы получаем константу перечисления со значением 0. Используя,test_str[hello]
мы получаем строку "hello".Чтобы перечисление и таблица поиска соответствовали напрямую, мы должны убедиться, что они содержат одинаковое количество элементов. Если кто-то будет поддерживать код и изменять только перечисление, а не таблицу поиска, или наоборот, этот метод не будет работать.
Решение состоит в том, чтобы перечисление сообщало вам, сколько элементов оно содержит. Для этого есть часто используемый трюк C, просто добавьте элемент в конце, который только заполняет цель сообщить, сколько элементов имеет перечисление:
Теперь мы можем проверить во время компиляции, что количество элементов в перечислении равно количеству элементов в таблице поиска, предпочтительно с помощью статического утверждения C11:
(Есть уродливые, но полнофункциональные способы создания статических утверждений в более старых версиях стандарта C, если кто-то настаивает на использовании компиляторов динозавров. Что касается C ++, он также поддерживает статические утверждения.)
В качестве примечания, в C11 мы также можем достичь более высокой безопасности типов, изменив макрос stringify:
(
int
поскольку константы перечисления на самом деле имеют типint
, а неtest_t
)Это предотвратит
STRINGIFY(random_stuff)
компиляцию кода вроде .источник
#define
, добавить ссылку на это определение в объявлении перечисления и в справочной таблице. Если вы допустите ошибку при добавлении этих строк, программа не скомпилируется. Числа, которые я добавил к идентификаторам, ни в коем случае не являются обязательными, вы также можете написать,#define APPLES hello
а#define ORANGES world
затемtypedef enum { APPES, ORANGES, TEST_N } test_t;
и так далее.То, что я сделал, является комбинацией того, что я видел здесь и в аналогичных вопросах на этом сайте. Я сделал это Visual Studio 2013. Я не тестировал его с другими компиляторами.
Прежде всего, я определяю набор макросов, которые помогут.
Затем определите один макрос, который создаст класс перечисления и функции для получения строк.
Теперь определение типа перечисления и наличие для него строк становится действительно простым. Все, что вам нужно сделать, это:
Следующие строки можно использовать для проверки.
Это выведет:
Я считаю, что он очень чистый и простой в использовании. Есть некоторые ограничения:
Если вы можете обойти это. Я думаю, особенно как его использовать, это красиво и скудно. Преимущества:
источник
Чистым решением этой проблемы было бы:
Преимущество этого решения заключается в том, что оно простое, а также создание функции может быть легко выполнено с помощью копирования и замены. Обратите внимание, что если вы собираетесь выполнить много преобразований, а ваше перечисление имеет слишком много возможных значений, это решение может потребовать загрузки ЦП.
источник
Я немного опоздал, но вот мое решение с использованием g ++ и только стандартных библиотек. Я попытался свести к минимуму загрязнение пространства имен и избавиться от необходимости повторно вводить имена перечислений.
Заголовочный файл my_enum.hpp:
Пример использования:
Это выведет:
Вам нужно определить все только один раз, ваше пространство имен не должно быть загрязнено, и все вычисления выполняются только один раз (остальное - просто поиск). Однако вы не получаете типобезопасность классов перечислений (они по-прежнему являются короткими целыми числами), вы не можете присваивать значения перечислениям, вы должны определять перечисления где-нибудь, где вы можете определять пространства имен (например, глобально).
Я не уверен, насколько хорошо это работает, и насколько это хорошая идея (я изучал C до C ++, поэтому мой мозг все еще работает таким образом). Если кто-нибудь знает, почему это плохая идея, не стесняйтесь указать на это.
источник
Это 2017 год, но вопрос все еще жив
Еще один способ:
Выходы:
источник
Это усовершенствованная версия расширенного перечисления класса ... она не добавляет никаких других значений перечисления, кроме предоставленных.
Использование: DECLARE_ENUM_SEQ (CameraMode, (3), Fly, FirstPerson, PerspectiveCorrect)
источник
Мне нужно, чтобы это работало в обоих направлениях, и я часто встраиваю свои перечисления в содержащий класс, поэтому я начал с решения Джеймса Макнеллиса, путь в верхней части этих ответов, но я сделал это решение. Обратите внимание, что я предпочитаю класс enum, а не просто enum, что несколько усложняет ответ.
Чтобы использовать его внутри класса, вы можете сделать что-то вроде этого:
И я написал тест CppUnit, который демонстрирует, как его использовать:
Вы должны выбрать, какой макрос использовать: DEFINE_ENUMERATION или DEFINE_ENUMERATION_INSIDE_CLASS. Вы увидите, что я использовал последнее при определении ComponentStatus :: Status, но я использовал первое, когда просто определял Status. Отличие простое. Внутри класса я использую префикс методов to / from как «static», а если не в классе, я использую «inline». Мелкие отличия, но необходимые.
К сожалению, я не думаю, что есть чистый способ избежать этого:
хотя вы можете вручную создать встроенный метод после определения вашего класса, который просто привязывается к методу класса, например:
источник
Мой собственный ответ, не использующий ускорение - с использованием моего собственного подхода без тяжелой магии определения, и это решение имеет ограничение, заключающееся в невозможности определить конкретное значение перечисления.
Последнюю версию можно найти на github здесь:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h
источник
На это есть много других ответов, но я думаю, что лучший способ - использовать функции C ++ 17 и использовать constexpr, чтобы переводы выполнялись во время компиляции. Это типобезопасный вариант, и нам не нужно возиться с макросами. Увидеть ниже:
Затем это можно легко использовать для обнаружения ошибок строкового ключа во время компиляции:
Код более подробный, чем некоторые другие решения, но мы можем легко выполнить преобразование Enum в String и преобразование String в Enum во время компиляции и обнаружить ошибки типа. С некоторыми из будущих функций C ++ 20 это, вероятно, можно будет еще немного упростить.
источник