winapi — битовые поля и перечисления Rust в стиле C ++

Я начинающий в Rust, который происходит от C / C ++. Для начала я попытался создать простую программу типа «Hello-World» для Microsoft Windows, используя user32.MessageBox где я наткнулся на проблему, связанную с битовыми полями. Отказ от ответственности: все фрагменты кода были написаны в редакторе SO и могут содержать ошибки.

Консолидированные объявления C, необходимые для вызова версии функции UTF-16LE:

enum MessageBoxResult {
IDFAILED,
IDOK,
IDCANCEL,
IDABORT,
IDRETRY,
IDIGNORE,
IDYES,
IDNO,
IDTRYAGAIN = 10,
IDCONTINUE
};

enum MessageBoxType {
// Normal enumeration values.
MB_OK,
MB_OKCANCEL,
MB_ABORTRETRYIGNORE,
MB_YESNOCANCEL,
MB_YESNO,
MB_RETRYCANCEL,
MB_CANCELTRYCONTINUE,

MB_ICONERROR            = 0x10UL,
MB_ICONQUESTION         = 0x20UL,
MB_ICONEXCLAMATION      = 0x30UL,
MB_ICONINFORMATION      = 0x40UL,

MB_DEFBUTTON1           = 0x000UL,
MB_DEFBUTTON2           = 0x100UL,
MB_DEFBUTTON3           = 0x200UL,
MB_DEFBUTTON4           = 0x300UL,

MB_APPLMODAL            = 0x0000UL,
MB_SYSTEMMODAL          = 0x1000UL,
MB_TASKMODAL            = 0x2000UL,

// Flag values.
MB_HELP                 = 1UL << 14,

MB_SETFOREGROUND        = 1UL << 16,
MB_DEFAULT_DESKTOP_ONLY = 1UL << 17,
MB_TOPMOST              = 1UL << 18,
MB_RIGHT                = 1UL << 19,
MB_RTLREADING           = 1UL << 20,
MB_SERVICE_NOTIFICATION = 1UL << 21
};

MessageBoxResult __stdcall MessageBoxW(
HWND            hWnd,
const wchar_t * lpText,
const wchar_t * lpCaption,
MessageBoxType  uType
);

Использование:

MessageBoxType mbType = MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON3 | MB_TOPMOST;
if ((mbType & 0x07 /* All bits for buttons */ == MB_YESNO) && (mbType & 0x70 /* All bits for icons */ == MB_ICONEXCLAMATION) && (mbType & 0x300 /* All bits for default buttons */ == MB_DEFBUTTON3) && (mbType & MB_TOPMOST != 0)) {
MessageBoxW(NULL, L"Text", L"Title", mbType);
}

MessageBoxType перечисление содержит значения перечисления и значения флага. Проблема в том, что MB_DEFBUTTON2 а также MB_DEFBUTTON3 могут быть использованы вместе и «неожиданно» привести к MB_DEFBUTTON4, Также доступ довольно склонен к ошибкам и уродлив, я должен |, & и сдвигать все вручную при проверке флагов в значении.

В C ++ одно и то же перечисление может быть разумно помещено в структуру, которая имеет тот же размер, что и перечисление, и делает способ доступа более простым, безопасным и красивым. Это использует битовые поля — расположение битовых полей, не определенных стандартом C, но поскольку я хочу использовать его только для x86-Windows, он всегда один и тот же, поэтому я могу положиться на него.

enum class MessageBoxResult : std::uint32_t {
Failed,
Ok,
Cancel,
Abort,
Retry,
Ignore,
Yes,
No,
TryAgain = 10,
Continue
};

enum class MessageBoxButton : std::uint32_t {
Ok,
OkCancel,
AbortRetryIgnore,
YesNoCancel,
YesNo,
RetryCancel,
CancelTryContinue
};

enum class MessageBoxDefaultButton : std::uint32_t {
One,
Two,
Three,
Four
};

// Union so one can access all flags as a value and all boolean values separately.
union MessageBoxFlags {
enum class Flags : std::uint32_t {
None,
Help                = 1UL << 0,
SetForeground       = 1UL << 2,
DefaultDesktopOnly  = 1UL << 3,
TopMost             = 1UL << 4,
Right               = 1UL << 5,
RtlReading          = 1UL << 6,
ServiceNotification = 1UL << 7
};

// Flags::operator|, Flags::operator&, etc. omitted here.

Flags flags;
struct {
bool help                   : 1;
char _padding0              : 1;
bool setForeground          : 1;
bool defaultDesktopOnly     : 1;
bool topMost                : 1;
bool right                  : 1;
bool rtlReading             : 1;
bool serviceNotification    : 1;
char _padding1              : 8;
char _padding2              : 8;
char _padding3              : 8;
};

constexpr MessageBoxFlags(const Flags flags = Flags::None)
: flags(flags) {}
};

enum class MessageBoxIcon : std::uint32_t {
None,
Stop,
Question,
Exclamation,
Information
};

enum class MessageBoxModality : std::uint32_t {
Application,
System,
Task
};

