what macro define __va_args__ c++ c macros c-preprocessor variadic

define - macros in c++



Crear una lista de cadenas y una lista de enumeraciĆ³n desde una macro de C++ (8)

Aquí está mi solución:

#define FRUITS(fruit) / fruit(Apple) / fruit(Orange) / fruit(Banana) #define CREATE_ENUM(name) / F_##name, #define CREATE_STRINGS(name) / #name,

El truco es que ''fruto'' es un argumento de la macro ''FRUTOS'' y será reemplazado por lo que pase. Por ejemplo:

FRUITS(CREATE_ENUM)

se ampliará a esto:

F_Apple, F_Orange, F_Banana,

Permite crear la enumeración y la matriz de cadena:

enum fruit { FRUITS(CREATE_ENUM) }; const char* fruit_names[] = { FRUITS(CREATE_STRINGS) };

Para poder hacer que mi código sea más corto y fácil de cambiar, quiero reemplazar algo como

enum{ E_AAA, E_BBB, E_CCC }; static const char *strings{"AAA", "BBB", "CCC" };

Con una macro, como INIT (AAA, BBB, CCC); pero cuando intento hacer una macro con argumentos variables, y la clasificación, aparece un error porque los argumentos no se declaran.

¿Alguna idea de como hacerlo?


Aquí hay una solución con Boost.Preprocessor:

#include <boost/preprocessor.hpp> #define DEFINE_ENUM_DECL_VAL(r, name, val) BOOST_PP_CAT(name, BOOST_PP_CAT(_, val)) #define DEFINE_ENUM_VAL_STR(r, name, val) BOOST_PP_STRINGIZE(val) #define DEFINE_ENUM(name, val_seq) / enum name { / BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(DEFINE_ENUM_DECL_VAL, name, val_seq)) / }; / static const char* BOOST_PP_CAT(name, _strings[] = ) { / BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(DEFINE_ENUM_VAL_STR, name, val_seq)) / }; DEFINE_ENUM(E, (AAA)(BBB)(CCC))

(AAA)(BBB)(CCC) es una secuencia de preprocesador Boost de elementos de árbol AAA, BBB y CCC; la macro agrega el nombre de enumeración a sus modalidades:

enum E { E_AAA, E_BBB, E_CCC }; static const char* E_strings[] = { "AAA", "BBB", "CCC" };


Aquí una solución que aprendí hace unos días. La versión simplificada que atiende tu pregunta es:

#define ENUM_MACRO(name, v1, v2, v3, v4, v5, v6, v7)/ enum name { v1, v2, v3, v4, v5, v6, v7};/ const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7}; ENUM_MACRO(Week, Sun, Mon, Tue, Wed, Thu, Fri, Sat);

Pero puedes tener una versión mejorada, con una llamada a función , como esta:

#define ENUM_MACRO(name, v1, v2, v3, v4, v5, v6, v7)/ enum name { v1, v2, v3, v4, v5, v6, v7};/ const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7};/ const char *name##ToString(value) { return name##Strings[value]; } ENUM_MACRO(Week, Sun, Mon, Tue, Wed, Thu, Fri, Sat);

Esto crecerá para ser:

