Switch to cmake + vcpkg, builds on linux, windows needs verification.

Code has been modified to be portable.
This commit is contained in:
eelke 2026-10-17 16:21:22 +02:00
parent c1a5ac1aad
commit 49f6619a4e
34 changed files with 678 additions and 327 deletions

View file

@ -1,13 +1,14 @@
#include "ASyncDBConnection.h"
#include "ASyncDBConnection.h"
#include "ScopeGuard.h"
#include "util.h"
#include "Pgsql_PgException.h"
#include "Win32Event.h"
#include "WaitHandleList.h"
#include <atomic>
#include <chrono>
#include <queue>
#include <WinSock2.h>
#include <QDebug>
#include <QEventLoop>
#include <QSocketNotifier>
#include <QTimer>
namespace {
@ -45,10 +46,6 @@ public:
struct t_Command {
std::mutex m_mutex;
t_CommandQueue m_queue;
Win32Event m_newEvent;
t_Command()
: m_newEvent(Win32Event::Reset::Auto, Win32Event::Initial::Clear)
{}
} m_commandQueue;
ConnectionConfig m_config;
@ -64,18 +61,29 @@ public:
void stop();
/// Wakes the worker thread when it is waiting in its event loop
void wake();
private:
enum class WaitResult {
Socket, ///< the socket became ready
Wakeup, ///< woken up by stop() or by a new command
Timeout ///< the timeout expired
};
ASyncDBConnection *asyncConnObject = nullptr;
/// \todo Implement new method to stop the thread
Win32Event m_stopEvent;
std::atomic<bool> terminateRequested{false}; ///< is set when the thread should stop
std::mutex m_wakeMutex;
QEventLoop *m_loop = nullptr; ///< loop the worker blocks in, guarded by m_wakeMutex
Pgsql::Connection m_connection;
bool terminateRequested = false; ///< is set when the thread should stop
bool m_terminated = true;
Pgsql::Canceller m_canceller;
QElapsedTimer m_timer;
bool hasQueuedCommand();
WaitResult waitForSocket(QSocketNotifier::Type type, int timeout_ms, bool wakeOnNewCommand);
bool makeConnection();
void communicate();
@ -97,7 +105,6 @@ private:
ASyncDBConnectionThread::ASyncDBConnectionThread(ASyncDBConnection *asco)
: asyncConnObject(asco)
, m_stopEvent(Win32Event::Reset::Manual, Win32Event::Initial::Clear)
{}
void ASyncDBConnectionThread::run()
@ -154,6 +161,59 @@ bool ASyncDBConnectionThread::cancel()
return m_canceller.cancel(nullptr);
}
bool ASyncDBConnectionThread::hasQueuedCommand()
{
std::lock_guard<std::mutex> lg(m_commandQueue.m_mutex);
return !m_commandQueue.m_queue.empty();
}
ASyncDBConnectionThread::WaitResult ASyncDBConnectionThread::waitForSocket(QSocketNotifier::Type type, int timeout_ms, bool wakeOnNewCommand)
{
QEventLoop loop;
WaitResult result = WaitResult::Wakeup;
QSocketNotifier notifier(m_connection.socket(), type);
QObject::connect(&notifier, &QSocketNotifier::activated, &loop, [&result, &loop] {
result = WaitResult::Socket;
loop.quit();
});
QTimer timer;
timer.setSingleShot(true);
if (timeout_ms >= 0) {
QObject::connect(&timer, &QTimer::timeout, &loop, [&result, &loop] {
result = WaitResult::Timeout;
loop.quit();
});
timer.start(timeout_ms);
}
{
std::lock_guard<std::mutex> lg(m_wakeMutex);
m_loop = &loop;
}
// Checked after publishing the loop, so a wake-up that happened before is not lost
bool pending = terminateRequested || (wakeOnNewCommand && hasQueuedCommand());
if (!pending) {
loop.exec();
}
{
std::lock_guard<std::mutex> lg(m_wakeMutex);
m_loop = nullptr;
}
return result;
}
void ASyncDBConnectionThread::wake()
{
std::lock_guard<std::mutex> lg(m_wakeMutex);
if (m_loop) {
QMetaObject::invokeMethod(m_loop, [loop = m_loop] { loop->quit(); }, Qt::QueuedConnection);
}
}
bool ASyncDBConnectionThread::makeConnection()
{
using namespace std::literals::chrono_literals;
@ -165,30 +225,14 @@ bool ASyncDBConnectionThread::makeConnection()
bool ok = m_connection.connectStart(conn_string); //keywords, values);
auto start = std::chrono::steady_clock::now();
if (ok && m_connection.status() != CONNECTION_BAD) {
Win32Event socket_event(Win32Event::Reset::Auto, Win32Event::Initial::Clear);
long fd = FD_WRITE;
QSocketNotifier::Type type = QSocketNotifier::Write;
while (true) {
// poll till complete or failed (we can get an abort command)
int sock = m_connection.socket();
WSAEventSelect(sock, socket_event.handle(), fd);
WaitHandleList whl;
auto wait_result_socket_event = whl.add(socket_event);
auto wait_result_stop = whl.add(m_stopEvent);
auto elapsed = std::chrono::steady_clock::now() - start;
auto remaining = std::chrono::duration_cast<std::chrono::milliseconds>(30s - elapsed).count();
WaitResult res = waitForSocket(type, remaining > 0 ? static_cast<int>(remaining) : 0, false);
auto nu = std::chrono::steady_clock::now();
std::chrono::duration<float, std::milli> diff = -(nu-start);
diff += 30s;
DWORD timeout = diff.count();
DWORD res = MsgWaitForMultipleObjectsEx(
whl.count(), // _In_ DWORD nCount,
whl, // _In_ const HANDLE *pHandles,
timeout, // _In_ DWORD dwMilliseconds,
0, // _In_ DWORD dwWakeMask,
0 // _In_ DWORD dwFlags
);
if (res == wait_result_socket_event) {
if (res == WaitResult::Socket) {
auto poll_state = m_connection.connectPoll();
if (poll_state == PGRES_POLLING_OK) {
qDebug() << "ASyncDBConnection Connected";
@ -202,17 +246,21 @@ bool ASyncDBConnectionThread::makeConnection()
}
else if (poll_state == PGRES_POLLING_READING) {
doStateCallback({ ASyncDBConnection::State::Connecting, "Negotiating" });
fd = FD_READ;
type = QSocketNotifier::Read;
}
else if (poll_state == PGRES_POLLING_WRITING) {
doStateCallback({ ASyncDBConnection::State::Connecting, "Negotiating" });
fd = FD_WRITE;
type = QSocketNotifier::Write;
}
}
else if (res == wait_result_stop) {
else if (terminateRequested) {
m_connection.close();
return false;
}
else if (res == WaitResult::Timeout) {
doStateCallback({ ASyncDBConnection::State::NotConnected, "Connection timed out" });
m_connection.close();
return false;
}
} // end while (true)
}
@ -252,7 +300,7 @@ void ASyncDBConnectionThread::communicate()
void ASyncDBConnectionThread::stop()
{
terminateRequested = true;
m_stopEvent.set();
wake();
}
void ASyncDBConnectionThread::doStateCallback(ASyncDBConnection::StateData state)
@ -264,34 +312,18 @@ void ASyncDBConnectionThread::doStateCallback(ASyncDBConnection::StateData state
bool ASyncDBConnectionThread::waitForAndSendCommand()
{
SOCKET sock = static_cast<SOCKET>(m_connection.socket());
Win32Event socket_event(Win32Event::Reset::Manual, Win32Event::Initial::Clear);
long fd = FD_READ | FD_CLOSE;
WSAEventSelect(sock, socket_event.handle(), fd);
WaitHandleList whl;
auto wait_result_new_command = whl.add(m_commandQueue.m_newEvent);
auto wait_result_socket = whl.add(socket_event);
whl.add(m_stopEvent);
DWORD res = MsgWaitForMultipleObjectsEx(
whl.count(), // _In_ DWORD nCount,
whl, // _In_ const HANDLE *pHandles,
INFINITE, // _In_ DWORD dwMilliseconds,
0, // _In_ DWORD dwWakeMask,
0 // _In_ DWORD dwFlags
);
if (res == wait_result_new_command) {
if (hasQueuedCommand()) {
doNewCommand();
return true;
}
// Idle connection: a readable socket carries notifies or notices, or signals a lost connection
if (waitForSocket(QSocketNotifier::Read, -1, true) == WaitResult::Socket) {
if (!m_connection.consumeInput()) {
doStateCallback({ ASyncDBConnection::State::NotConnected, "Connection lost" });
return false;
}
}
else if (res == wait_result_socket) {
WSANETWORKEVENTS net_events;
WSAEnumNetworkEvents(sock, socket_event.handle(), &net_events);
if (net_events.lNetworkEvents & FD_CLOSE) {
doStateCallback({ ASyncDBConnection::State::NotConnected, "Connection lost" });
return false;
}
}
// Note if it was stop we can just return and function
// above will stop looping because terminateRequested has been set too by stop
return true;
@ -347,42 +379,16 @@ bool ASyncDBConnectionThread::consumeResultInput()
void ASyncDBConnectionThread::waitForResult()
{
SOCKET sock = static_cast<SOCKET>(m_connection.socket());
Win32Event socket_event(Win32Event::Reset::Manual, Win32Event::Initial::Clear);
long fd = FD_READ | FD_CLOSE;
bool finished = false;
while ( ! finished) {
WSAEventSelect(sock, socket_event.handle(), fd);
WaitResult res = waitForSocket(QSocketNotifier::Read, -1, false);
WaitHandleList whl;
auto wait_result_socket = whl.add(socket_event);
auto wait_result_stop = whl.add(m_stopEvent);
DWORD res = MsgWaitForMultipleObjectsEx(
whl.count(), // _In_ DWORD nCount,
whl, // _In_ const HANDLE *pHandles,
INFINITE, // _In_ DWORD dwMilliseconds,
0, // _In_ DWORD dwWakeMask,
0 // _In_ DWORD dwFlags
);
if (res == wait_result_socket) {
WSANETWORKEVENTS net_events;
WSAEnumNetworkEvents(sock, socket_event.handle(), &net_events);
if (net_events.lNetworkEvents & FD_READ) {
if (consumeResultInput()) {
finished = true;
}
if (res == WaitResult::Socket) {
if (consumeResultInput()) {
finished = true;
}
else if (net_events.lNetworkEvents & FD_CLOSE) {
doStateCallback({ ASyncDBConnection::State::NotConnected, "Close while waiting for result" });
finished = true;
stop();
}
}
if (res == wait_result_stop) {
else if (terminateRequested) {
// Send cancel, close connection and terminate thread
cancel();
doStateCallback(ASyncDBConnection::State::Terminating);
@ -455,8 +461,8 @@ bool ASyncDBConnection::send(const std::string &command, on_result_callback on_r
{
std::lock_guard<std::mutex> lg(m_threadData->m_commandQueue.m_mutex);
m_threadData->m_commandQueue.m_queue.emplace(command, on_result);
m_threadData->m_commandQueue.m_newEvent.set();
}
m_threadData->wake();
return true;
}
@ -468,8 +474,8 @@ bool ASyncDBConnection::send(const std::string &command, Pgsql::Params params, o
{
std::lock_guard<std::mutex> lg(m_threadData->m_commandQueue.m_mutex);
m_threadData->m_commandQueue.m_queue.emplace(command, std::move(params), on_result);
m_threadData->m_commandQueue.m_newEvent.set();
}
m_threadData->wake();
return true;
}

View file

@ -11,6 +11,7 @@
#include <QElapsedTimer>
#include <mutex>
#include <memory>
#include <thread>
class ASyncDBConnectionThread;
/** \brief Class that handles asynchronous execution of queries.
@ -33,7 +34,7 @@ public:
class StateData {
public:
State State;
enum State State;
QString Message;
StateData(enum State state, QString message = QString())

187
pglablib/CMakeLists.txt Normal file
View file

@ -0,0 +1,187 @@
add_library(pglablib STATIC
Pglablib.cpp
ASyncDBConnection.cpp
ConnectionConfig.cpp
TestConnection.cpp
catalog/PgType.cpp
catalog/PgTypeContainer.cpp
catalog/PgNamespace.cpp
catalog/PgClass.cpp
catalog/PgDatabase.cpp
catalog/PgDatabaseContainer.cpp
catalog/PgAuthId.cpp
catalog/PgAuthIdContainer.cpp
catalog/PgClassContainer.cpp
catalog/PgDatabaseCatalog.cpp
catalog/PgNamespaceContainer.cpp
catalog/PgAttribute.cpp
catalog/PgContainer.cpp
catalog/PgAttributeContainer.cpp
catalog/PgIndex.cpp
catalog/PgIndexContainer.cpp
catalog/PgConstraint.cpp
catalog/PgConstraintContainer.cpp
ParamListJson.cpp
ParamListModel.cpp
sqlite/SQLiteConnection.cpp
sqlite/sqlite3.c
ui/catalog/tables/TableNode.cpp
ui/catalog/tables/TableSize.cpp
ui/catalog/tables/TableTreeBuilder.cpp
util.cpp
SqlFormattingUtils.cpp
catalog/PgKeywordList.cpp
QueryGenerator.cpp
catalog/PgObject.cpp
catalog/PgTablespace.cpp
catalog/PgTablespaceContainer.cpp
codebuilder/LanguageConfig.cpp
codebuilder/CodeBuilder.cpp
codebuilder/NameManglingRules.cpp
codebuilder/DefaultConfigs.cpp
codebuilder/TypeMappings.cpp
codebuilder/IndentationConfig.cpp
codebuilder/StructureTemplate.cpp
FormatToStream.cpp
codebuilder/StringLiteralRules.cpp
codebuilder/StringEscapeRule.cpp
catalog/PgTrigger.cpp
catalog/PgTriggerContainer.cpp
catalog/PgProc.cpp
catalog/PgProcContainer.cpp
catalog/PgDatabaseObject.cpp
catalog/PgServerObject.cpp
catalog/PgNamespaceObject.cpp
catalog/PgCollation.cpp
catalog/PgCollationContainer.cpp
catalog/PgInherits.cpp
catalog/PgInheritsContainer.cpp
SelectionEditorFactory.cpp
catalog/PgAm.cpp
model/TypeModelFactory.cpp
model/TypeSelectionItemModel.cpp
catalog/PgAmContainer.cpp
model/CollationModel.cpp
model/CollationModelFactory.cpp
catalog/PgLanguageContainer.cpp
catalog/PgLanguage.cpp
catalog/PgAcl.cpp
catalog/PgSequence.cpp
catalog/PgSequenceContainer.cpp
utils/HumanReadableBytes.cpp
utils/KeyStrengthener.cpp
utils/PasswordManager.cpp
utils/PostgresqlUrlParser.cpp
Pglablib.h
ASyncDBConnection.h
ConnectionConfig.h
TestConnection.h
catalog/PgType.h
catalog/PgTypeContainer.h
catalog/PgNamespace.h
catalog/PgClass.h
catalog/PgDatabase.h
catalog/PgDatabaseContainer.h
catalog/PgContainer.h
catalog/PgAuthId.h
catalog/PgAuthIdContainer.h
catalog/PgClassContainer.h
catalog/PgDatabaseCatalog.h
catalog/PgNamespaceContainer.h
catalog/PgAttribute.h
catalog/PgAttributeContainer.h
catalog/PgIndex.h
catalog/PgIndexContainer.h
catalog/PgConstraint.h
catalog/PgConstraintContainer.h
ParamListJson.h
ParamListModel.h
sqlite/SQLiteConnection.h
sqlite/sqlite3.h
sqlite/sqlite3ext.h
ui/catalog/tables/TableNode.h
ui/catalog/tables/TableSize.h
ui/catalog/tables/TableTreeBuilder.h
util.h
SqlFormattingUtils.h
catalog/PgCatalogTypes.h
catalog/PgKeywordList.h
QueryGenerator.h
catalog/PgObject.h
catalog/PgTablespace.h
catalog/PgTablespaceContainer.h
codebuilder/LanguageConfig.h
codebuilder/CodeBuilder.h
codebuilder/NameManglingRules.h
codebuilder/DefaultConfigs.h
codebuilder/TypeMappings.h
codebuilder/IndentationConfig.h
codebuilder/StructureTemplate.h
FormatToStream.h
codebuilder/ResultLoopTemplate.h
codebuilder/StringEscapeRule.h
codebuilder/StringLiteralRules.h
catalog/PgTrigger.h
catalog/PgTriggerContainer.h
catalog/PgProc.h
catalog/PgProcContainer.h
catalog/PgDatabaseObject.h
catalog/PgServerObject.h
catalog/PgNamespaceObject.h
catalog/PgCollation.h
catalog/PgCollationContainer.h
AbstractModelFactory.h
AbstractEditorFactory.h
catalog/PgInherits.h
catalog/PgInheritsContainer.h
SelectionEditorFactory.h
model/TypeSelectionItemModel.h
model/TypeModelFactory.h
catalog/PgAm.h
catalog/PgAmContainer.h
model/CollationModel.h
model/CollationModelFactory.h
catalog/PgLanguageContainer.h
catalog/PgLanguage.h
catalog/PgAcl.h
catalog/PgSequence.h
catalog/PgSequenceContainer.h
utils/HumanReadableBytes.h
utils/KeyStrengthener.h
utils/PasswordManager.h
utils/PostgresqlUrlParser.h
)
find_package(Boost REQUIRED COMPONENTS container timer chrono assert)
find_path(BOTAN_INCLUDE_DIR botan/hash.h REQUIRED)
find_library(BOTAN_LIBRARY NAMES botan-3 botan REQUIRED)
if(NOT TARGET Botan::Botan)
add_library(Botan::Botan STATIC IMPORTED)
set_target_properties(Botan::Botan PROPERTIES
IMPORTED_LOCATION "${BOTAN_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${BOTAN_INCLUDE_DIR}"
)
endif()
target_link_libraries(pglablib PUBLIC
core
pgsql
Boost::container
Boost::timer
Boost::chrono
Boost::assert
Botan::Botan
Qt6::Core
Qt6::Widgets
)
target_include_directories(pglablib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
)
set_target_properties(pglablib PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
POSITION_INDEPENDENT_CODE ON
)

View file

@ -200,7 +200,7 @@ QString quoteIdent(QString ident)
{
assert(ident.length() > 0);
static const wchar_t dquote = L'"';
static const QChar dquote = u'"';
if (identNeedsQuotes(ident)) {
QString out;

View file

@ -1,31 +0,0 @@
#include "WaitHandleList.h"
#include "win32event.h"
WaitHandleList::WaitHandleList() = default;
WaitHandleList::~WaitHandleList() = default;
WaitResultValue WaitHandleList::add(HANDLE h)
{
m_waitHandles.push_back(h);
return WAIT_OBJECT_0 + static_cast<DWORD>(m_waitHandles.size() - 1);
}
WaitResultValue WaitHandleList::add(Win32Event &e)
{
return add(e.handle());
}
DWORD WaitHandleList::count() const
{
return static_cast<DWORD>(m_waitHandles.size());
}
void WaitHandleList::clear()
{
m_waitHandles.clear();
}
WaitHandleList::operator const HANDLE*() const
{
return m_waitHandles.data();
}

View file

@ -1,26 +0,0 @@
#ifndef WAITHANDLELIST_H
#define WAITHANDLELIST_H
#include <windows.h>
#include <vector>
class Win32Event;
using WaitResultValue = DWORD;
class WaitHandleList {
public:
WaitHandleList();
~WaitHandleList();
WaitResultValue add(HANDLE h);
WaitResultValue add(Win32Event &e);
DWORD count() const;
void clear();
operator const HANDLE*() const;
private:
std::vector<HANDLE> m_waitHandles;
};
#endif // WAITHANDLELIST_H

View file

@ -1,47 +0,0 @@
#ifndef WIN32EVENT_H
#define WIN32EVENT_H
#include <WinSock2.h>
#include <windows.h>
/** Simpel wrapper around a Win32 Event object.
Mostly to make cleanup automatic.*/
class Win32Event {
public:
enum class Reset { Auto=0, Manual=1 };
enum class Initial { Clear=0, Set=1 };
Win32Event(Reset r, Initial is)
: hEvent(CreateEvent(
nullptr, // _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL(r), // _In_ BOOL bManualReset,
BOOL(is), // _In_ BOOL bInitialState,
nullptr //_In_opt_ LPCTSTR lpName
))
{}
Win32Event(Reset r, Initial is, int sock, long net_events)
: Win32Event(r, is)
{
WSAEventSelect(sock, hEvent, net_events);
}
~Win32Event()
{
CloseHandle(hEvent);
}
Win32Event(const Win32Event &) = delete;
Win32Event &operator=(const Win32Event &) = delete;
void set() { SetEvent(hEvent); }
void reset() { ResetEvent(hEvent); }
HANDLE handle() { return hEvent; }
private:
HANDLE hEvent;
};
#endif // WIN32EVENT_H

View file

@ -1,13 +1,15 @@
#include "PgLanguage.h"
#include <stdexcept>
QString PgLanguage::createSql() const
{
throw std::exception("PgLanguage::createSql() not implemented");
throw std::logic_error("PgLanguage::createSql() not implemented");
}
QString PgLanguage::dropSql() const
{
throw std::exception("PgLanguage::dropSql() not implemented");
throw std::logic_error("PgLanguage::dropSql() not implemented");
}
QString PgLanguage::typeName() const

View file

@ -48,15 +48,15 @@ namespace {
for(; index < len; index++) {
QChar ch = str[index];
if (!double_quote && ch == L'\'')
if (!double_quote && ch == u'\'')
single_quote = !single_quote;
else if (!single_quote && ch == L'"')
else if (!single_quote && ch == u'"')
double_quote = !double_quote;
else if (!double_quote && !single_quote && ch == L'(')
else if (!double_quote && !single_quote && ch == u'(')
brackets++;
else if (!double_quote && !single_quote && ch == L')')
else if (!double_quote && !single_quote && ch == u')')
brackets--;
else if (!double_quote && !single_quote && brackets == 0 && ch == L',') {
else if (!double_quote && !single_quote && brackets == 0 && ch == u',') {
if (index != start_array)
res.push_back(str.mid(start_array, index - 1).trimmed());
else

View file

@ -38,7 +38,7 @@ QString PgServerObject::alterOwnerSql(const QString& ident) const
}
void PgServerObject::setAcls(boost::optional<AclList> acls)
void PgServerObject::setAcls(std::optional<AclList> acls)
{
m_acls = std::move(acls);
}

View file

@ -4,7 +4,7 @@
#include <QString>
#include "PgObject.h"
#include "PgAcl.h"
#include <boost/optional.hpp>
#include <optional>
class PgAuthId;
@ -26,7 +26,7 @@ public:
* @brief setAcls
* @param acls Important: pass empty optional when acl IS NULL, pass empty list for empty array
*/
void setAcls(boost::optional<AclList> acls);
void setAcls(std::optional<AclList> acls);
QString aclString() const;
/**
* @brief grantSql
@ -53,7 +53,7 @@ protected:
private:
Oid m_ownerOid = InvalidOid;
const PgAuthId * m_owner = nullptr;
boost::optional<AclList> m_acls;
std::optional<AclList> m_acls;
};
#endif // PGSERVEROBJECT_H

View file

@ -2,7 +2,6 @@
#define STRINGESCAPERULE_H
#include <QString>
#include <boost/optional.hpp>
enum class ConvertToNumericEscape {
Utf8, ///< Generates upto four escapes for 4 utf8 chars

View file

@ -1,5 +1,6 @@
#pragma once
#include <cstdint>
#include <string>
std::string HumanReadableBytes(uint64_t bytes);

View file

@ -9,7 +9,7 @@
#include <botan/base64.h>
#include <botan/mac.h>
#include <botan/block_cipher.h>
#include <boost/lexical_cast.hpp>
#include <botan/exceptn.h>
using namespace Botan;
@ -33,7 +33,7 @@ public:
* distinct key which is derived by hashing the wrapped key name with HMAC.
*/
const std::vector<uint8_t> wrapped_name =
nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()),
nist_key_wrap_padded(reinterpret_cast<const uint8_t*>(name.data()),
name.size(),
*m_cipher);
@ -47,7 +47,7 @@ public:
secure_vector<uint8_t> get(const std::string& name, const std::string_view &wrapped_key) const
{
const std::vector<uint8_t> wrapped_name =
nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()),
nist_key_wrap_padded(reinterpret_cast<const uint8_t*>(name.data()),
name.size(),
*m_cipher);

View file

@ -19,7 +19,11 @@ namespace Botan {
class PasswordManagerException: public std::exception {
public:
using std::exception::exception; //(char const* const _Message);
PasswordManagerException() = default;
explicit PasswordManagerException(const char *message) : m_message(message) {}
const char *what() const noexcept override { return m_message.c_str(); }
private:
std::string m_message;
};
class PasswordManagerLockedException: public PasswordManagerException {