Enum с тэгом
От: remark Россия http://www.1024cores.net/
Дата: 12.09.07 19:32
Оценка: 32 (5)
По следам здесь
Автор: remark
Дата: 06.09.07

По просьбам трудящихся здесь
Автор: alexeiz
Дата: 11.09.07

Наиболее близко к здесь
Автор: shadone
Дата: 06.09.07


Если имеется соотв. проблема, то такое решение очень удобно. Как в UI коде, так и в логике. В UI делал, например, что один комбобокс заполняется всеми значениями енума, а второй только избранными по некоторому полю в тэге. В логике тоже можно делать какие-то проверки или конвертации.

Сначала примеры использования.

В качестве тэга связывается просто bool:
DEFINE_STRICT_ENUM_W_TAG( Color, int, false, bool,
((Red,        1,        "STR_RED",        (false)))    
((Yello,    3,        "STR_YELLO",    (false)))
((Green,    5,        "STR_GREEN",    (true)))
)

Первый параметр — имя, которое получит "енум"
Второй — низлежащий тип
Третий — признак, что это "строгий" енум, т.е. не может содержать значения кроме перечисленных
Четвёртый — тип тэга
Далее перечисляются элементы перечисления, где
Первый элемент — имя
Второй — числовой эквивалент (как в обычном енуме)
Третий — строковое представление "для пользователя"
Четвёртый — инициализатор для тэга



Пример посложнее:

struct my_tag
{
    bool flag;
    int code;

    my_tag(bool flag, int code);
};

DEFINE_STRICT_ENUM_W_TAG( Color2, int, false, my_tag,
((Red,        1,        "STR_RED",    (true, 16)))    
((Yello,    3,        "STR_YELLO",    (true, 100)))
((Green,    5,        "STR_GREEN",    (false, 200)))
)

Тут, я надеюсь, всё понятно по аналогии.

Эадание строкового представления "для пользователя" и тэга опционально.


Теперь как это можно собственно использовать в коде:
    Color2::Entries const& e = Color2::enumGetDescriptions();
    for (size_t i = 0; i != e.size(); ++i)
    {
        if (e[i].getTag().flag)
        {
            addToComboBox(e[i].getDesc().getExtName(), e[i].getTag().code);
        }
    }


Или так:
    Color2 c = Color2::Green;
    if (c.enumTag().flag)
    {
    }



Реализацию сейчас отдельно запостю — там много и не особо интересно.


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Реализация
От: remark Россия http://www.1024cores.net/
Дата: 12.09.07 19:34
Оценка:
namespace tools { namespace common 
{

namespace detail 
{
    /**    Пустой тэг для связывания со значением enum'а */
    struct EmptyTag
    {
    };
}


/**    Базовый класс для типобезопасных перечислений (enum)
 *    @template Derived Производный класс
 *    @template Type Тип, к которому/из которого должно приводиться данное перечисление
 *    @template bStrict Признак, что перечисление является строго безопасным,
 *            т.е. при попытке занести в него неизвестное значение кидается исключение.
 *            Иначе в перечислении могут содержаться неизвестные значения
 *
 *    Пример использования смотри в конце файла
 */
template<typename Derived, typename Type = int, bool bStrict = true, typename TagType = detail::EmptyTag>
class StrictEnumBase
{
    /**    Структура с описанием значения перечисления */
    class EntryDesc
    {
    public:
        /**    Конструктор
        */
        EntryDesc(Type value, const std::string& sIntName, const std::string& sExtName)
            : m_value(value)
            , m_sIntName(sIntName)
            , m_sExtName(sExtName)
        {}

        /**    Получить значение
        */
        Type getValue() const
        {
            return m_value;
        }

        /**    Получить внутреннее имя (для вывода в лог)
        */
        std::string getIntName() const
        {
            return m_sIntName;
        }

        /**    Получить внешнее имя (для вывода пользователю)
        */
        std::string getExtName() const
        {
            return m_sExtName;
        }

    private:
        /*const*/ Type m_value;                //!< значение
        /*const*/ std::string m_sIntName;    //!< внутреннее имя (для вывода в лог)
        /*const*/ std::string m_sExtName;    //!< внешнее имя (для вывода пользователю)
    };

    class EntryInfo;
    /**    Внутренний тип для хранения информации об элементах */
    typedef std::map<Type, EntryInfo> IntEntries;

public:
    /**    Информация, сопоставляемая с каждым элементом - описание и пользовательский тэг */
    class EntryInfo
    {
    public:
        /**    Конструктор
         */
        EntryInfo(const EntryDesc& desc, const TagType& tag)
            : m_desc(desc)
            , m_tag(tag)
        {}
        
        /**    Получить описание
         */
        const EntryDesc& getDesc() const
        {
            return m_desc;
        }