union MessageBoxType {
std::uint32_t value;
struct {                                          // Used bits                                   Minimum (Base 2)                          Maximum (Base 2)                          Min (Base 16) Max (Base 16)
MessageBoxButton button                 :  3; // 0000.0000.0000.0000|0000.0000.0000.0XXX     0000.0000.0000.0000|0000.0000.0000.0000 - 0000.0000.0000.0000|0000.0000.0000.0110 : 0x0000.0000 - 0x0000.0006
std::uint32_t _reserved0                :  1; // 0000.0000.0000.0000|0000.0000.0000.X000
MessageBoxIcon icon                     :  3; // 0000.0000.0000.0000|0000.0000.0XXX.0000     0000.0000.0000.0000|0000.0000.0001.0000 - 0000.0000.0000.0000|0000.0000.0100.0000 : 0x0000.0010 - 0x0000.0040
std::uint32_t _reserved1                :  1; // 0000.0000.0000.0000|0000.0000.X000.0000
MessageBoxDefaultButton defaultButton   :  2; // 0000.0000.0000.0000|0000.00XX.0000.0000     0000.0000.0000.0000|0000.0001.0000.0000 - 0000.0000.0000.0000|0000.0011.0000.0000 : 0x0000.0100 - 0x0000.0300
std::uint32_t _reserved2                :  2; // 0000.0000.0000.0000|0000.XX00.0000.0000
MessageBoxModality modality             :  2; // 0000.0000.0000.0000|00XX.0000.0000.0000     0000.0000.0000.0000|0001.0000.0000.0000 - 0000.0000.0000.0000|0010.0000.0000.0000 : 0x0000.1000 - 0x0000.2000
MessageBoxFlags::Flags flags            :  8; // 0000.0000.00XX.XXXX|XX00.0000.0000.0000     0000.0000.0000.0000|0100.0000.0000.0000 - 0000.0000.0010.0000|0000.0000.0000.0000 : 0x0000.4000 - 0x0020.0000
std::uint32_t _padding0                 : 10; // XXXX.XXXX.XX00.0000|0000.0000.0000.0000
};

MessageBoxType(const MessageBoxButton button, const MessageBoxIcon icon = MessageBoxIcon::None, const MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton::One, const MessageBoxModality modality = MessageBoxModality::Application, const MessageBoxFlags::Flags flags = MessageBoxFlags::Flags::None)
: button(button), _reserved0(0), icon(icon), _reserved1(0), defaultButton(defaultButton), _reserved2(0), modality(modality), flags(flags), _padding0(0) {}
MessageBoxType() : value(0) {}
};

MessageBoxResult __stdcall MessageBoxW(
HWND            parentWindow,
const wchar_t * text,
const wchar_t * caption,
MessageBoxType  type
);

Использование:

auto mbType = MessageBoxType(MessageBoxButton::YesNo, MessageBoxIcon::Exclamation, MessageBoxDefaultButton::Three, MessageBoxModality::Application, MessageBoxFlags::Flags::TopMost);
if (mbType.button == MessageBoxButton::YesNo && mbType.icon == MessageBoxIcon::Exclamation && mbType.defaultButton == MessageBoxDefaultButton::Three && mbType.flags.topMost) {
MessageBoxW(nullptr, L"Text", L"Title", mbType);
}

С этой версией C ++ я могу получить доступ к флагам как к логическим значениям и иметь классы перечисления для других типов, хотя это все еще просто std::uint32_t в памяти. Теперь я изо всех сил пытался реализовать это в Rust.

#[repr(u32)]
enum MessageBoxResult {
Failed,
Ok,
Cancel,
Abort,
Retry,
Ignore,
Yes,
No,
TryAgain = 10,
Continue
}

#[repr(u32)]
enum MessageBoxButton {
Ok,
OkCancel,
AbortRetryIgnore,
YesNoCancel,
YesNo,
RetryCancel,
CancelTryContinue
}

#[repr(u32)]
enum MessageBoxDefaultButton {
One,
Two,
Three,
Four
}

#[repr(u32)]
enum MessageBoxIcon {
None,
Stop,
Question,
Exclamation,
Information
}

#[repr(u32)]
enum MessageBoxModality {
Application,
System,
Task
}

// MessageBoxFlags and MessageBoxType ?

Я знаю о Ящик WinApi который, на мой взгляд, генерируется автоматически из VC ++ — заголовочных файлов, которые не помогают, потому что у меня будут те же проблемы, что и в C. Я также видел макрос битовых флагов но мне кажется, он не справляется с такой «сложностью».

Как бы я реализовал MessageBoxFlags а также MessageBoxType в Rust, поэтому я могу получить к нему доступ хорошим (не обязательно таким же) способом, как в моей реализации C ++?

5

Решение

битовое Ящик @Boiethios упомянул то, что я хотел. Я создал свой первый макрос ящик битовое который создает другой тип доступа для перечислений и логических значений.

Теперь я могу написать следующий код для использования перечислений и логических флагов, как я хочу:

bitfield!(pub Style(u32) {
pub button:                 Button(0, 3),
pub icon:                   Icon(4, 3),
pub default_button:         DefaultButton(8, 2),
pub modality:               Modality(12, 2),
pub help:                   14,
pub foreground:             16,
pub default_desktop_only:   17,
pub top_most:               18,
pub right:                  19,
pub right_to_left_reading:  20,
pub service_notification:   21,
});

Это создает struct Style(u32), аксессоры для флагов и инициализатор struct StyleInit которая имеет реализацию по умолчанию для всех флагов.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]