从 C++ 创建字符串列表和枚举列表;宏

发布于 2024-10-29 22:24:35 字数 235 浏览 7 评论 0原文

为了使我的代码更短且更容易更改,我想

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

用宏替换类似的内容,例如 INIT(AAA, BBB, CCC);但是当我尝试使用可变参数执行宏并进行字符串化时,我收到错误,因为未声明参数。

关于如何做到这一点有什么想法吗?

In order to make my code shorter and easier to change I want to replace something like

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

With a macro, like INIT(AAA, BBB, CCC); but when I try doing a macro with variable arguments, and stringification I get an error as the arguments are not declared.

Any idea on how to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

耳钉梦 2024-11-05 22:24:35

这是我几天前学到的解决方案。解决您问题的简化版本是:

#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);

但是您可以有一个改进的版本,带有函数调用,如下所示:

#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);

这将变成:

  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]; };

您甚至可以使用第一个元素的偏移,就像这个:

#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);

我希望这有帮助。

小心,
Beco

参考:

打印月份问题,作者:Kush,回答者:丹尼·瓦罗德

Here a solution I learned a few days ago. The simplified version that attends your question is:

#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);

But you can have an improved version, with a function call, like this:

#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);

This will grow to be:

  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]; };

You can even use an offset for the first element, like this one:

#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);

I hope this helps.

Take care,
Beco

Reference:

Print the month question, by Kush, answer by Danny Varod

云巢 2024-11-05 22:24:35

您可以使用一些宏魔法来做到这一点:

#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 };

这是一个测试程序:

#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;
}

它输出:

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

You can do it with a bit of macro magic:

#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 };

Here is a test program:

#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;
}

It outputs:

I like Mango
I do not like Banana
Apple is a fruit
Mango is a fruit
Is Tomato a fruit?
维持三分热 2024-11-05 22:24:35

这是我的解决方案:

#define FRUITS(fruit) \
  fruit(Apple)        \
  fruit(Orange)       \
  fruit(Banana)       

#define CREATE_ENUM(name) \
  F_##name,

#define CREATE_STRINGS(name) \
  #name,

诀窍是“fruit”是宏“FRUITS”的参数
并将被您传递给的内容所取代。
例如:

FRUITS(CREATE_ENUM)

将扩展为:

F_Apple, F_Orange, F_Banana, 

让我们创建枚举和字符串数组:

enum fruit {
  FRUITS(CREATE_ENUM)
};

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

Here is my solution:

#define FRUITS(fruit) \
  fruit(Apple)        \
  fruit(Orange)       \
  fruit(Banana)       

#define CREATE_ENUM(name) \
  F_##name,

#define CREATE_STRINGS(name) \
  #name,

The trick is that 'fruit' is an argument of the macro 'FRUITS'
and will be replaced by what ever you pass to.
For example:

FRUITS(CREATE_ENUM)

will expand to this:

F_Apple, F_Orange, F_Banana, 

Lets create the enum and the string array:

enum fruit {
  FRUITS(CREATE_ENUM)
};

const char* fruit_names[] = {
  FRUITS(CREATE_STRINGS)
};
肤浅与狂妄 2024-11-05 22:24:35

下面是使用 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) 是树元素 AAA、BBB 和 CCC 的 Boost.Preprocessor 序列;该宏将枚举名称附加到其模式中:

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

Here is a solution with 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) is a Boost.Preprocessor sequence of tree elements AAA, BBB and CCC; the macro append the enum name to it's modalities:

enum E { E_AAA, E_BBB, E_CCC };
static const char* E_strings[] = { "AAA", "BBB", "CCC" };
傾旎 2024-11-05 22:24:35

一种方法是使用 X-Macros,它基本上是定义宏的方法,然后用于生成比简单宏容易允许的更复杂的结构。 这是一个完全按照您的要求进行操作的示例

One way to do this is with X-Macros, which are basically a way to define a macro which is then used for generating more complex structures than a simple macro easily allows. Here is an example of doing exactly what you are asking.

〃安静 2024-11-05 22:24:35

我参加聚会有点晚了,但这里有另一个建议。
它创建一个强类型枚举类,例如 MyEnumName 和一个配套的静态帮助器类 Enumator
它比以前的答案更大,因为它具有更多功能,例如用于从/到字符串转换的流运算符。
请注意,由于使用索引序列,它依赖于 c++ 14 标准。

用途:

/* 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";
}

来源:

    #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__ }

I am a bit late to the party but here is another suggestion.
It creates a strongly typed enum class, say MyEnumName and a companion static helper class Enumator<MyEnumName>.
It's bigger than previous answers as it has more features, e.g. stream operators for conversion from/to string.
Note that it relies on c++ 14 standard due to the use of index sequence.

Usage:

/* 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";
}

Source:

    #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__ }
暖树树初阳… 2024-11-05 22:24:35

处理这个问题的一种方法是定义一个列表宏,即扩展为另一个留给用户定义的宏。例如:

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

定义 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

定义字符串: 就

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

我个人而言,我发现这比 X 宏更容易使用,因为它不依赖于 include-文件魔法。

One way to handle this is to define a list macro, i.e. something that expands to another macro that is left for the user to define. For example:

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

To define the 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

To define the string:

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

Personally, I find this much easier to work with than the X macro, as this does not rely in include-file magic.

安静 2024-11-05 22:24:35

对于一个简单的解决方案,我建议类似 X 宏

对于添加其他几个功能(如范围检查、增强类型安全、可选关联数据等)的更复杂的解决方案,有一个建议(但从未最终确定)Boost.Enum 库

For a simple solution, I'd recommend something like X-Macros.

For a more complex solution that adds several other features (like range checking, enhanced type safety, optional associated data, etc.), there's a proposed (but never finalized) Boost.Enum library.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文