        /**    Получить тэг
         */
        const TagType& getTag() const
        {
            return m_tag;
        }

    private:
        /*const*/ EntryDesc m_desc;        //!< описание
        /*const*/ TagType m_tag;        //!< тэг
    };

    /**    Внешний тип для хранения информации об элементах */
    typedef std::vector<EntryInfo> Entries;

    /**    Оператор сравнения
     *    @param other Второй операнд
     *    @return Результат
     */
    bool operator < (const StrictEnumBase& other) const
    {
        return m_value < other.m_value;
    }

    /**    Оператор сравнения
     *    @param other Второй операнд
     *    @return Результат
     */
    bool operator > (const StrictEnumBase& other) const
    {
        return m_value > other.m_value;
    }

    /**    Оператор сравнения
     *    @param other Второй операнд
     *    @return Результат
     */
    bool operator <= (const StrictEnumBase& other) const
    {
        return m_value <= other.m_value;
    }

    /**    Оператор сравнения
     *    @param other Второй операнд
     *    @return Результат
     */
    bool operator >= (const StrictEnumBase& other) const
    {
        return m_value >= other.m_value;
    }

    /**    Оператор сравнения
     *    @param other Второй операнд
     *    @return Результат
     */
    bool operator == (const StrictEnumBase& other) const
    {
        return m_value == other.m_value;
    }

    /**    Оператор сравнения
     *    @param other Второй операнд
     *    @return Результат
     */
    bool operator != (const StrictEnumBase& other) const
    {
        return m_value != other.m_value;
    }

    /**    Получить значение перечисления
     */
    Type enumGetValue() const
    {
        return m_value;
    }

    /**    Установить значение перечисления
     *    @throw std::out_of_range Если устанавливается неправильное значение
     */
    void enumSetValue(Type value)
    {
        assertExist(value);
        m_value = value;
    }

    /**    Получить описание значения
     *    Можно получать и для неизвестных значений
     *    @return Описание значения
     */
    EntryDesc enumDesc() const
    {
        IntEntries::iterator iter = getEntries().find(m_value);
        if (iter == getEntries().end())
            return enumMakeEmptyDesc(m_value);
        return iter->second.getDesc();
    }

    /**    Получить тэг, связанный со значением
    *    Можно получать только для известных значений
    *    @return Тэг
    *    @throw std::out_of_range Если вызывается для неизвестного значения
    */
    const TagType& enumTag() const // throw (std::out_of_range)
    {
        IntEntries::iterator iter = getEntries().find(m_value);
        if (iter == getEntries().end())
            throw std::out_of_range("StrictEnum: out of range");
        return iter->second.getTag();
    }

    /**    Проверить, является ли значение известным
     */
    bool enumIsKnown() const
    {
        return enumIsExist(m_value);
    }

    /******************    Статические функции типа ******************/

    /**    Проверка на вхождение заданного числа в перечисление
     */
    static bool enumIsExist(Type value)
    {
        return getEntries().find(value) != getEntries().end();
    }

    /**    Создать объект из значения
     *    @throw std::out_of_range Если устанавливается неправильное значение
     */
    static Derived enumFromValue(Type value)
    {
        assertExist(value);
        return Derived(value);
    }

    /**    Создать объект из значения
     *    Если значение неправильное, то возвращает значение по-умолчанию
     *    @param defaultValue Значение по-умолчанию
     */
    static Derived enumFromValue(Type value, Derived defaultValue)
    {
        if (enumIsExist(value))
            return Derived(value);
        else
            return defaultValue;
    }

    /**    Получить описания всех известных значений перечисления
     */
    static Entries enumGetDescriptions()
    {
        // Копируем описания элементов из внутреннего представления во внешнее
        IntEntries intEntries = getEntries();
        Entries entries;
        entries.reserve(intEntries.size());
        IntEntries::const_iterator iter = intEntries.begin();
        IntEntries::const_iterator end = intEntries.end();
        for (; iter != end; ++iter)
            entries.push_back(iter->second);
        return entries;
    }

protected:
    /**    Тип значения */
    typedef Type ValueType;

    /**    Конструктор для известных значений
     */
    StrictEnumBase(Type value, const std::string& sIntName, const std::string& sExtName, const TagType& tag = TagType())
        : m_value(value)
    {
        getEntries().insert(std::make_pair(value, EntryInfo(EntryDesc(value, sIntName, sExtName), tag)));
    }

    /**    Конструктор для неизвестных значений
    */
    StrictEnumBase(Type value)
        : m_value(value)
    {
    }

private:
    /**    Непосредственно значение */
    Type m_value;

    /**    Получить множество значений, которые могут содержаться в данном перечислении
     */
    static IntEntries& getEntries()
    {
        static IntEntries entries;
        return entries;
    }

