Rust位域和枚举C ++样式

时间:2018-04-10 15:22:25

标签: c++ winapi rust bit-fields bitflags

我是来自C / C ++的Rust初学者。首先,我尝试创建一个简单的" Hello-World"像使用user32.MessageBox的Microsoft Windows程序,我偶然发现了与位域相关的问题。 免责声明:所有代码段都是在SO编辑器中编写的,可能包含错误。

MessageBox" Hello-World"在C

调用该函数的UTF-16LE版本所需的合并C声明是:

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_DEFBUTTON2MB_DEFBUTTON3可以一起使用,并且&#34;意外地&#34;结果为MB_DEFBUTTON4。此外,访问非常容易出错且很丑陋,我必须|&并在检查值中的标志时手动移动所有内容。

MessageBox&#34; Hello-World&#34;在C ++中

在C ++中,可以巧妙地将相同的枚举放入结构中,该结构与枚举具有相同的大小,使访问方式更容易,更安全,更漂亮。它使用了位域 - the layout of bitfields not defined by the C standard,但由于我只想将它用于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中实现它。

MessageBox&#34; Hello-World&#34;在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 crate我的理解是从VC ++自动生成的 - 头文件没有帮助,因为我会遇到与C相同的问题。我也看到了{{3}但在我看来它并没有处理这种&#34;复杂性&#34;。

我如何在Rust中实现MessageBoxFlagsMessageBoxType,所以我可以在我的C ++实现中以一种不错的(不一定相同的)方式访问它?

1 个答案:

答案 0 :(得分:0)

提到的bitfield箱子@Boiethios就是我想要的。我创建了自己的第一个宏箱bitfield,它为枚举和布尔值创建了一种不同的访问器。

现在我可以编写以下代码来使用枚举和布尔标志:

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