Como fazer printf para mostrar os valores de variáveis que são do tipo enum? Por exemplo:
typedef enum {Linux, Apple, Windows} OS_type;
OS_type myOS = Linux;
e o que eu preciso é algo como
printenum(OS_type, "My OS is %s", myOS);
que deve mostrar uma string "Linux", não um número inteiro.
Suponho que primeiro tenha que criar uma matriz de cadeias indexada por valor. Mas não sei se essa é a maneira mais bonita de fazer isso. É possível de alguma forma?
c++
c
preprocessor
ansi-c
psihodelia
fonte
fonte
Respostas:
Realmente não há uma maneira bonita de fazer isso. Basta configurar uma matriz de seqüências indexadas pelo enum.
Se você realiza muitas saídas, pode definir um operador << que recebe um parâmetro enum e faz a pesquisa por você.
fonte
A solução ingênua, é claro, é escrever uma função para cada enumeração que realiza a conversão em string:
Isso, no entanto, é um desastre de manutenção. Com a ajuda da biblioteca Boost.Preprocessor, que pode ser usada com o código C e C ++, você pode aproveitar facilmente o pré-processador e deixá-lo gerar essa função para você. A macro de geração é a seguinte:
A primeira macro (começando com
X_
) é usada internamente pela segunda. A segunda macro primeiro gera a enumeração e, em seguida, gera umaToString
função que pega um objeto desse tipo e retorna o nome do enumerador como uma seqüência de caracteres (essa implementação, por razões óbvias, requer que os enumeradores sejam mapeados para valores exclusivos).Em C ++, você poderia implementar a
ToString
função como umaoperator<<
sobrecarga, mas acho que é um pouco mais limpo exigir um "ToString
" explícito para converter o valor em forma de string.Como exemplo de uso, sua
OS_type
enumeração seria definida da seguinte maneira:Embora a macro pareça muito trabalhosa e a definição de
OS_type
aparência bastante estranha, lembre-se de que você deve escrever a macro uma vez e usá-la para todas as enumerações. Você pode adicionar funcionalidade adicional a ele (por exemplo, uma forma de string para conversão de enum) sem muitos problemas e resolve completamente o problema de manutenção, pois você só precisa fornecer os nomes uma vez quando invoca a macro.A enumeração pode ser usada como se estivesse definida normalmente:
Os trechos de código nesta postagem, começando com a
#include <boost/preprocessor.hpp>
linha, podem ser compilados conforme publicados para demonstrar a solução.Essa solução específica é para C ++, pois usa sintaxe específica para C ++ (por exemplo, não
typedef enum
) e sobrecarga de função, mas seria fácil fazer isso funcionar com C também.fonte
(Windows)
em(Windows, 3)
seguida, substituir oBOOST_PP_SEQ_ENUM
com uma escrita adequadamenteBOOST_PP_SEQ_FOR_EACH
. Não tenho um exemplo disso útil, mas posso escrever um, se quiser.Este é o bloco do pré-processador
Definição de enum
Ligue usando
Retirado daqui . Quão legal é isso ? :)
fonte
Use
std::map<OS_type, std::string>
e preencha-o com enumeração como chave e representação de cadeia como valores, então você pode fazer o seguinte:fonte
O problema com enumerações C é que não é um tipo próprio, como é em C ++. Uma enumeração em C é uma maneira de mapear identificadores para valores integrais. Só isso. É por isso que um valor enum é intercambiável com valores inteiros.
Como você adivinha corretamente, uma boa maneira é criar um mapeamento entre o valor da enumeração e uma string. Por exemplo:
fonte
enum
são tipos em C. As constantes do tipo de enumeração integral são do tipoint
e não doenum
tipo pelo qual elas são definidas, talvez seja o que você quis dizer. Mas não vejo o que isso tem a ver com a questão.Eu combinei o James' , Howard e de Eder soluções e criou uma aplicação mais genérica:
O código completo está escrito abaixo (use "DEFINE_ENUM_CLASS_WITH_ToString_METHOD" para definir uma enumeração) ( demonstração online ).
fonte
Este exemplo simples funcionou para mim. Espero que isto ajude.
fonte
Você tentou o seguinte:
A
stringify()
macro pode ser usada para transformar qualquer texto no seu código em uma string, mas apenas o texto exato entre parênteses. Não há desreferenciamento variável ou substituições de macro ou qualquer outro tipo de ação.http://www.cplusplus.com/forum/general/2949/
fonte
Há muitas respostas boas aqui, mas achei que algumas pessoas poderiam achar as minhas úteis. Gosto porque a interface que você usa para definir a macro é o mais simples possível. Também é útil porque você não precisa incluir nenhuma biblioteca extra - tudo vem com C ++ e nem sequer requer uma versão realmente tardia. Peguei peças de vários lugares on-line para não poder receber crédito por tudo isso, mas acho que é único o suficiente para garantir uma nova resposta.
Primeiro, crie um arquivo de cabeçalho ... chame-o de EnumMacros.h ou algo assim e coloque isso:
Então, no seu programa principal, você pode fazer isso ...
Onde a saída seria >> O valor de 'Apple' é: 2 de 4
Aproveitar!
fonte
Supondo que sua enumeração já esteja definida, você pode criar uma matriz de pares:
Agora, você pode criar um mapa:
Agora você pode usar o mapa. Se sua enumeração for alterada, você precisará adicionar / remover pares dos pares de matrizes []. Eu acho que é a maneira mais elegante de obter uma string de enum em C ++.
fonte
bimap
caso queira analisar nomes e transformá-los em enumerações (por exemplo, de um arquivo XML).Para C99, existe
P99_DECLARE_ENUM
no P99 que permite que você simplesmente declareenum
assim:e use
color_getname(A)
para obter uma sequência com o nome da cor.fonte
Aqui está o meu código C ++:
fonte
Um pouco atrasado para a festa, mas aqui está a minha solução C ++ 11:
fonte
error: ‘hash’ is not a class template
->#include <functional>
Minha preferência é minimizar a digitação repetitiva e difícil de entender as macros e evitar a introdução de definições de macro no espaço geral do compilador.
Então, no arquivo de cabeçalho:
e a implementação do cpp é:
Observe o #undef da macro assim que terminarmos.
fonte
Minha solução, não usando boost:
E aqui está como usá-lo
fonte
Outro atraso para a parte, usando o pré-processador:
(Basta inserir os números das linhas para facilitar a conversa.) As linhas 1 a 4 são as que você edita para definir os elementos da enumeração. (Eu chamei isso de "macro de lista", porque é uma macro que faz uma lista de coisas. @Lundin me informa que essa é uma técnica bem conhecida chamada X-macros.)
A linha 7 define a macro interna para preencher a declaração de enum real nas linhas 8-11. A linha 12 undefine a macro interna (apenas para silenciar o aviso do compilador).
A linha 14 define a macro interna para criar uma versão em cadeia do nome do elemento enum. Em seguida, as linhas 15-18 geram uma matriz que pode converter um valor de enumeração na sequência correspondente.
As linhas 21 a 27 geram uma função que converte uma string no valor de enum ou retorna NULL se a string não corresponder a nenhuma.
Isso é um pouco complicado na maneira como lida com o 0º elemento. Eu realmente trabalhei com isso no passado.
Admito que essa técnica incomoda as pessoas que não querem pensar que o próprio pré-processador pode ser programado para escrever código para você. Eu acho que ilustra fortemente a diferença entre legibilidade e facilidade de manutenção . É difícil ler o código, mas se a enumeração tiver algumas centenas de elementos, você poderá adicionar, remover ou reorganizar elementos e ainda assim garantir que o código gerado não tenha erros.
fonte
#define TEST_1 hello #define TEST_2 world
entãotypedef enum { TEST_1, TEST_2 } test_t;
e criar uma tabela de consulta de string que use uma macro stringify:const char* table[]= { STRINGIFY(TEST_1), STRINGIFY(TEST_2), };
já existem várias respostas sugerindo soluções semelhantes. Muito mais legível.Aqui está o método Old Skool (usado extensivamente no gcc) usando apenas o pré-processador C. Útil se você estiver gerando estruturas de dados discretas, mas precisar manter a ordem consistente entre elas. As entradas em mylist.tbl podem, obviamente, ser estendidas para algo muito mais complexo.
test.cpp:
E então mylist.tbl:
fonte
Em c ++ como este:
fonte
de http://www.codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C e depois
inserir
Funciona bem se os valores na enumeração não forem duplicados.
Código de amostra para converter um valor de enum em string:
Código de exemplo para o oposto:
fonte
Obrigado James pela sua sugestão. Foi muito útil, então implementei o contrário para contribuir de alguma forma.
fonte
Para estender a resposta de James, alguém quer algum código de exemplo para suportar enum define com valor int, também tenho esse requisito, então aqui está o meu caminho:
A primeira é a macro de uso interno, usada por FOR_EACH:
E, aqui está a macro define:
Portanto, ao usá-lo, você pode escrever assim:
que será expandido para:
A idéia básica é definir um SEQ, que todo elemento é um TUPLE, para que possamos adicionar valor ao membro enum. No loop FOR_EACH, verifique o tamanho do item TUPLE, se o tamanho for 2, expanda o código para KEY = VALUE, caso contrário, mantenha o primeiro elemento do TUPLE.
Como o SEQ de entrada é realmente TUPLEs, portanto, se você deseja definir funções STRINGIZE, pode ser necessário pré-processar os enumeradores de entrada primeiro, eis a macro para executar o trabalho:
A macro
DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ
manterá apenas o primeiro elemento em cada TUPLE e, posteriormente, será convertida para SEQ, agora modifique o código de James, e você terá todo o poder.Minha implementação talvez não seja a mais simples; portanto, se você não encontrar nenhum código limpo, use o meu para sua referência.
fonte
Solução limpa e segura em puro padrão C:
Resultado
Fundamentação
Ao resolver o problema principal "tenha constantes enum com cadeias correspondentes", um programador sensato apresentará os seguintes requisitos:
O primeiro requisito, e talvez também o segundo, pode ser cumprido com várias soluções macro confusas, como o infame truque "x macro" ou outras formas de macro mágica. O problema dessas soluções é que elas deixam uma confusão completamente ilegível de macros misteriosas - elas não atendem ao terceiro requisito acima.
A única coisa necessária aqui é na verdade ter uma tabela de consulta de strings, à qual podemos acessar usando a variável enum como índice. Essa tabela deve naturalmente corresponder diretamente ao enum e vice-versa. Quando um deles é atualizado, o outro também deve ser atualizado ou não funcionará.
Explicação do código
Suponha que tenhamos uma enumeração como
Isso pode ser alterado para
Com a vantagem de que essas constantes de macro agora podem ser usadas em outros lugares, por exemplo, gerar uma tabela de consulta de string. A conversão de uma constante do pré-processador em uma string pode ser feita com uma macro "stringify":
E é isso. Ao usar
hello
, obtemos a constante enum com valor 0. Ao usartest_str[hello]
, obtemos a string "hello".Para fazer com que a tabela de enum e consulta correspondam diretamente, precisamos garantir que eles contenham a mesma quantidade de itens. Se alguém manter o código e alterar apenas a enumeração, e não a tabela de consulta, ou vice-versa, esse método não funcionará.
A solução é ter a enumeração para informar quantos itens ela contém. Existe um truque C comumente usado para isso: basta adicionar um item no final, o que apenas cumpre o objetivo de dizer quantos itens a enumeração possui:
Agora podemos verificar em tempo de compilação se o número de itens na enumeração é igual ao número de itens na tabela de consulta, de preferência com uma declaração estática C11:
(Existem maneiras feias, mas totalmente funcionais, de criar afirmações estáticas em versões mais antigas do padrão C também, se alguém insistir em usar compiladores de dinossauros. Quanto ao C ++, ele também suporta afirmações estáticas.)
Como observação lateral, em C11 também podemos obter maior segurança de tipo alterando a macro stringify:
(
int
porque as constantes de enumeração são realmente do tipoint
, nãotest_t
)Isso impedirá que o código
STRINGIFY(random_stuff)
seja compilado.fonte
#define
, adicione uma referência àquela definida na declaração da enumeração e na tabela de consulta. Se você gostaria de adicionar essas linhas, o programa não será compilado. Os números que adicionei aos identificadores não são de forma alguma obrigatórios, você também pode escrever#define APPLES hello
e#define ORANGES world
seguir,typedef enum { APPES, ORANGES, TEST_N } test_t;
e assim por diante.O que fiz é uma combinação do que vi aqui e em perguntas semelhantes neste site. Eu fiz isso no Visual Studio 2013. Não testei com outros compiladores.
Antes de tudo, defino um conjunto de macros que fará os truques.
Em seguida, defina uma única macro que criará a classe enum e as funções para obter as seqüências de caracteres.
Agora, definir um tipo de enumeração e ter cadeias de caracteres torna-se realmente fácil. Tudo que você precisa fazer é:
As seguintes linhas podem ser usadas para testá-lo.
Isso produzirá:
Eu acredito que é muito limpo e fácil de usar. Existem algumas limitações:
Se você pode contornar isso. Eu acho que, especialmente como usá-lo, isso é agradável e enxuto. Vantagens:
fonte
Uma solução limpa para esse problema seria:
O bom dessa solução é que ela é simples e também a construção da função pode ser feita facilmente via cópia e substituição. Observe que, se você realizar muitas conversões e sua enumeração tiver muitos valores possíveis, essa solução poderá tornar-se intensiva da CPU.
fonte
Estou um pouco atrasado, mas aqui está a minha solução usando g ++ e apenas bibliotecas padrão. Tentei minimizar a poluição do espaço para nome e remover qualquer necessidade de redigitar nomes de enum.
O arquivo de cabeçalho "my_enum.hpp" é:
Exemplo de uso:
Isso produzirá:
Você só precisa definir tudo uma vez, seu espaço para nome não deve ser poluído e todo o cálculo é feito apenas uma vez (o resto são apenas pesquisas). No entanto, você não obtém a segurança de tipo das classes enum (elas ainda são apenas inteiros curtos), não pode atribuir valores às enumerações, é necessário definir enumerações em algum lugar em que possa definir namespaces (por exemplo, globalmente).
Não tenho certeza de quão bom é o desempenho disso ou se é uma boa ideia (eu aprendi C antes de C ++, para que meu cérebro ainda funcione dessa maneira). Se alguém souber por que essa é uma má idéia, sinta-se à vontade para apontar.
fonte
É 2017, mas a questão ainda está viva
Ainda outra maneira:
Saídas:
fonte
Esta é uma versão de enum estendida de classe elaborada ... não adiciona nenhum outro valor de enum além dos fornecidos.
Uso: DECLARE_ENUM_SEQ (CameraMode, (3), Fly, FirstPerson, PerspectiveCorrect)
fonte
Eu precisava que isso funcionasse nas duas direções E, com frequência, integro minhas enumerações em uma classe que contenha, e então comecei com a solução de James McNellis, bem no topo dessas respostas, mas eu fiz essa solução. Note também que eu prefiro a classe enum do que apenas enum, o que complica um pouco a resposta.
Para usá-lo dentro de uma classe, você pode fazer algo assim:
E eu escrevi um teste CppUnit, que demonstra como usá-lo:
Você precisa escolher qual macro usar, DEFINE_ENUMERATION ou DEFINE_ENUMERATION_INSIDE_CLASS. Você verá que eu usei o último ao definir ComponentStatus :: Status, mas usei o último ao definir Status. A diferença é simples. Dentro de uma classe, prefixo os métodos de / para como "estático" e, se não estiver em uma classe, uso "inline". Diferenças triviais, mas necessárias.
Infelizmente, acho que não há uma maneira limpa de evitar isso:
embora você possa criar manualmente um método embutido após sua definição de classe que simplesmente acorrente ao método de classe, algo como:
fonte
Minha própria resposta, não usando boost - usando minha própria abordagem sem definir magias pesadas, e esta solução tem uma limitação de não ser capaz de definir um valor específico de enum.
A versão mais recente pode ser encontrada no github aqui:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h
fonte
Existem muitas outras respostas para isso, mas acho que uma maneira melhor é usar os recursos do C ++ 17 e usar o constexpr para que as traduções sejam feitas em tempo de compilação. Esse é o tipo seguro e não precisamos mexer com macros. Ver abaixo:
Isso pode ser facilmente utilizado para que erros de chave de cadeia sejam detectados em tempo de compilação:
O código é mais detalhado do que algumas outras soluções, mas podemos facilmente fazer conversão Enum para String e conversão String para Enum em tempo de compilação e detectar erros de tipo. Com alguns dos futuros recursos do C ++ 20, isso provavelmente pode ser simplificado um pouco mais.
fonte