    /**    Проверить, что значение является известным
     *    @throw std::out_of_range Если неизвестное значение
     */
    static void assertExist(Type value)
    {
        if (bStrict && !enumIsExist(value))
            throw std::out_of_range("StrictEnum: out of range");
    }

    /**    Создать описание для неизвестного значения
     */
    static EntryDesc enumMakeEmptyDesc(Type value)
    {
        std::ostringstream stream;
        stream << "<" << value << ">";
        return EntryDesc(value, stream.str(), "");
    }
};




#define DEFINE_STRICT_ENUM(EnumName, Type, strict_f, seq) \
class EnumName : public tools::common::StrictEnumBase<EnumName, Type, strict_f> { \
    friend class tools::common::StrictEnumBase<EnumName, Type, strict_f>; \
    EnumName(ValueType value, const std::string& sIntName, const std::string& sExtName) \
        : tools::common::StrictEnumBase<EnumName, Type, strict_f>(value, sIntName, sExtName) \
        {} \
    EnumName(ValueType value) \
        : tools::common::StrictEnumBase<EnumName, Type, strict_f>(value) \
        {} \
public: \
    BOOST_PP_SEQ_FOR_EACH_I(DEFINE_STRICT_ENUM_DEF, EnumName, seq);\
};\
    BOOST_PP_SEQ_FOR_EACH_I(DEFINE_STRICT_ENUM_DECL, EnumName, seq);

#define DEFINE_STRICT_ENUM_DEF(r, aux, i, record) \
    static const aux BOOST_PP_TUPLE_ELEM(2, 0, record);

#define DEFINE_STRICT_ENUM_DECL(r, aux, i, record) \
    __declspec(selectany) const aux aux::BOOST_PP_TUPLE_ELEM(2, 0, record)(BOOST_PP_TUPLE_ELEM(2, 1, record), BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(2, 0, record)), "");





#define DEFINE_STRICT_ENUM_W_NAME(EnumName, Type, strict_f, seq) \
class EnumName : public tools::common::StrictEnumBase<EnumName, Type, strict_f> { \
    friend class tools::common::StrictEnumBase<EnumName, Type, strict_f>; \
    EnumName(ValueType value, const std::string& sIntName, const std::string& sExtName) \
    : tools::common::StrictEnumBase<EnumName, Type, strict_f>(value, sIntName, sExtName) \
        {} \
    EnumName(ValueType value) \
        : tools::common::StrictEnumBase<EnumName, Type, strict_f>(value) \
    {} \
public: \
    BOOST_PP_SEQ_FOR_EACH_I(DEFINE_STRICT_ENUM_W_NAME_DEF, EnumName, seq);\
};\
    BOOST_PP_SEQ_FOR_EACH_I(DEFINE_STRICT_ENUM_W_NAME_DECL, EnumName, seq);

#define DEFINE_STRICT_ENUM_W_NAME_DEF(r, aux, i, record) \
    static const aux BOOST_PP_TUPLE_ELEM(3, 0, record);

#define DEFINE_STRICT_ENUM_W_NAME_DECL(r, aux, i, record) \
    __declspec(selectany) const aux aux::BOOST_PP_TUPLE_ELEM(3, 0, record)(BOOST_PP_TUPLE_ELEM(3, 1, record), BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(3, 0, record)), BOOST_PP_TUPLE_ELEM(3, 2, record));





#define DEFINE_STRICT_ENUM_W_TAG(EnumName, Type, strict_f, tag_t, seq) \
class EnumName : public tools::common::StrictEnumBase<EnumName, Type, strict_f, tag_t> { \
    friend class tools::common::StrictEnumBase<EnumName, Type, strict_f, tag_t>; \
    EnumName(ValueType value, const std::string& sIntName, const std::string& sExtName, const tag_t& tag) \
        : tools::common::StrictEnumBase<EnumName, Type, strict_f, tag_t>(value, sIntName, sExtName, tag) \
        {} \
    EnumName(ValueType value) \
        : tools::common::StrictEnumBase<EnumName, Type, strict_f, tag_t>(value) \
        {} \
public: \
    BOOST_PP_SEQ_FOR_EACH_I(DEFINE_STRICT_ENUM_W_TAG_DEF, EnumName, seq);\
};\
    BOOST_PP_SEQ_FOR_EACH_I(DEFINE_STRICT_ENUM_W_TAG_DECL, (EnumName)(tag_t), seq);

#define DEFINE_STRICT_ENUM_W_TAG_DEF(r, aux, i, record) \
    static const aux BOOST_PP_TUPLE_ELEM(4, 0, record);

