我與WinAPI的的作業CreateDialogIndirect職能,這對一些要求DLGTEMPLATE,并DLGTEMPLATEEX通過第二個引數指向的結構。我的代碼運行良好,但是,我想擺脫#define宏。
我創建了一個簡化的示例來關注宏。這是一個帶有宏的作業程式,它可以被編譯,并輸出預期的內容:
#include <iostream>
int wmain()
{
#define TITLE L"Title"
struct {
wchar_t title[ sizeof( TITLE ) / sizeof( TITLE[ 0 ] ) ];
int font_size;
} s = {
TITLE,
12,
};
std::wcout
<< L"s.title = " << s.title << std::endl
<< L"s.font_size = " << s.font_size << std::endl;
return 0;
}
在 Visual Studio 2022 中,我在#define宏上看到三個點,我可以閱讀以下工具提示:
- 宏可以轉換為 constexpr
- 顯示潛在的修復
- 將宏轉換為 constexpr
我想看到它轉換成一些東西以避免宏,所以我點擊它,代碼變成了這樣:
#include <iostream>
int wmain()
{
constexpr auto TITLE = L"Title";
struct {
wchar_t title[ sizeof( TITLE ) / sizeof( TITLE[ 0 ] ) ];
int font_size;
} s = {
TITLE,
12,
};
std::wcout
<< L"s.title = " << s.title << std::endl
<< L"s.font_size = " << s.font_size << std::endl;
return 0;
}
如果我按 F7 進行編譯,則會收到以下錯誤訊息:
- example.cpp(10,9):錯誤 C2440:“正在初始化”:無法從“const wchar_t *const”轉換為“wchar_t”
我不想輸入L"Title"兩次,也不想手動計算字串的長度。那么,什么可以很好地替代宏,它能夠初始化struct結構中的陣列并確定陣列的大小?
uj5u.com熱心網友回復:
取決于你真正想要什么。一種方法是使用std::wstring_view而不是陣列,但這可能適合您的需要,也可能不適合。如果是這樣,這很容易:
struct {
std::wstring_view title = L"Title";
int font_size{12};
} s;
uj5u.com熱心網友回復:
DLGTEMPLATEEX 是一個特例。事實上,在 wndows 頭檔案中沒有具有這樣名稱的結構。如果你想使用一個結構,你必須創建你自己的,自定義陣列大小。 4
例子
constexpr TCHAR TITLE[] = _T("Title"); // IMPORTANT! To be 100% sure that's a array,
// declare it explicitely.
struct MyDlgTemplateEx {
WORD dlgVer;
WORD signature;
DWORD helpID;
DWORD exStyle;
DWORD style;
WORD cDlgItems;
short x;
short y;
short cx;
short cy;
TCHAR menu[1];
TCHAR WwindowClass[1];
TCHAR title[sizeof(TITLE) / sizeof(TITLE[0]);
WORD pointsize;
WORD weight;
BYTE italic;
BYTE charset;
TCHAR typeface[1]; // a NULL string.
};
首先顯而易見的是,有相當多的成員變數。進行未命名的就地初始化(如您的示例),沒有明確的成員名稱很容易導致錯誤和錯誤。
初始化此類大型結構的最常見方法是明確地:
// ...
MyDlgTemplateEx tmpl = {}; // this declatres and fills structure with zeroes.
// initialize all non-zero members explicitely.
// cutting and pasting the structure declaration and editing around that is fairly fast.
tmpl.dlgVer = 1;
tmpl.signature = 0xFFFF; // this is a DLGTEMPLATEEX struct.
tmpl.exStyle = WS_EX_DLGMODALFRAME;
tmpl.style = WS_CAPTION;
tmpl.cDlgItems = 1;
tmpl.x = 10;
tmpl.y = 10;
tmpl.cx = 100;
tmpl.cy = 100;
_tcscpy_s(tmpl.title, TITLE); // must copy into array that's in structure.
uj5u.com熱心網友回復:
模板是宏使用的一種相當常見的替代品。由于您不想手動計算字串的長度(我不怪您),讓我們將長度(陣列長度,而不是字串長度)作為模板引數。您的匿名struct成為模板化物體的可能候選者,因為每次更改標題的長度時最終都會得到不同的型別。
實際轉換為模板很簡單(考慮到問題的級別),我不會在這里詳細介紹。當您撰寫建構式時,就會出現漂亮的部分。通過在引數串列中使用模板引數,編譯器將能夠推斷出它。這為您提供了您似乎正在尋找的易用性,盡管需要一些額外的設定。
警告:通過建構式的引數推匯出模板引數是 C 17 的特性。任何堅持使用 C 11 或 14 的人(希望你們當中沒有多少人)都可以通過撰寫一個單獨的函式模板來構造和回傳一個結構體,從而得到類似的結果。我將把它留作練習。
#include <iostream>
#include <cstring> // For memcpy
template <size_t N>
struct DlgTemplate {
wchar_t title[N];
int font_size;
// Constructor that will deduce `N`.
// For those not familiar with this syntax: the incoming title_ is a
// reference to an array with exactly N elements.
DlgTemplate(const wchar_t (&title_)[N], int font_size_) :
font_size(font_size_)
{
// Copy the title.
std::memcpy(title, title_, sizeof(title));
}
// Reminder: N is the array length, which includes the null terminator.
// The string length is N-1.
};
int main()
{
// As of C 17, the template argument can be deduced here.
auto s = DlgTemplate(L"Title", 12);
std::wcout
<< L"s.title = " << s.title << std::endl
<< L"s.font_size = " << s.font_size << std::endl;
}
接下來的考慮可能是如何處理所有被仁慈地排除在外的欄位。一種選擇是向建構式添加更多引數以處理所有欄位。但是,為了可讀性,我可能傾向于洗掉font_size_作為建構式引數(記得標記建構式explicit)然后在構造后設定每個欄位。以下是我對初始化的想法。
auto s = DlgTemplate(L"Title");
s.font_size = 12;
//s.other_field = value;
// etc.
這個問題有一個重要的細節需要注意。Visual Studio 實施的“潛在修復”導致長度資訊丟失。
constexpr auto TITLE = L"Title";
定義TITLE為一個指標(到const wchar_t)。它的大小是固定的,與標題的長度無關。計算sizeof( TITLE ) / sizeof( TITLE[ 0 ] )不會給出標題的長度,這TITLE不適用于模板。
constexpr wchar_t TITLE[] = L"Title";
定義TITLE為一個陣列,長度資訊保留為型別的一部分。這TITLE可以與模板一起使用。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/364597.html
標籤:C
上一篇:OpenGL:批處理渲染器:轉換應該在CPU還是GPU上進行?
下一篇:C -繼承中的多載與覆寫
