aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2022-07-30 03:14:13 +0200
committerJohn Ankarström <john@ankarstrom.se>2022-07-30 03:14:13 +0200
commitebabd34385feb629b759216c1f7d85edc20bf2fd (patch)
tree432e68a6a58bcaf4bc1dd3c5ae438157f09f4d4a
parentd485eb2bf7e0a82702afec9387f21bf0f89c985f (diff)
downloadEpisodeBrowser-ebabd34385feb629b759216c1f7d85edc20bf2fd.tar.gz
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.
-rw-r--r--README15
-rw-r--r--c/common.cpp64
-rw-r--r--c/common.h17
-rw-r--r--c/datalistview.cpp10
-rw-r--r--c/episodelistview.cpp20
-rw-r--r--c/main.cpp20
-rw-r--r--c/pl.cpp9
-rw-r--r--c/pl.h16
8 files changed, 110 insertions, 61 deletions
diff --git a/README b/README
index d7c644f..b492c53 100644
--- a/README
+++ b/README
@@ -62,17 +62,16 @@ Following is a summary of some coding conventions used in the project.
- p = pointer
- b = bool, BOOL, int (boolean value)
- - i = integer
+ - i = int
- h = handle
- - hX = X handle (e.g., hWnd = HWND)
- - l = long, LPARAM
- - w = WORD, WPARAM
+ - l = long, (LPARAM)
+ - w = unsigned short, WORD, (WPARAM)
- dw = DWORD
- lvi = LVITEM
- - sz = unmanaged, zero-terminated narrow string (char*)
- - wsz = unmanaged, zero-terminated wide string (wchar_t*)
- - s = managed narrow string (std::string)
- - ws = managed wide string (std::wstring)
+ - sz = char*
+ - wsz = wchar_t*
+ - ws = std::wstring
+ - wso = wstring_owner
The list above is non-exhaustive. Variables whose type is unknown (in
templates) do not need prefixes. Some very common self-explanatory
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? */
}