enum Week { Sun, Mon, Tue, Wed, Thu, Fri, Sat}; const char *WeekStrings[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; const char *WeekToString(value) { return WeekStrings[value]; };

Incluso puedes usar un desplazamiento para el primer elemento, como este:

#define ENUM_MACRO(name, offset, v1, v2, v3, v4, v5, v6, v7)/ enum name { v1 = offset, v2, v3, v4, v5, v6, v7};/ const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7};/ const char *name##ToString(value) { return name##Strings[value - offset ]; } ENUM_MACRO(Week, 1, Sun, Mon, Tue, Wed, Thu, Fri, Sat);

Espero que esto ayude.

Cuidate beco

Referencia:

Imprime la pregunta del mes , por Kush, respuesta por Danny Varod


Llego un poco tarde a la fiesta pero aquí hay otra sugerencia.
Crea una clase enum fuertemente MyEnumName , por ejemplo, MyEnumName y una clase auxiliar auxiliar estática Enumator<MyEnumName> .
Es más grande que las respuestas anteriores, ya que tiene más funciones, por ejemplo, operadores de flujo para la conversión de / a cadena.
Tenga en cuenta que se basa en el estándar c ++ 14 debido al uso de la secuencia de índice.

Uso:

/* One line definition - no redundant info */ ENUM_DEFINE(WeekDay /*first item is enum name*/, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday); /* works seemlessly with streams (good for logging) */ auto dayOne = WeekDay::Sunday; std::cout << "day of week is: " << day_of_week; /* explicit construction from string using WeekDay_enum companion class*/ auto dayTwo = Enumator<WeekDay>::fromString("Tuesday"); /*Iterate over all enum values using Enumator<WeekDay> companion class*/ std::cout << "Days of the week are:/n" for (auto enumVal : Enumator<WeekDay>::getValues()) { std::cout << enumVal << "/n"; }

Fuente:

#include <array> #include <string> #include <sstream> #include <stdexcept> template<typename E> using isEnum = typename std::enable_if<std::is_enum<E>::value>::type; template<typename E, typename = isEnum<E>> constexpr static int enumSize() { return 0; } template<typename E, typename = isEnum<E>> inline static std::string getEnumStringValues() { return ""; } /*Enum companion class to hold the methods that can''t be declared in an enum*/ template<typename EnumType, isEnum<EnumType>* = nullptr> class Enumator { Enumator() = delete; /* prevents instantiation */ public: constexpr static int size() { return enumSize<EnumType>(); } /* list of all enum values a string */ static auto const& getValuesStr() { static std::array<std::string, size()> values; if (values[0].empty()) { std::string valuesStr = getEnumStringValues<EnumType>(); std::stringstream ss(valuesStr); for (auto& value : values) { std::getline(ss, value, '',''); } } return values; }; /* list of all enum values */ static auto const& getValues() { static std::array<EnumType, size()> values{ make_array(std::make_index_sequence<size()>()) }; return values; }; /* To/from string conversion */ constexpr static std::string const& toString(EnumType arg) { return getValuesStr()[static_cast<unsigned>(arg)]; } static EnumType fromString(std::string const& val) { /* Attempt at converting from string value */ auto const& strValues = getValuesStr(); for (unsigned int i = 0; i < strValues.size(); i++) { if (val == strValues[i]) { return static_cast<EnumType>(i); } } throw std::runtime_error("No matching enum value found for token: " + val); } private: /* Helper method to initialize array of enum values */ template<std::size_t...Idx> static auto make_array(std::index_sequence<Idx...>) { return std::array<EnumType, size()>{{static_cast<EnumType>(Idx)...}}; } }; template<typename EnumType, isEnum<EnumType>* = nullptr> inline std::istream& operator>> (std::istream& input, EnumType& arg) { std::string val; input >> val; arg = Enumator<EnumType>::fromString(val); return input; } template<typename EnumType, isEnum<EnumType>* = nullptr> inline std::ostream& operator<< (std::ostream& output, const EnumType& arg) { return output << Enumator<EnumType>::toString(arg); } #define ENUM_DEFINE(EnumName,...)/ / enum class EnumName;/ / template<>/ constexpr int enumSize<EnumName>() {/ /*Trick to get the number of enum members:*// /*dump all the enum values in an array and compute its size *// enum EnumName { __VA_ARGS__ }; / EnumName enumArray[]{ __VA_ARGS__ }; / return sizeof(enumArray) / sizeof(enumArray[0]); / }/ / template<>/ inline std::string getEnumStringValues<EnumName>() { return #__VA_ARGS__; }/ / enum class EnumName : int { __VA_ARGS__ }


Para una solución simple, recomendaría algo como X-Macros .

Para una solución más compleja que agrega varias otras características (como verificación de rango, seguridad de tipo mejorada, datos asociados opcionales, etc.), hay una biblioteca Boost.Enum propuesta (pero nunca finalizada).


Puedes hacerlo con un poco de magia macro:

#define FRUITS / etype(Unknown), / etype(Apple), / etype(Orange), / etype(Banana), / etype(Apricot), / etype(Mango) #define etype(x) F_##x typedef enum { FRUITS } Fruit; #undef etype #define etype(x) #x static const char *strFruit[] = { FRUITS };

Aquí hay un programa de prueba:

#include <iostream> #include <exception> #include <vector> #define FRUITS / etype(Unknown), / etype(Apple), / etype(Orange), / etype(Banana), / etype(Apricot), / etype(Mango) #define etype(x) F_##x typedef enum { FRUITS } Fruit; #undef etype #define etype(x) #x static const char *strFruit[] = { FRUITS }; const char *enum2str (Fruit f) { return strFruit[static_cast<int>(f)]; } Fruit str2enum (const char *f) { const int n = sizeof(strFruit) / sizeof(strFruit[0]); for (int i = 0; i < n; ++i) { if (strcmp(strFruit[i], f) == 0) return (Fruit) i; } return F_Unknown; } int main (int argc, char *argv[]) { std::cout << "I like " << enum2str(F_Mango) << std::endl; std::cout << "I do not like " << enum2str(F_Banana) << std::endl; std::vector<char *> v; v.push_back("Apple"); v.push_back("Mango"); v.push_back("Tomato"); for (int i = 0; i < v.size(); ++i) { const Fruit f = str2enum(v[i]); if (f == F_Unknown) std::cout << "Is " << v[i] << " a fruit?" << std::endl; else std::cout << v[i] << " is a fruit" << std::endl; } return 0; }

Produce:

I like Mango I do not like Banana Apple is a fruit Mango is a fruit Is Tomato a fruit?


Una forma de hacerlo es con X-Macros , que son básicamente una forma de definir una macro que luego se utiliza para generar estructuras más complejas de lo que una simple macro permite fácilmente. Aquí hay un ejemplo de cómo hacer exactamente lo que estás preguntando.


Una forma de manejar esto es definir una macro de lista , es decir, algo que se expande a otra macro que queda para que el usuario la defina. Por ejemplo:

#define MY_LIST MY_ENTRY(AAA) MY_ENTRY(BBB) MY_ENTRY(CCC)

Para definir la enum :

#define MY_ENTRY(x) E_##x, enum name { MY_LIST NUMBER_OF_ELEMENTS /* Needed to eat trailing comma (not needed in C99, but in C++) */ }; #undef MY_ENTRY

Para definir la cadena:

#define MY_ENTRY(x) #x, static const char *strings[] = { MY_LIST }; #undef MY_ENTRY

Personalmente, encuentro que es mucho más fácil trabajar con él que la macro X , ya que no se basa en la inclusión de archivos mágicos.