С++ пометить значение перечисления как устаревшее?

Можно ли пометить значение перечисления как устаревшее?

e.g.

enum MyEnum {
    firstvalue = 0
    secondvalue,
    thirdvalue, // deprecated
    fourthvalue
};

Вторым призовым решением было бы решение ifdef для MSVC и GCC.


person moala    schedule 30.03.2011    source источник
comment
Что вы хотите, чтобы произошло? Просто переименуйте его, и компилятор выдаст ошибку...   -  person Lindydancer    schedule 30.03.2011
comment
@Lindydancer: Но это выходит за рамки устаревания, не так ли? Я думаю, что смысл в том, чтобы разрешить компиляцию существующего кода, но выдать предупреждение об устаревшем ресурсе.   -  person Fred Larson    schedule 30.03.2011
comment
@Lindydancer: если бы этого было достаточно, не потребовалось бы устаревания классов, функций, типов...   -  person moala    schedule 30.03.2011
comment
Можно ли 1) переименовать устаревшее значение перечисления, 2) #define макрос, который сопоставляет устаревший токен с переименованным токеном, но также включает текст предупреждения, например, прагму или что-то в этом роде?   -  person JCooper    schedule 30.03.2011
comment
@JCooper: звучит как интересное решение, используя _Pragma, вы действительно можете включить прагму в расширение макроса. Почему вы не публикуете ответ?   -  person Matthieu M.    schedule 30.03.2011
comment
Надеюсь, правильное решение/хак также будет работать с перечислениями с ограниченной областью видимости в C++0x =]   -  person David    schedule 30.03.2011
comment
Привет @moala, ни один из ответов еще не принят, но можете ли вы принять stackoverflow.com/a/20266156/194921 как современный способ сделать это? (начиная с С++ 14)   -  person Paul Du Bois    schedule 08.08.2020


Ответы (8)


вы можете сделать это:

enum MyEnum {
    firstvalue = 0,
    secondvalue,
    thirdvalue, // deprecated
    fourthvalue
};
#pragma deprecated(thirdvalue)

тогда, когда когда-либо будет использоваться переменная, компилятор выведет следующее:

warning C4995: 'thirdvalue': name was marked as #pragma deprecated

EDIT
Это выглядит немного хакерским, и у меня нет компилятора GCC, чтобы подтвердить (может ли кто-нибудь сделать это для меня?), но это должно работать:

enum MyEnum {
    firstvalue = 0,
    secondvalue,
#ifdef _MSC_VER
    thirdvalue,
#endif
    fourthvalue = secondvalue + 2
};

#ifdef __GNUC__
__attribute__ ((deprecated)) const MyEnum thirdvalue = MyEnum(secondvalue + 1);
#elif defined _MSC_VER
#pragma deprecated(thirdvalue)
#endif

это комбинация моего ответа и ответа MSalters

person Tom    schedule 30.03.2011
comment
Вы знаете, можно ли сделать его красивее? без промежуточного #ifdef _MSC_VER? Я пытаюсь написать макрос для кросс-платформенной обработки, но это делает его немного более неприятным для конечного пользователя. Было бы неплохо, если бы конечный пользователь мог использовать автономный макрос. - person lpapp; 28.11.2013
comment
Еще один момент, чтобы иметь его без ifdef, это потенциально может нарушить двоичную совместимость на основе базового представления enum на данной платформе. - person lpapp; 03.12.2013
comment
Хорошее решение, но, к сожалению, оно не будет работать для кода C++11, использующего область перечисления следующим образом: MyEnum test = MyEnum::thirdvalue; - person Konstantin; 24.05.2018

Вы можете использовать атрибут [[deprecated]] начиная с C++14.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3760.html

person lpapp    schedule 28.11.2013
comment
пример будет выглядеть так: enum E { A [[deprecated]] = 3, B }; - person Michał Walenciak; 12.10.2018

Начиная с GCC 6 вы можете просто отказаться от перечислений:

enum {
  newval,
  oldval __attribute__ ((deprecated ("too old")))
};

Источник: https://gcc.gnu.org/gcc-6/changes.html

person usr1234567    schedule 13.10.2015

Вы можете объявить константы перечисления вне объявления перечисления:

enum MyEnum {
    firstvalue = 0
    secondvalue,
    thirdvalue
};
__attribute__ ((deprecated)) const MyEnum fourthvalue = MyEnum(thirdvalue + 1);
person MSalters    schedule 30.03.2011
comment
Однако это не будет работать ни для msvc, ни для перечислений классов С++ 11. - person lpapp; 29.11.2013
comment
Он также ломается, если компилятор выбирает базовый тип для MyEnum, который недостаточно велик для хранения присваиваемого вами значения (thirdvalue + 1). - person Tony Delroy; 10.07.2014

Ну, раз мы уже на макро-хаках, то вот мой :-)

enum MyEnum
{
 foo,
 bar,
 baz
};

typedef __attribute__ ((deprecated))MyEnum MyEnum_deprecated;
#define bar ((MyEnum_deprecated) bar)

int main ()
{
    int a = foo; // yuck, why did C++ ever allow that...
    int b = bar;

    MyEnum c = foo;
    MyEnum d = bar;

    return 0;
}

Это работает с gcc и не требует нарушения безопасности типов. К сожалению, он по-прежнему злоупотребляет вашим кодом с помощью макросов, так что мда. Но, насколько я мог понять, это настолько хорошо, насколько это возможно.