#define DEFINE_STRICT_ENUM_W_TAG_DECL(r, aux, i, record) \
    __declspec(selectany) const BOOST_PP_SEQ_ELEM(0, aux) BOOST_PP_SEQ_ELEM(0, aux)::BOOST_PP_TUPLE_ELEM(4, 0, record)(BOOST_PP_TUPLE_ELEM(4, 1, record), BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(4, 0, record)), BOOST_PP_TUPLE_ELEM(4, 2, record), BOOST_PP_SEQ_ELEM(1, aux)BOOST_PP_TUPLE_ELEM(4, 3, record));

}}



R>


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Enum с тэгом
От: IROV..  
Дата: 13.09.07 09:51
Оценка:
Здравствуйте, remark, Вы писали:

А я бы за яйца и на вешалку

А если серьездно, зачем?
неужели, это все реально нужно?
я не волшебник, я только учусь!
Re[2]: Enum с тэгом
От: remark Россия http://www.1024cores.net/
Дата: 13.09.07 09:57
Оценка: +1
Здравствуйте, IROV.., Вы писали:

IRO>неужели, это все реально нужно?


Мне или тебе?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Enum с тэгом
От: green.nsk  
Дата: 19.09.07 05:19
Оценка: +1
Всегда было интересно, где ты работаешь, что такие "приколы" постишь регулярно? Ты их по роду деятельности целенаправленно придумываешь или как?

Приколы — потому что многим вещам найти применение достаточно сложно (на мой взгляд), а уж тем более не получается представить проект, где такое огромное количество подобных вещей, применены по делу.
Re[2]: Enum с тэгом
От: remark Россия http://www.1024cores.net/
Дата: 20.09.07 09:28
Оценка:
Здравствуйте, green.nsk, Вы писали:

GN>Всегда было интересно, где ты работаешь, что такие "приколы" постишь регулярно? Ты их по роду деятельности целенаправленно придумываешь или как?


Я просто думаю...


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Enum с тэгом
От: _vvs Россия  
Дата: 20.09.07 11:43
Оценка: +2
Здравствуйте, green.nsk, Вы писали:

<skipped>

<offtopic>
Я, честно говоря, не вижу никаких проблем в том, что remark выкладывает такие посты на данный форум
Форум для разработчиков С++. Решение, которое он предложил, работает.
Вас же он не заставляют пользоваться данным решением, а многим оно наверняка будет интересно
IMHO такие вещи можно и нужно обсуждать на данном форуме
</offtopic>
Re[3]: Enum с тэгом
От: green.nsk  
Дата: 21.09.07 02:41
Оценка:
Нет, я ни в коем случае не осуждаю и сам с интересом читаю (пусть и не всё). Мне просто интересно, откуда такие вещи берутся: получаются при решении реальных жизненых задач или просто развлечение.

Ну в общем-то, я ответ на свой вопрос получил
Re[4]: Enum с тэгом
От: remark Россия http://www.1024cores.net/
Дата: 21.09.07 12:17
Оценка: 2 (1)
Здравствуйте, green.nsk, Вы писали:

GN>Нет, я ни в коем случае не осуждаю и сам с интересом читаю (пусть и не всё). Мне просто интересно, откуда такие вещи берутся: получаются при решении реальных жизненых задач или просто развлечение.



Когда как. Определенно такие решения не для каждого случая. Такие решения хорошо подходят только для определенных случаев. Но что бы иметь возможность применить такое решение в определённом случае, его надо знать, знать что такое возможно.
Какие-то решения я иногда применяю на практике, какие-то — не доводилось.
Фишка обычно не в самом конкретном применении, а в низлежащей фиче или приёме. Главное для меня — владеть приёмом. Возможно есть такая проблема, для которой сейчас просто нет хорошего решения, и такой приём способен её решить. Но при этом я просто ещё не сталкивался с этой проблемой. Поэтому какие-то приёмы сейчас могут выглядеть надуманными. Но при этом возможно я когда-то сталкнусь с этой проблемой, и пойму, что этот приём идеально подходит для её решения.
Сам уже запутался, что пишу. Ну вот например, Александреску придумал mojo. Вещь очень крутая и не имеющая аналогов, и не моделируемая другими средствами. Скорее всего он не сразу додумался применить именно этот приём для решения именно этой проблемы. Допустим вначале он просто додумался до возможности различать некоторые объекты при некоторых условиях, основываясь на некоторых правилах С++. Но пока не было подходящей проблемы, это выглядело глупо и слишком сложно и не нужно. А потом он сталкнулся с проблемой отличения временных объектов, и тогда всё встало на свои места.
Я надеюсь, что хотя бы часть людей рассматривает мои идеи как "открытый вопрос" и как возможность для дальнейшего развития. И возможно потом кто-то наткнётся на какую-то проблему, и запостит какое-то офигительное решение для неё... Как говорится "одна голова — хорошо, а две лучше"...
Но при этом возможно что идея и на самом деле — фуфло, и никакой проблемы она не решает в принципе. Проблема в том, что это не известно...



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.