Существует ли прагматичное решение для удаления переключателей перечисления?

Название этого вопроса звучит немного странно, но я не мог придумать лучшего способа его сформулировать. Моя проблема в следующем; У меня есть тип внутри проекта под названием AmbiguousType, который представляет собой объединение в этом формате.

Declarations.h (заголовочный файл утилиты)

typedef union AmbiguousType
{
    u32 unsigned32;
    u64 unsigned64;
    i32 signed32;
    i64 signed64;
} AmbiguousType;

Теперь у меня есть много вспомогательных функций, которые облегчают использование этого типа. Я прокручивал эти функции, пытаясь найти способы сделать их более эффективными/менее громоздкими, и кое-что понял; большинство функций имеют оператор switch в качестве основного тела. Возьмем, к примеру, эту функцию, которая используется для присвоения заданному неоднозначному типу.

Declarations.c (исходный файл утилиты)

/**
 * @brief Assign a value to an ambiguous type.
 * @param affected The affected variable.
 * @param member What state are we making the ambiguous type?
 * @param value The value that state will have.
 */
void AssignAmbiguousType(AmbiguousType* affected, AmbiguousTypeSpecifier member,
                         void* value)
{
    switch (member)
    {
        // Note that "VPTT" is a helper macro to turn a void pointer into the given type.
        // U__ - unsigned __ bit integer.
        // I__ - signed __ bit integer.
        case unsigned32: affected->unsigned32 = VPTT(u32, value); return;
        case unsigned64: affected->unsigned64 = VPTT(u64, value); return;
        case signed32:   affected->signed32 = VPTT(i32, value); return;
        case signed64:   affected->signed64 = VPTT(i64, value); return;
    }
}

Мой вопрос: существует ли прагматичный способ удалить этот оператор switch из функции, сохранив при этом его использование? Мне это кажется излишним, но я не могу понять, что могло бы решить эту проблему.

Я пробовал использовать макросы для простого объединения токенов (##), но это явно не сработало, поскольку параметры известны только во время выполнения. Однако здесь я в растерянности. Есть ли вообще решение этой проблемы, или это лучшее, что можно получить?

Предполагая, что ваша ISA имеет прямой порядок байтов, и если вы не возражаете против некоторых носовых демонов, то вы можете просто полностью отказаться от типа union: просто передать одно значение u64, потому что оно может быть источником операций чтения с меньшим типом. значения и цель записи в меньшие целочисленные типы.

Dai 16.06.2024 03:16

@Zenais, сделайте AmbiguousType членом AmbiguousStruct, у которого есть второй член, который является указателем на набор функций, которые будут использоваться с этим экземпляром AmbiguousStruct.

chux - Reinstate Monica 16.06.2024 03:18

@chux-ReinstateMonica Виртуальная таблица на C? ... в некоторых частях мира это ересь (хотя более серьезно; как насчет присвоения старшего бита u64 в качестве тега типа, при условии, что ОП согласен с его сокращением до 56-битного целого числа?)

Dai 16.06.2024 03:19

@Dai, vtable правда, но звучит весело, когда крутишь лампочку на C++. (или свет C++ — это оксюморон?)

chux - Reinstate Monica 16.06.2024 03:20

@Dai, Note OP нужно только 2 бита, поэтому uint62_t, но этот подход (2 бита или байт) все еще не избавляет от switch().

chux - Reinstate Monica 16.06.2024 03:22

Смотрите мой ответ: Написание «общего» метода struct-print на C Он показывает подход switch и то, как преобразовать его в vtable. Это может дать вам некоторые идеи.

Craig Estey 16.06.2024 05:51

Ваше описание проблемы не указывает на необходимость иметь тип AmbiguousType. Просто используйте тип, которого будет достаточно для представления всех ваших случаев. И тогда не будет никаких операторов переключения/регистра.

freestyle 17.06.2024 18:24
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
103
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете уменьшить повторяемость, используя макросы, подобные X-макросам. Вот пример.

// define once, include everywhere
#define GEN_SWITCH(DISCRIMINANT, BODY) \
    switch(DISCRIMINANT) { \
        case DISC(u,32) : BODY(u,32) break; \
        case DISC(i,32) : BODY(i,32) break; \
        case DISC(u,64) : BODY(u,64) break; \
        case DISC(i,64) : BODY(i,64) break; \
        default: cannot_happen(); }

#define CONCAT(a,b) a ## b
#define CONCAT2(a,b) CONCAT(a,b)

#define TYPENAME_i(sz) CONCAT2(signed,sz)
#define TYPENAME_u(sz) CONCAT2(unsigned,sz)
#define VPNAME(ui,sz) CONCAT(ui,sz)
#define FIELD(ui,sz) CONCAT2(TYPENAME_,ui)(sz)
#define DISC(ui,sz) CONCAT2(TYPENAME_,ui)(sz)
// more macros that map signedness+size to something

// When you need to generate a switch
#define BODY(ui,sz) affected->FIELD(ui,sz) = VPTT(VPNAME(ui,sz), value);
GEN_SWITCH(member, BODY)

Последняя строка расширяется до следующего (отформатировано для удобства чтения):

switch(member) { 
  case unsigned32 : affected->unsigned32 = VPTT(u32, value); break; 
  case signed32 : affected->signed32 = VPTT(i32, value); break; 
  case unsigned64 : affected->unsigned64 = VPTT(u64, value); break; 
  case signed64 : affected->signed64 = VPTT(i64, value); break; 
  default: cannot_happen(); 
}

Таким образом, каждая вспомогательная функция будет выглядеть так:

#define BODY ... something ...
GEN_SWITCH(discriminant, BODY)

Прошу прощения за поздний ответ, был в отпуске. В любом случае, огромное вам за это спасибо! Я не знаю, как я не понял, что нужно сделать это раньше, это удовлетворяет всем моим критериям (то есть красивее) и при этом не требует каких-либо затрат на производительность. Спасибо.

Zenais 21.06.2024 20:49

Другие вопросы по теме