Aqui está o que estou tentando fazer:
typedef enum { ONE, TWO, THREE } Numbers;
Estou tentando escrever uma função que faria um caso de switch semelhante ao seguinte:
char num_str[10];
int process_numbers_str(Numbers num) {
switch(num) {
case ONE:
case TWO:
case THREE:
{
strcpy(num_str, num); //some way to get the symbolic constant name in here?
} break;
default:
return 0; //no match
return 1;
}
Em vez de definir em cada caso, há uma maneira de defini-lo usando a variável enum como estou tentando fazer acima?
c
enums
c-preprocessor
zxcv
fonte
fonte
A técnica de tornar algo tanto um identificador C quanto uma string? pode ser usado aqui.
Como de costume com essas coisas de pré-processador, escrever e entender a parte do pré-processador pode ser difícil, e inclui passar macros para outras macros e envolve o uso de operadores # e ##, mas usá-lo é muito fácil. Acho este estilo muito útil para enums longos, onde manter a mesma lista duas vezes pode ser realmente problemático.
Código de fábrica - digitado apenas uma vez, geralmente escondido no cabeçalho:
enumFactory.h:
Fabrica Usada
someEnum.h:
someEnum.cpp:
A técnica pode ser facilmente estendida para que as macros XX aceitem mais argumentos, e você também pode ter preparado mais macros para substituir XX para necessidades diferentes, semelhantes às três que forneci neste exemplo.
Comparação com X-Macros usando #include / #define / #undef
Embora seja semelhante ao X-Macros que outros mencionaram, acho que esta solução é mais elegante porque não requer #undefing nada, o que permite ocultar mais das coisas complicadas está na fábrica o arquivo de cabeçalho - o arquivo de cabeçalho é algo que você não toca em nada quando precisa definir um novo enum; portanto, a nova definição de enum é muito mais curta e mais clara.
fonte
SOME_ENUM(XX)
é exatamente um X-macro (para ser mais preciso, o "formulário do usuário" que passa aXX
função em vez de usar#def
#undef
) e, por sua vez, todo o X-MACRO é então passado para DEFINE_ENUM que o usa. Para não tirar nada da solução - funciona bem. Só para esclarecer que é um uso de macros X.XX
por cima de reing#define
é que o primeiro padrão pode ser usado em expansões macro. Observe que as únicas outras soluções tão concisas como esta requerem a criação e inclusão múltipla de um arquivo separado para definir um novo enum.#define DEFINE_ENUM(EnumType) ...
, substituirENUM_DEF(...)
porEnumType(...)
e fazer com que o usuário diga#define SomeEnum(XX) ...
. O pré-processador C se expandirá contextualmenteSomeEnum
na invocação da macro quando seguido por parênteses e em um token regular caso contrário. (Claro, isso causa problemas se o usuário gosta de usarSomeEnum(2)
para lançar para o tipo enum em vez de(SomeEnum)2
oustatic_cast<SomeEnum>(2)
.)#define
e#undef
. No entanto, você discorda de que o "formulário do usuário" (sugerido, por exemplo, no final deste artigo ) seja um tipo de x-macro? Eu certamente sempre o chamei de x-macro também e nas bases de código C em que estive recentemente é a forma mais comum (essa é obviamente uma observação tendenciosa). Eu posso ter analisado o OP de forma errada.fonte
#define ENUM_END(typ) }; extern const char * typ ## _name_table[];
nodefs.h
arquivo - isso irá declarar sua tabela de nomes nos arquivos que você usa. (Não consigo descobrir uma boa maneira de declarar o tamanho da tabela, no entanto.) Além disso, pessoalmente, eu deixaria de fora o ponto-e-vírgula final, mas os méritos são discutíveis de qualquer maneira.typ
a linha#define ENUM_END(typ) };
?Definitivamente, há uma maneira de fazer isso - use macros X () . Essas macros usam o pré-processador C para construir enums, arrays e blocos de código a partir de uma lista de dados de origem. Você só precisa adicionar novos itens ao #define que contém a macro X (). A instrução switch se expandiria automaticamente.
Seu exemplo pode ser escrito da seguinte maneira:
Existem maneiras mais eficientes (ou seja, usando Macros X para criar uma matriz de string e um índice de enum), mas esta é a demonstração mais simples.
fonte
Eu sei que você tem algumas respostas boas e sólidas, mas você sabe sobre o operador # no pré-processador C?
Ele permite que você faça isso:
fonte
char const *kConstStr[]
C ou C ++ não fornecem essa funcionalidade, embora eu tenha precisado dela com frequência.
O código a seguir funciona, embora seja mais adequado para enums não esparsos.
Por não esparso, não quero dizer da forma
uma vez que tem grandes lacunas nele.
A vantagem desse método é que ele coloca as definições de enums e strings próximas umas das outras; ter uma instrução switch em uma função os lança. Isso significa que é menos provável que você mude um sem o outro.
fonte
BEIJO. Você fará todos os tipos de outras coisas de switch / case com seus enums, então por que a impressão deveria ser diferente? Esquecer um caso em sua rotina de impressão não é um grande negócio quando você considera que existem cerca de 100 outros lugares onde pode esquecer um caso. Basta compilar -Wall, que avisará sobre correspondências não exaustivas de maiúsculas e minúsculas. Não use "default" porque isso tornará a troca exaustiva e você não receberá avisos. Em vez disso, deixe o switch sair e lidar com o caso padrão assim ...
fonte
Tente converter enums C ++ em strings . Os comentários têm melhorias que resolvem o problema quando os itens enum têm valores arbitrários.
fonte
O uso de boost :: preprocessor torna possível uma solução elegante como a seguinte:
Etapa 1: incluir o arquivo de cabeçalho:
Etapa 2: declare o objeto de enumeração com a seguinte sintaxe:
Etapa 3: use seus dados:
Obtendo o número de elementos:
Obtendo a string associada:
Obtendo o valor enum da string associada:
Parece limpo e compacto, sem arquivos extras para incluir. O código que escrevi em EnumUtilities.h é o seguinte:
Existem algumas limitações, ou seja, as do boost :: preprocessor. Nesse caso, a lista de constantes não pode ser maior que 64 elementos.
Seguindo a mesma lógica, você também pode pensar em criar um enum esparso:
Nesse caso, a sintaxe é:
O uso é semelhante ao anterior (sem a função eName ## 2Enum, que você pode tentar extrapolar da sintaxe anterior).
Eu testei em mac e linux, mas esteja ciente de que boost :: preprocessor pode não ser totalmente portátil.
fonte
Ao mesclar algumas das técnicas aqui, eu vim com a forma mais simples:
fonte
Se você estiver usando o gcc, é possível usar:
Então é só ligar, por exemplo
fonte
Confira as ideias em Mu Dynamics Research Labs - Arquivo de blogs . Eu encontrei isso no início deste ano - esqueci o contexto exato em que me deparei com isso - e o adaptei a este código. Podemos debater os méritos de adicionar um E na frente; é aplicável ao problema específico abordado, mas não faz parte de uma solução geral. Guardei isso na minha pasta 'vinhetas' - onde guardo fragmentos de código interessantes para o caso de querer depois. Tenho vergonha de dizer que não anotei de onde veio essa ideia na época.
Cabeçalho: paste1.h
Fonte de exemplo:
Não necessariamente o uso mais limpo do mundo do pré-processador C - mas ele evita a gravação do material várias vezes.
fonte
Fazer algo tanto um identificador C quanto uma string
fonte
Se o índice enum for baseado em 0, você pode colocar os nomes em uma matriz de char * e indexá-los com o valor enum.
fonte
Discussão adicional sobre este método
Truques da diretiva do pré-processador para iniciantes
fonte
Eu criei uma classe simples templated
streamable_enum
que usos transmitir operadores<<
e>>
e baseia-se nastd::map<Enum, std::string>
:Uso:
fonte
Aqui está uma solução usando macros com os seguintes recursos:
escreva cada valor do enum apenas uma vez, então não há listas duplas para manter
não guarde os valores enum em um arquivo separado que será # incluído posteriormente, para que eu possa escrevê-lo onde quiser
não substitua o enum em si, ainda quero ter o tipo de enum definido, mas, além disso, quero poder mapear cada nome de enum para a string correspondente (para não afetar o código legado)
a pesquisa deve ser rápida, de preferência sem caixa de troca, para aqueles enormes enums
https://stackoverflow.com/a/20134475/1812866
fonte
Achei que uma solução como Boost.Fusion one para adaptar structs e classes seria bom, eles até tinham em algum ponto, usar enums como uma sequência de fusão.
Então, fiz apenas algumas pequenas macros para gerar o código para imprimir os enums. Isso não é perfeito e não tem nada a ver com o código clichê gerado pelo Boost.Fusion, mas pode ser usado como as macros do Boost Fusion. Quero realmente gerar os tipos necessários ao Boost.Fusion para integrar nessa infraestrutura que permite imprimir nomes de membros de estrutura, mas isso vai acontecer mais tarde, por enquanto são apenas macros:
A resposta antiga abaixo é muito ruim, por favor, não use isso. :)
Resposta antiga:
Tenho procurado uma maneira que resolva esse problema sem alterar muito a sintaxe da declaração de enums. Cheguei a uma solução que usa o pré-processador para recuperar uma string de uma declaração de enum stringificada.
Sou capaz de definir enums não esparsos como este:
E posso interagir com eles de diferentes maneiras:
Com base nas seguintes definições:
Quando eu vou precisar de suporte para esparsa enum e quando eu vou ter mais tempo eu vou melhorar as to_string e from_string implementações com boost :: xpressive, mas esta custos, com o tempo de compilação por causa da modelagem importante executada e os é gerado executáveis provavelmente será muito maior. Mas isso tem a vantagem de ser mais legível e fácil de manter do que esse código de manipulação manual de string feio. : D
Caso contrário, sempre usei boost :: bimap para realizar esses mapeamentos entre o valor enums e a string, mas ele deve ser mantido manualmente.
fonte
Como prefiro não usar macros por todos os motivos usuais, usei uma solução de macro mais limitada que tem a vantagem de manter a macro de declaração de enum livre. As desvantagens incluem ter que copiar e colar a definição de macro para cada enum e ter que adicionar explicitamente uma invocação de macro ao adicionar valores ao enum.
fonte