Предложение, сделанное Томом, намного чище (я полагаю, работает для MSVC), но, к сожалению, единственное сообщение, которое даст вам gcc, - это "игнорирование прагмы".

person Damon    schedule 30.03.2011
comment
Будет ли это правильно работать с перечислением внутри класса или пространства имен? - person lpapp; 28.11.2013
comment
Поскольку это макро-хак, он будет работать с перечислениями внутри пространств имен даже лучше, чем вам хотелось бы. Это недостаток макросов, они просто заменяют текст и не учитывают пространства имен. К сожалению, вы не можете назначить атрибут одному счетчику, а только всему объекту. Таким образом, вы должны сделать такой вид взлома, чтобы заставить его работать. - person Damon; 28.11.2013
comment
На самом деле это не будет работать с пространствами имен без дополнительных неудобств для пользователя, поэтому это решение -1 с моей стороны. Кроме того, у него есть ограничение, заключающееся в том, что ни один общий макрос не может его обернуть, поскольку макрос внутри макроса сломается... Он даже сломается с перечислениями классов С++ 11. - person lpapp; 29.11.2013
comment
Однако вопрос не был ни в пространствах имен, ни в C++11. Кроме того, я не вижу причин, по которым это не должно работать с перечислениями С++ 11, если вы используете их с правильной квалификацией, как вы все равно должны это делать. Также почему вы думаете, что вложенные макросы не будут работать? Они работают в другом месте, почему бы и нет в этом случае? - person Damon; 29.11.2013
comment
Деймон: как бы ты заставил это работать с вложенными макросами? Насколько я вижу, определение будет оцениваться там, где оно определено, а не в фактическом вызове макроса, связанном с переопределением, не так ли? Пожалуйста, выделите мое имя в следующий раз, иначе я могу пропустить ответ, как здесь. - person lpapp; 03.12.2013

Использование зависимых от компилятора прагм: Вот документация для Gcc и Visual Studio.

person Sadique    schedule 30.03.2011
comment
Это на самом деле не отвечает на вопрос, например. gcc позволяет помечать функции, типы и переменные как устаревшие, но не упоминает о применении этого к конкретным значениям перечисления. - person Paul R; 30.03.2011
comment
К сожалению, gcc не позволяет указать атрибут для значения перечисления, а только для всего типа перечисления. Конечно, вы можете создать два перечисления (одно с устаревшими значениями) в качестве обходного пути, но тогда, конечно, они будут иметь разные типы. - person Damon; 30.03.2011
comment
@Damon: может быть, со ссылкой, это был бы отличный ответ. - person moala; 30.03.2011
comment
Ссылкой будет сообщение об ошибке компилятора (в прошлом я пробовал то же самое!) И отсутствие упоминания в документах, тогда как для типов, переменных и функций есть явное упоминание. К сожалению, мое решение не так хорошо, как кажется, поскольку оно обязательно потребует приведения перечислений к int. Есть веская причина, почему перечисления типизированы (даже сильнее в C++0x), подделка и обман системы типов - это не то, что обычно хотелось бы делать. - person Damon; 30.03.2011
comment
На самом деле существует решение, которое достаточно безопасно для типов, но оно также некрасиво (использует определения типов и определения). - person Damon; 30.03.2011

Возможно, вы сможете использовать некоторые макросы.

enum MyEnum {
    firstvalue = 0
    secondvalue,
    real_thirdvalue, // deprecated
    fourthvalue
};

template <MyEnum v>
struct real_value
{
    static MyEnum value()
    { 
        1 != 2U;  // Cause a warning in for example g++. Leave a comment behind for the user to translate this warning into "thirdvalue is deprecated"
        return v;
    }
};

#define thirdvalue (real_value<real_thirdvalue>::value());

Это не будет работать в контексте, когда необходима константа.

person Mark B    schedule 30.03.2011
comment
Я понятия не имею, работает ли это, но +1 за откровенный кровавый хакерство. ;-) - person Paul R; 30.03.2011
comment
Зачем тебе сходить с ума? - person lpapp; 29.11.2013

У меня есть решение (вдохновленное Mark B), в котором используется boost/serialization/static_warning.hpp. Однако мой позволяет использовать thirdvalue в качестве символической константы. Он также выдает предупреждения для каждого места, где кто-то пытается использовать thirdvalue.

#include <boost/serialization/static_warning.hpp>

enum MyEnum {
    firstvalue = 0,
    secondvalue,
    deprecated_thirdvalue, // deprecated
    fourthvalue
};

template <int line>
struct Deprecated
{
    BOOST_SERIALIZATION_BSW(false, line);
    enum {MyEnum_thirdvalue = deprecated_thirdvalue};
};

#define thirdvalue (static_cast<MyEnum>(Deprecated<__LINE__>::MyEnum_thirdvalue))

enum {symbolic_constant = thirdvalue};

int main()
{
    MyEnum e = thirdvalue;
}

В GCC я получаю предупреждения, которые в конечном итоге указывают на строки виновника, содержащие thirdvalue.

Обратите внимание, что использование шаблона Deprecated приводит к тому, что строка вывода компилятора, созданная здесь, показывает, где используется устаревшее перечисление.

Если вы сможете найти способ переносимого создания предупреждения внутри шаблона Deprecated, тогда вы сможете избавиться от зависимости от Boost.

person Emile Cormier    schedule 30.03.2011