#jostik #compiler
Обзор жостиков C++ 😔 (номер 0b11)
На этот раз будет аж семь жостиков - три про DIY в Clang, одно про устройство Wireshark, одно с видео, и пара про идиомы C++ 📈 7️⃣ В данный момент это самый плотный набор жостиков.
1️⃣ [Clang] Сделай сам наследование в enum 🍷
У enum'ов в C++ нет наследования. Но это могло бы быть полезным в редких случаях, когда есть общий enum для всех кейсов, но в каких-то окружениях есть мелкие различия, которыми не хочется засорять общее место.
Пусть программа работает с большими ресторанами, куда отправляем запросы на доставку. С нашей стороны можно запросить новую доставку или отмену доставки (статус становится Adding/Cancelling и ждется ответ от сервера), после ответов от сервера статус становится Added/Cancelled/Delivered.
Большинство ресторанов не имеет возможности изменить адрес доставки (только отменять и создавать новую), но пусть какое-нибудь "Кафе Пушкинъ" 😬 так умеет, тогда неплохо бы иметь enum-наследник:
enum class DeliveryStatus : uint8_t {
Adding,
Added,
Cancelling,
Cancelled,
Delivered,
};
enum class CafePushkinDeliveryStatus : DeliveryStatus {
Moving,
};
(Планируется, что при изменении заказа статус меняется Added -> Moving -> Added)
Я попробовал придумать дизайн для наследования 🤔 Придумал такие правила:
1️⃣ У enum-наследника нумерация первой константы как будто она следует за последней константой родителя:
enum class First : short {
None, /* = 0 */
MovedPermanently = 301,
Found, /* = 302 */
SeeOther, /* = 303 */
};
enum class Second : First {
NotModified, /* = 304 */
UseProxy, /* = 305 */
BadRequest = 400,
Unauthorized, /* = 401 */
};
enum class Third : Second {
PaymentRequired, /* = 402 */
Forbidden, /* = 403 */
InternalServerError = 500,
};
2️⃣ enum-наследник наследует родительский underlying type транзитивно
static_assert(sizeof(First) == 2);
static_assert(sizeof(Second) == 2);
static_assert(sizeof(Third) == 2);
static_assert(std::is_same_v);
static_assert(std::is_same_v);
static_assert(std::is_same_v);
3️⃣ enum-родитель может неявно кастоваться к своему enum-наследнику:
inline constexpr Third kFound = First::Found;
4️⃣ Скоуп enum-наследника включает в себя все символы enum-родителей - этот пример аналогичен прошлому:
inline constexpr Third kFound = Third::Found;
То есть lookup по Third::Found находит enum constant First::Found (имеет тип First), потом на нем делается implicit cast к типу Third 🪄
Сделал proof of concept в Clang - ссылка на дифф 😐
(Это базовый вариант - нет тестов, не супер красивый код, не рассмотрена возможность множественного наследования, и так далее...)
Код компилятора жесткий, есть много хаков чтобы например различать a ? new enum E : int{} от foo(a, enum E : int{}).
Также под флагами поддерживается нестандартный синтаксис, например можно писать enum E : int *p; что равносильно enum E : int; E *p; (прикол от microsoft)
ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ 🏃
Обзор жостиков C++ 😔 (номер 0b11)
На этот раз будет аж семь жостиков - три про DIY в Clang, одно про устройство Wireshark, одно с видео, и пара про идиомы C++ 📈 7️⃣ В данный момент это самый плотный набор жостиков.
1️⃣ [Clang] Сделай сам наследование в enum 🍷
У enum'ов в C++ нет наследования. Но это могло бы быть полезным в редких случаях, когда есть общий enum для всех кейсов, но в каких-то окружениях есть мелкие различия, которыми не хочется засорять общее место.
Пусть программа работает с большими ресторанами, куда отправляем запросы на доставку. С нашей стороны можно запросить новую доставку или отмену доставки (статус становится Adding/Cancelling и ждется ответ от сервера), после ответов от сервера статус становится Added/Cancelled/Delivered.
Большинство ресторанов не имеет возможности изменить адрес доставки (только отменять и создавать новую), но пусть какое-нибудь "Кафе Пушкинъ" 😬 так умеет, тогда неплохо бы иметь enum-наследник:
enum class DeliveryStatus : uint8_t {
Adding,
Added,
Cancelling,
Cancelled,
Delivered,
};
enum class CafePushkinDeliveryStatus : DeliveryStatus {
Moving,
};
(Планируется, что при изменении заказа статус меняется Added -> Moving -> Added)
Я попробовал придумать дизайн для наследования 🤔 Придумал такие правила:
1️⃣ У enum-наследника нумерация первой константы как будто она следует за последней константой родителя:
enum class First : short {
None, /* = 0 */
MovedPermanently = 301,
Found, /* = 302 */
SeeOther, /* = 303 */
};
enum class Second : First {
NotModified, /* = 304 */
UseProxy, /* = 305 */
BadRequest = 400,
Unauthorized, /* = 401 */
};
enum class Third : Second {
PaymentRequired, /* = 402 */
Forbidden, /* = 403 */
InternalServerError = 500,
};
2️⃣ enum-наследник наследует родительский underlying type транзитивно
static_assert(sizeof(First) == 2);
static_assert(sizeof(Second) == 2);
static_assert(sizeof(Third) == 2);
static_assert(std::is_same_v);
static_assert(std::is_same_v);
static_assert(std::is_same_v);
3️⃣ enum-родитель может неявно кастоваться к своему enum-наследнику:
inline constexpr Third kFound = First::Found;
4️⃣ Скоуп enum-наследника включает в себя все символы enum-родителей - этот пример аналогичен прошлому:
inline constexpr Third kFound = Third::Found;
То есть lookup по Third::Found находит enum constant First::Found (имеет тип First), потом на нем делается implicit cast к типу Third 🪄
Сделал proof of concept в Clang - ссылка на дифф 😐
(Это базовый вариант - нет тестов, не супер красивый код, не рассмотрена возможность множественного наследования, и так далее...)
Код компилятора жесткий, есть много хаков чтобы например различать a ? new enum E : int{} от foo(a, enum E : int{}).
Также под флагами поддерживается нестандартный синтаксис, например можно писать enum E : int *p; что равносильно enum E : int; E *p; (прикол от microsoft)
ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ 🏃