From ebabd34385feb629b759216c1f7d85edc20bf2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Ankarstr=C3=B6m?= Date: Sat, 30 Jul 2022 03:14:13 +0200 Subject: Add wstring_owner, replacing std::wstring. std::basic_string is nice, but it is not very ergonomic if everything you really need is to automatically free C strings at end of scope. I suppose I could have used std::unique_ptr for this, but I suspect the ergonomics would be worse. --- c/common.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++--------- c/common.h | 17 +++++++++++++- c/datalistview.cpp | 10 ++++---- c/episodelistview.cpp | 20 ++++++++-------- c/main.cpp | 20 ++++++++-------- c/pl.cpp | 9 ++++---- c/pl.h | 16 ++++--------- 7 files changed, 103 insertions(+), 53 deletions(-) (limited to 'c') diff --git a/c/common.cpp b/c/common.cpp index bc2d4af..614bfeb 100644 --- a/c/common.cpp +++ b/c/common.cpp @@ -2,6 +2,59 @@ #include "common.h" +/* wstring_owner: Simple wrapper for wide C strings. */ + +wstring_owner::wstring_owner() {} + +wstring_owner::wstring_owner(wchar_t* const wsz) : p(wsz) {} + +wstring_owner& wstring_owner::operator=(wchar_t* const wsz) +{ + if (p != wsz) { + delete p; + p = wsz; + } + return *this; +} + +wstring_owner::wstring_owner(wstring_owner&& wso) noexcept : p(std::exchange(wso.p, nullptr)) {} + +wstring_owner& wstring_owner::operator=(wstring_owner&& wso) noexcept +{ + std::swap(p, wso.p); + return *this; +} + +/* Return pointer, releasing ownership. */ +wchar_t* wstring_owner::release() +{ + wchar_t* p2 = p; + p = nullptr; + return p2; +} + +wstring_owner::operator bool() const +{ + return p; +} + +wstring_owner::~wstring_owner() +{ + delete p; +} + +wstring_owner WsoFromSz(const char* const sz, const int iCp) +{ + int cbMultiByte = strlen(sz)+1; + int cchWideChar = MultiByteToWideChar(iCp, 0, sz, cbMultiByte, NULL, 0); + wchar_t* wsz = new wchar_t[cchWideChar]; + if (!MultiByteToWideChar(iCp, 0, sz, cbMultiByte, wsz, cchWideChar)) { + delete wsz; + throw Win32Error(GetLastError()); + } + return wsz; +} + /* Win32Error: Exception for Windows API errors. */ Win32Error::Win32Error() : dwErr(GetLastError()) {} @@ -55,17 +108,6 @@ Library::~Library() FreeLibrary(m_hModule); } -/* Convert narrow unmanaged string to wide managed string. */ -std::wstring WsFromSz(const char* sz, int iCp) -{ - int cbMultiByte = strlen(sz)+1; - int cchWideChar = MultiByteToWideChar(iCp, 0, sz, cbMultiByte, NULL, 0); - std::wstring ws(cchWideChar, 0); - if (!MultiByteToWideChar(iCp, 0, sz, cbMultiByte, ws.data(), cchWideChar)) - throw Win32Error(GetLastError()); - return ws; -} - /* Move message box to center of main window. */ static LRESULT CALLBACK CBTProc(const int nCode, const WPARAM wParam, const LPARAM lParam) noexcept { diff --git a/c/common.h b/c/common.h index 8b88635..7b7e996 100644 --- a/c/common.h +++ b/c/common.h @@ -11,7 +11,22 @@ #define WA "A" #endif -std::wstring WsFromSz(const char* sz, int iCp = CP_UTF8); +struct wstring_owner +{ + wstring_owner(); + wstring_owner(wchar_t* wsz); + wstring_owner& operator=(wchar_t* wsz); + wstring_owner(wstring_owner& wso) = delete; + wstring_owner& operator=(wstring_owner& wso) = delete; + wstring_owner(wstring_owner&& wso) noexcept; + wstring_owner& operator=(wstring_owner&& wso) noexcept; + wchar_t *release(); + operator bool() const; + ~wstring_owner(); + wchar_t* p = nullptr; +}; + +wstring_owner WsoFromSz(const char* sz, int iCp = CP_UTF8); int EBMessageBox(const wchar_t* wszText, const wchar_t* wszCaption, unsigned uType); struct Win32Error : public std::exception diff --git a/c/datalistview.cpp b/c/datalistview.cpp index ea68f2d..02a399f 100644 --- a/c/datalistview.cpp +++ b/c/datalistview.cpp @@ -54,20 +54,20 @@ void DataListView::ShowEpisode(const int iEpisode) LVITEM lviValue = {LVIF_TEXT}; for (int i = 0; q.NextSolution(std::nothrow); i++) { - std::wstring wsKey; - std::wstring wsValue; + wstring_owner wsoKey; + wstring_owner wsoValue; - if (!(PlGet(t+1, &wsKey) && PlGet(t+2, &wsValue))) + if (!(PlGet(t+1, &wsoKey) && PlGet(t+2, &wsoValue))) continue; lviKey.iItem = i; lviKey.iSubItem = 0; - lviKey.pszText = wsKey.data(); + lviKey.pszText = wsoKey.p; ListView_InsertItem(hWnd, &lviKey); lviValue.iItem = i; lviValue.iSubItem = 1; - lviValue.pszText = wsValue.data(); + lviValue.pszText = wsoValue.p; ListView_SetItem(hWnd, &lviValue); } diff --git a/c/episodelistview.cpp b/c/episodelistview.cpp index b3ee02f..132941c 100644 --- a/c/episodelistview.cpp +++ b/c/episodelistview.cpp @@ -272,16 +272,16 @@ int CALLBACK EpisodeListView::SortProc(const LPARAM iItem1, const LPARAM iItem2, case ELVSITITLE: { Mark m; - std::wstring ws1, ws2; + wstring_owner wso1, wso2; int cch, cch1, cch2; - if (!Pl("episode_data","episode_title",lvi1.lParam,&ws1)) + if (!Pl("episode_data","episode_title",lvi1.lParam,&wso1)) return 0; - if (!Pl("episode_data","episode_title",lvi2.lParam,&ws2)) + if (!Pl("episode_data","episode_title",lvi2.lParam,&wso2)) return 0; - cch1 = wcslen(ws1.c_str()); - cch2 = wcslen(ws2.c_str()); + cch1 = wcslen(wso1.p); + cch2 = wcslen(wso2.p); cch = cch1 > cch2? cch2: cch1; - return iOrder*_wcsnicmp(ws1.c_str(), ws2.c_str(), cch); + return iOrder*_wcsnicmp(wso1.p, wso2.p, cch); } default: return 0; @@ -412,12 +412,12 @@ void EpisodeListView::Update() /* Update episode name and rating. */ void EpisodeListView::UpdateItem(const LVITEM* const pLvi) { - std::wstring wsName; - if (!Pl("episode_data","episode_title",pLvi->lParam,&wsName)) { + wstring_owner wsoName; + if (!Pl("episode_data","episode_title",pLvi->lParam,&wsoName)) { if (!Pl("episode_data","update_episode_data")) goto r; - if (!Pl("episode_data","episode_title",pLvi->lParam,&wsName)) goto r; + if (!Pl("episode_data","episode_title",pLvi->lParam,&wsoName)) goto r; } - ListView_SetItemText(hWnd, pLvi->iItem, ELVSITITLE, wsName.data()); + ListView_SetItemText(hWnd, pLvi->iItem, ELVSITITLE, wsoName.p); r: int iRating; if (!Pl("episode_data","episode_rating",pLvi->lParam,&iRating)) { diff --git a/c/main.cpp b/c/main.cpp index c9be01d..2edc6ec 100644 --- a/c/main.cpp +++ b/c/main.cpp @@ -48,7 +48,7 @@ void TerminateMsg(const wchar_t* wsz1, const wchar_t* wsz2) noexcept { wchar_t wsz[256] = {0}; if (wsz2) - wszf(wsz, L"Episode Browser was terminated due to %s: %s.", wsz1, wsz2); + wszf(wsz, L"Episode Browser was terminated due to %s: %s", wsz1, wsz2); else wszf(wsz, L"Episode Browser was terminated due to %s.", wsz1); MessageBox(g_hWnd, wsz, L"Fatal Error", MB_ICONERROR); @@ -59,15 +59,14 @@ void OnTerminate() noexcept try { std::rethrow_exception(std::current_exception()); } catch (const term_t& t) { - std::wstring ws; - if (PlString(t, &ws)) - TerminateMsg(L"a Prolog exception", ws.c_str()); + if (auto wso = PlString(t)) + TerminateMsg(L"a Prolog exception", wso.p); else TerminateMsg(L"a Prolog exception", NULL); } catch (const Win32Error& e) { TerminateMsg(L"a Windows error", e.WhatW()); } catch (const std::exception& e) { - TerminateMsg(L"an exception", WsFromSz(e.what()).c_str()); + TerminateMsg(L"an exception", WsoFromSz(e.what()).p); } catch (...) { TerminateMsg(L"an exception", NULL); } @@ -466,13 +465,13 @@ void WaitFor(const char* szMod, const char* szPred) static atom_t aThread; static int bActive; static int iTimer; - static std::wstring wsPred; + static wstring_owner wsoPred; if (bActive) { - wchar_t wsz[128] = {0}; + wchar_t wsz[256] = {0}; wszf(wsz, L"Another task (%s) is active. " L"Do you want to cancel the existing task and start a new one?", - wsPred.c_str()); + wsoPred.p); if (EBMessageBox(wsz, L"Error", MB_YESNO|MB_ICONWARNING) != IDYES) return; KillTimer(NULL, iTimer); @@ -483,7 +482,8 @@ void WaitFor(const char* szMod, const char* szPred) /* The timer procedure animates an ellipsis in the status bar * while the thread is running. */ - static auto proc = [](HWND, UINT, UINT_PTR, DWORD) -> void { + static auto proc = [](HWND, UINT, UINT_PTR, DWORD) -> void + { static int i = 0; if (Pl("episode_data","thread_running",aThread)) { @@ -501,7 +501,7 @@ void WaitFor(const char* szMod, const char* szPred) Plx(szMod,"thread_create",szPred,&aThread); SendMessage(g_hWndStatus, SB_SETTEXT, MAKEWPARAM(1,0), (LPARAM)L"."); if (prefer(iTimer = SetTimer(NULL, -1, 500, proc))) { - wsPred = WsFromSz(szPred); + wsoPred = WsoFromSz(szPred); bActive = 1; } } diff --git a/c/pl.cpp b/c/pl.cpp index 665fe3a..1255609 100644 --- a/c/pl.cpp +++ b/c/pl.cpp @@ -90,10 +90,11 @@ int Query::NextSolution() } /* Convert Prolog term to wide characters. */ -int PlString(const term_t t, std::wstring* const pWs, const int iFlags) +wstring_owner PlString(const term_t t, const int iFlags) { char* sz; - int r = PL_get_chars(t, &sz, iFlags); - if (r) *pWs = WsFromSz(sz); - return r; + if (PL_get_chars(t, &sz, iFlags)) + return {WsoFromSz(sz)}; + else + return {}; } diff --git a/c/pl.h b/c/pl.h index c0af4bc..c7fcfc8 100644 --- a/c/pl.h +++ b/c/pl.h @@ -7,7 +7,7 @@ #include "common.h" -int PlString(term_t t, std::wstring* pWs, int iFlags = CVT_WRITE); +wstring_owner PlString(const term_t t, const int iFlags = CVT_WRITE); struct Frame { @@ -54,8 +54,7 @@ inline int PlPut(term_t, long*) { return -1; } inline int PlPut(term_t, long long*) { return -1; } inline int PlPut(term_t, atom_t*) { return -1; } inline int PlPut(term_t, char**) { return -1; } -inline int PlPut(term_t, std::string*) { return -1; } -inline int PlPut(term_t, std::wstring*) { return -1; } +inline int PlPut(term_t, wstring_owner*) { return -1; } inline int PlGet(term_t, int) { return -1; } inline int PlGet(term_t, long) { return -1; } @@ -68,18 +67,11 @@ inline int PlGet(term_t t, long* x) { return PL_get_long(t, x); } inline int PlGet(term_t t, long long* x) { return PL_get_int64(t, x); } inline int PlGet(term_t t, atom_t* x) { return PL_get_atom(t, x); } inline int PlGet(term_t t, char** x) { return PL_get_atom_chars(t, x); } -inline int PlGet(term_t t, std::string* x) { +inline int PlGet(term_t t, wstring_owner* x) { Mark m; char* sz; if (!PlGet(t, &sz)) return 0; - *x = sz; - return 1; -} -inline int PlGet(term_t t, std::wstring* x) { - Mark m; - char* sz; - if (!PlGet(t, &sz)) return 0; - *x = WsFromSz(sz); + *x = WsoFromSz(sz); return 1; /* or catch potential exception from BstrFromSz? */ } -- cgit v1.2.3