Merge pull request #34 from Holt59/cleaning

Cleaning and improvements
This commit is contained in:
Al
2020-05-22 12:30:48 +02:00
committed by GitHub
11 changed files with 1441 additions and 1476 deletions
+494
View File
@@ -0,0 +1,494 @@
#ifndef PYTHON_CONVERTERS_HPP
#define PYTHON_CONVERTERS_HPP
#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QString>
#include <QVariant>
#include <QWidget>
// sip and qt slots seems to conflict
#include <sip.h>
// Include the container converters from utils:
#include "pythonutils.h"
namespace utils {
namespace bpy = boost::python;
namespace QString_converter {
/**
* We need this since sip does not expose QString but uses standard python str.
*/
struct QString_to_python_str
{
static PyObject* convert(const QString& str) {
// It's safer to explicitly convert to unicode as if we don't, this can return
// either str or unicode without it being easy to know which to expect
bpy::object pyStr = bpy::object(qUtf8Printable(str));
if (SIPBytes_Check(pyStr.ptr()))
pyStr = pyStr.attr("decode")("utf-8");
return bpy::incref(pyStr.ptr());
}
};
struct QString_from_python_str
{
static void* convertible(PyObject* objPtr) {
return SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr) ? objPtr : nullptr;
}
static void construct(PyObject* objPtr, bpy::converter::rvalue_from_python_stage1_data* data) {
// Ensure the string uses 8-bit characters
PyObject* strPtr = PyUnicode_Check(objPtr) ? PyUnicode_AsUTF8String(objPtr) : objPtr;
// Extract the character data from the python string
const char* value = SIPBytes_AsString(strPtr);
assert(value != nullptr);
// allocate storage
void* storage = ((bpy::converter::rvalue_from_python_storage<QString>*)data)->storage.bytes;
// construct QString in the allocated memory
new (storage) QString(value);
data->convertible = storage;
// Deallocate local copy if one was made
if (strPtr != objPtr)
Py_DecRef(strPtr);
}
};
}
namespace QFlags_converter {
/**
*
*/
template <typename T>
struct QFlags_to_int
{
static PyObject* convert(const QFlags<T>& flags) {
return bpy::incref(bpy::object(static_cast<int>(flags)).ptr());
}
};
template <typename T>
struct QFlags_from_python_obj
{
static void* convertible(PyObject* objPtr) {
return SIPLong_Check(objPtr) ? objPtr : nullptr;
}
static void construct(PyObject* objPtr, bpy::converter::rvalue_from_python_stage1_data* data) {
int intVersion = (int)SIPLong_AsLong(objPtr);
T tVersion = (T)intVersion;
void* storage = ((bpy::converter::rvalue_from_python_storage<QFlags<T>>*)data)->storage.bytes;
new (storage) QFlags<T>(tVersion);
data->convertible = storage;
}
};
}
namespace QVariant_converter {
struct QVariant_to_python_obj
{
static PyObject* convert(const QVariant& var) {
switch (var.type()) {
case QVariant::Invalid: return bpy::incref(Py_None);
case QVariant::Int: return SIPLong_FromLong(var.toInt());
case QVariant::UInt: return PyLong_FromUnsignedLong(var.toUInt());
case QVariant::Bool: return PyBool_FromLong(var.toBool());
case QVariant::String: return bpy::incref(bpy::object(var.toString()).ptr());
// We need to check for StringList here because these are not considered List
// since List is QList<QVariant> will StringList is QList<QString>:
case QVariant::StringList: return bpy::incref(bpy::object(var.toStringList()).ptr());
case QVariant::List: {
return bpy::incref(bpy::object(var.toList()).ptr());
} break;
case QVariant::Map: {
return bpy::incref(bpy::object(var.toMap()).ptr());
} break;
default: {
PyErr_Format(PyExc_TypeError, "type unsupported: %d", var.type());
throw bpy::error_already_set();
} break;
}
}
};
struct QVariant_from_python_obj
{
static void* convertible(PyObject* objPtr) {
if (!SIPBytes_Check(objPtr) && !PyUnicode_Check(objPtr) && !PyLong_Check(objPtr) &&
!PyBool_Check(objPtr) && !PyList_Check(objPtr) && !PyDict_Check(objPtr) &&
objPtr != Py_None) {
return nullptr;
}
return objPtr;
}
template <typename T>
static void constructVariant(const T& value, bpy::converter::rvalue_from_python_stage1_data* data) {
void* storage = ((bpy::converter::rvalue_from_python_storage<QVariant>*)data)->storage.bytes;
new (storage) QVariant(value);
data->convertible = storage;
}
static void constructVariant(bpy::converter::rvalue_from_python_stage1_data* data) {
void* storage = ((bpy::converter::rvalue_from_python_storage<QVariant>*)data)->storage.bytes;
new (storage) QVariant();
data->convertible = storage;
}
static void construct(PyObject* objPtr, bpy::converter::rvalue_from_python_stage1_data* data) {
if (PyList_Check(objPtr)) {
// We could check if all the elements can be converted to QString and store a QStringList
// in the QVariant but I am not sure that is really useful.
constructVariant(bpy::extract<QVariantList>(objPtr)(), data);
}
else if (objPtr == Py_None) {
constructVariant(data);
}
else if (PyDict_Check(objPtr)) {
constructVariant(bpy::extract<QVariantMap>(objPtr)(), data);
}
else if (SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr)) {
constructVariant(bpy::extract<QString>(objPtr)(), data);
}
// PyBools will also return true for SIPLong_Check but not the other way around, so the order
// here is relevant.
else if (PyBool_Check(objPtr)) {
constructVariant(bpy::extract<bool>(objPtr)(), data);
}
else if (SIPLong_Check(objPtr)) {
// QVariant doesn't have long. It has int or long long. Given that on m/s,
// long is 32 bits for 32- and 64- bit code...
constructVariant(bpy::extract<int>(objPtr)(), data);
}
else {
PyErr_SetString(PyExc_TypeError, "type unsupported");
throw bpy::error_already_set();
}
}
};
}
namespace QClass_converter {
template <typename T> struct MetaData;
template <> struct MetaData<QObject> { static const char* className() { return "QObject"; } };
template <> struct MetaData<QWidget> { static const char* className() { return "QWidget"; } };
template <> struct MetaData<QDateTime> { static const char* className() { return "QDateTime"; } };
template <> struct MetaData<QDir> { static const char* className() { return "QDir"; } };
template <> struct MetaData<QFileInfo> { static const char* className() { return "QFileInfo"; } };
template <> struct MetaData<QIcon> { static const char* className() { return "QIcon"; } };
template <> struct MetaData<QSize> { static const char* className() { return "QSize"; } };
template <> struct MetaData<QStringList> { static const char* className() { return "QStringList"; } };
template <> struct MetaData<QUrl> { static const char* className() { return "QUrl"; } };
template <> struct MetaData<QVariant> { static const char* className() { return "QVariant"; } };
template <typename T>
struct QClass_converters
{
struct QClass_to_PyQt
{
template <typename Q>
static typename std::enable_if_t<std::is_copy_constructible_v<Q>, T*> getSafeCopy(T* qClass)
{
return new T(*qClass);
}
template <typename Q>
static typename std::enable_if_t<!std::is_copy_constructible_v<Q>, T*> getSafeCopy(T* qClass)
{
return qClass;
}
static PyObject* convert(const T& object) {
const sipTypeDef* type = sipAPIAccess::sipAPI()->api_find_type(MetaData<T>::className());
if (type == nullptr) {
return bpy::incref(Py_None);
}
PyObject* sipObj = sipAPIAccess::sipAPI()->api_convert_from_type((void*)getSafeCopy<T>((T*)&object), type, 0);
if (sipObj == nullptr) {
return bpy::incref(Py_None);
}
if (std::is_copy_constructible_v<T>)
// Ensure Python deletes the C++ component
sipAPIAccess::sipAPI()->api_transfer_back(sipObj);
return bpy::incref(sipObj);
}
static PyObject* convert(T* object) {
if (object == nullptr) {
return bpy::incref(Py_None);
}
const sipTypeDef* type = sipAPIAccess::sipAPI()->api_find_type(MetaData<T>::className());
if (type == nullptr) {
return bpy::incref(Py_None);
}
PyObject* sipObj = sipAPIAccess::sipAPI()->api_convert_from_type(getSafeCopy<T>(object), type, 0);
if (sipObj == nullptr) {
return bpy::incref(Py_None);
}
if (std::is_copy_constructible_v<T>)
// Ensure Python deletes the C++ component
sipAPIAccess::sipAPI()->api_transfer_back(sipObj);
return bpy::incref(sipObj);
}
static PyObject* convert(const T* object) {
return convert((T*)object);
}
static PyTypeObject const* get_pytype() {
const sipTypeDef* type = sipAPIAccess::sipAPI()->api_find_type(MetaData<T>::className());
if (type == nullptr) {
return bpy::incref(Py_None);
}
return bpy::incref(type->td_py_type);
}
};
static void* QClass_from_PyQt(PyObject* objPtr)
{
// This would transfer responsibility for deconstructing the object to C++, but Boost assumes l-value converters (such as this) don't do that
// Instead, this should be called within the wrappers for functions which return deletable pointers.
//sipAPI()->api_transfer_to(objPtr, Py_None);
if (PyObject_TypeCheck(objPtr, sipAPIAccess::sipAPI()->api_simplewrapper_type)) {
sipSimpleWrapper* wrapper;
wrapper = reinterpret_cast<sipSimpleWrapper*>(objPtr);
return wrapper->data;
}
else if (PyObject_TypeCheck(objPtr, sipAPIAccess::sipAPI()->api_wrapper_type)) {
sipWrapper* wrapper;
wrapper = reinterpret_cast<sipWrapper*>(objPtr);
return wrapper->super.data;
}
else {
if constexpr (std::is_same_v<T, QStringList>)
{
// QStringLists aren't wrapped by PyQt - regular Python string/unicode lists are used instead
bpy::extract<QList<QString>> extractor(objPtr);
if (extractor.check())
return new QStringList(extractor());
}
PyErr_SetString(PyExc_TypeError, "type not wrapped");
bpy::throw_error_already_set();
}
return new void*;
}
};
}
namespace {
bool has_arity(PyObject* object, std::size_t arity) {
// Mostly from https://stackoverflow.com/a/36143796/2666289
bpy::object fn(bpy::handle<>(bpy::borrowed(object)));
auto inspect = bpy::import("inspect");
auto arg_spec = inspect.attr("getfullargspec")(fn);
bpy::object args = arg_spec.attr("args"),
varargs = arg_spec.attr("varargs"),
defaults = arg_spec.attr("defaults");
auto args_count = args ? bpy::len(args) : 0;
auto defaults_count = defaults ? bpy::len(defaults) : 0;
if (static_cast<bool>(inspect.attr("ismethod")(fn)) && fn.attr("__self__")) {
--args_count;
}
auto required_count = args_count - defaults_count;
return required_count <= arity // Cannot require more parameters than given,
&& (args_count >= arity || varargs); // Must accept enough parameters.
}
}
/**
* @brief Convert a python callable to a valid C++ Callable object. Also works
* for None.
*/
template <typename>
struct Functor_converter;
template <typename RET, typename... PARAMS>
struct Functor_converter<RET(PARAMS...)>
{
struct FunctorWrapper
{
FunctorWrapper(boost::python::object callable) : m_Callable(callable) {
}
~FunctorWrapper() {
GILock lock;
m_Callable = bpy::object();
}
RET operator()(const PARAMS&...params) {
GILock lock;
if constexpr (std::is_same_v<RET, void>) {
m_Callable(params...);
}
else {
return bpy::extract<RET>(m_Callable(params...));
}
}
boost::python::object m_Callable;
};
static void* convertible(PyObject* object)
{
// We allow None here, we will just default-construct a std::function:
if (object == Py_None) {
return object;
}
// Otherwize we check that we have a callable object:
if (!PyCallable_Check(object) || !has_arity(object, sizeof...(PARAMS))) {
return nullptr;
}
return object;
}
static void construct(PyObject* object, bpy::converter::rvalue_from_python_stage1_data* data)
{
bpy::object callable(bpy::handle<>(bpy::borrowed(object)));
void* storage =((bpy::converter::rvalue_from_python_storage<std::function<RET(PARAMS...)>>*)data)->storage.bytes;
if (callable.is_none()) {
new (storage) std::function<RET(PARAMS...)>{};
}
else {
new (storage) std::function<RET(PARAMS...)>(FunctorWrapper(callable));
}
data->convertible = storage;
}
};
/**
* @brief Call policy that automatically downcast shared pointer of type FromType
* to shared pointer of type ToType.
*/
template <class FromType, class ToType>
struct DowncastConverter {
bool convertible() const { return true; }
inline PyObject* operator()(std::shared_ptr<FromType> p) const {
if (p == nullptr) {
return bpy::detail::none();
}
else {
auto downcast_p = std::dynamic_pointer_cast<ToType>(p);
bpy::object p_value = downcast_p == nullptr ? bpy::object{ p } : bpy::object{ downcast_p };
return bpy::incref(p_value.ptr());
}
}
inline PyTypeObject const* get_pytype() const {
return bpy::converter::registered_pytype<FromType>::get_pytype();
}
};
template <class FromType, class ToType>
struct downcast_return {
template <class T>
struct apply_;
template <class T>
struct apply_<std::shared_ptr<T>> {
static_assert(std::is_convertible_v<std::shared_ptr<T>, std::shared_ptr<FromType>>);
using type = DowncastConverter<FromType, ToType>;
};
template <class T>
using apply = apply_<std::decay_t<T>>;
};
// Functions:
inline void register_qstring_converter() {
using namespace QString_converter;
bpy::to_python_converter<QString, QString_to_python_str>();
bpy::converter::registry::push_back(
&QString_from_python_str::convertible,
&QString_from_python_str::construct,
bpy::type_id<QString>());
}
inline void register_qvariant_converter() {
using namespace QVariant_converter;
bpy::to_python_converter<QVariant, QVariant_to_python_obj>();
bpy::converter::registry::push_back(
&QVariant_from_python_obj::convertible,
&QVariant_from_python_obj::construct,
bpy::type_id<QVariant>());
}
template <class Flags>
inline void register_qflags_converter() {
using T = typename Flags::enum_type;
using namespace QFlags_converter;
bpy::to_python_converter<Flags, QFlags_to_int<T>>();
bpy::converter::registry::push_back(
&QFlags_from_python_obj<T>::convertible,
&QFlags_from_python_obj<T>::construct,
bpy::type_id<Flags>());
}
template <class QClass>
inline void register_qclass_converter() {
using Converter = QClass_converter::QClass_converters<QClass>;
bpy::converter::registry::insert(&Converter::QClass_from_PyQt, bpy::type_id<QClass>());
bpy::to_python_converter<const QClass*, typename Converter::QClass_to_PyQt>();
bpy::to_python_converter<QClass*, typename Converter::QClass_to_PyQt>();
bpy::to_python_converter<QClass, typename Converter::QClass_to_PyQt>();
}
template <class Fn>
inline void register_functor_converter() {
using Converter = Functor_converter<Fn>;
bpy::converter::registry::push_back(
&Converter::convertible,
&Converter::construct,
bpy::type_id<std::function<Fn>>());
}
}
#endif
-17
View File
@@ -8,23 +8,6 @@
using namespace MOBase;
namespace bpy = boost::python;
void reportPythonError()
{
if (PyErr_Occurred()) {
ErrWrapper &errWrapper = ErrWrapper::instance();
errWrapper.startRecordingExceptionMessage();
PyErr_Print();
errWrapper.stopRecordingExceptionMessage();
QString errMsg = errWrapper.getLastExceptionMessage();
throw MyException(errMsg);
} else {
throw MyException("An unexpected C++ exception was thrown in python code");
}
}
ErrWrapper & ErrWrapper::instance()
{
static ErrWrapper err;
+104 -7
View File
@@ -1,16 +1,18 @@
#ifndef ERROR_H
#define ERROR_H
#include <QString>
#include <sstream>
// turn an error from the python interpreter into an exception
void reportPythonError();
#include <QString>
#include <fmt/format.h>
#include <sstream>
#include <utility.h>
struct ErrWrapper
{
static ErrWrapper & instance();
void write(const char * message);
static ErrWrapper& instance();
void write(const char* message);
void startRecordingExceptionMessage();
@@ -23,4 +25,99 @@ struct ErrWrapper
std::stringstream lastException;
};
namespace pyexcept {
/**
* @brief Exception to throw when a python implementation does not implement
* a pure virtual function.
*/
class MissingImplementation : public MOBase::MyException {
public:
MissingImplementation(std::string const& className, std::string const& methodName) :
MyException(QString::fromStdString(
fmt::format("Python class implementing \"{}\" has no implementation of method \"{}\".",
className, methodName))) { }
};
/**
* @brief Exception to throw when a python error occurs.
*/
class PythonError : public MOBase::MyException {
public:
/**
* @brief Create a new PythonError, fetching the error message from python. If the message
* cannot be retrieved, `defaultErrorMessage()` is used instead.
*/
PythonError() : MyException(getPythonErrorMessage()) { }
/**
* @brief Create a new PythonError with the given message.
*
* @param message Message for the exception.
*/
PythonError(QString message) : MyException(message) { }
protected:
/**
*
*/
static QString defaultErrorMessage() {
return QObject::tr("An unexpected C++ exception was thrown in python code.");
}
/**
*
*/
static QString getPythonErrorMessage() {
if (PyErr_Occurred()) {
ErrWrapper& errWrapper = ErrWrapper::instance();
errWrapper.startRecordingExceptionMessage();
PyErr_Print();
errWrapper.stopRecordingExceptionMessage();
return errWrapper.getLastExceptionMessage();
}
else {
return defaultErrorMessage();
}
}
};
/**
* @brief Exception to throw when an unknown error occured. This is typically thrown
* from a catch(...) block.
*/
class UnknownException : public MOBase::MyException {
public:
/**
* @brief Create a new UnknownException with the default message.
*
* @see defaultErrorMessage
*/
UnknownException() : MyException(defaultErrorMessage()) { }
/**
* @brief Create a new UnknownException with the given message.
*
* @param message Message for the exception.
*/
UnknownException(QString message) : MyException(message) { }
protected:
/**
*
*/
static QString defaultErrorMessage() {
return QObject::tr("An unknown exception was thrown in python code.");
}
};
}
#endif // ERROR_H
+62 -55
View File
@@ -9,7 +9,6 @@
#include <isavegame.h>
#include <isavegameinfowidget.h>
#include "gilock.h"
#include "pythonwrapperutilities.h"
/////////////////////////////
@@ -18,22 +17,22 @@
bool BSAInvalidationWrapper::isInvalidationBSA(const QString &bsaName)
{
return basicWrapperFunctionImplementation<BSAInvalidationWrapper, bool>(this, "isInvalidationBSA", bsaName);
return basicWrapperFunctionImplementation<bool>(this, "isInvalidationBSA", bsaName);
}
void BSAInvalidationWrapper::deactivate(MOBase::IProfile *profile)
{
return basicWrapperFunctionImplementation<BSAInvalidationWrapper, void>(this, "deactivate", boost::python::ptr(profile));
return basicWrapperFunctionImplementation<void>(this, "deactivate", boost::python::ptr(profile));
}
void BSAInvalidationWrapper::activate(MOBase::IProfile *profile)
{
return basicWrapperFunctionImplementation<BSAInvalidationWrapper, void>(this, "activate", boost::python::ptr(profile));
return basicWrapperFunctionImplementation<void>(this, "activate", boost::python::ptr(profile));
}
bool BSAInvalidationWrapper::prepareProfile(MOBase::IProfile *profile)
{
return basicWrapperFunctionImplementation<BSAInvalidationWrapper, bool>(this, "prepareProfile", boost::python::ptr(profile));
return basicWrapperFunctionImplementation<bool>(this, "prepareProfile", boost::python::ptr(profile));
}
/// end BSAInvalidation Wrapper
/////////////////////////////
@@ -42,22 +41,22 @@ bool BSAInvalidationWrapper::prepareProfile(MOBase::IProfile *profile)
QStringList DataArchivesWrapper::vanillaArchives() const
{
return basicWrapperFunctionImplementation<DataArchivesWrapper, QStringList>(this, "vanillaArchives");
return basicWrapperFunctionImplementation<QStringList>(this, "vanillaArchives");
}
QStringList DataArchivesWrapper::archives(const MOBase::IProfile *profile) const
{
return basicWrapperFunctionImplementation<DataArchivesWrapper, QStringList>(this, "archives", boost::python::ptr(profile));
return basicWrapperFunctionImplementation<QStringList>(this, "archives", boost::python::ptr(profile));
}
void DataArchivesWrapper::addArchive(MOBase::IProfile *profile, int index, const QString &archiveName)
{
return basicWrapperFunctionImplementation<DataArchivesWrapper, void>(this, "addArchive", boost::python::ptr(profile), index, archiveName);
return basicWrapperFunctionImplementation<void>(this, "addArchive", boost::python::ptr(profile), index, archiveName);
}
void DataArchivesWrapper::removeArchive(MOBase::IProfile *profile, const QString &archiveName)
{
return basicWrapperFunctionImplementation<DataArchivesWrapper, void>(this, "removeArchive", boost::python::ptr(profile), archiveName);
return basicWrapperFunctionImplementation<void>(this, "removeArchive", boost::python::ptr(profile), archiveName);
}
/// end DataArchives Wrapper
/////////////////////////////
@@ -66,22 +65,22 @@ void DataArchivesWrapper::removeArchive(MOBase::IProfile *profile, const QString
void GamePluginsWrapper::writePluginLists(const MOBase::IPluginList * pluginList)
{
return basicWrapperFunctionImplementation<GamePluginsWrapper, void>(this, "writePluginLists", boost::python::ptr(pluginList));
return basicWrapperFunctionImplementation<void>(this, "writePluginLists", boost::python::ptr(pluginList));
}
void GamePluginsWrapper::readPluginLists(MOBase::IPluginList * pluginList)
{
return basicWrapperFunctionImplementation<GamePluginsWrapper, void>(this, "readPluginLists", boost::python::ptr(pluginList));
return basicWrapperFunctionImplementation<void>(this, "readPluginLists", boost::python::ptr(pluginList));
}
void GamePluginsWrapper::getLoadOrder(QStringList &loadOrder)
{
return basicWrapperFunctionImplementation<GamePluginsWrapper, void>(this, "getLoadOrder", loadOrder);
return basicWrapperFunctionImplementation<void>(this, "getLoadOrder", loadOrder);
}
bool GamePluginsWrapper::lightPluginsAreSupported()
{
return basicWrapperFunctionImplementation<GamePluginsWrapper, bool>(this, "lightPluginsAreSupported");
return basicWrapperFunctionImplementation<bool>(this, "lightPluginsAreSupported");
}
/// end GamePlugins Wrapper
@@ -91,38 +90,45 @@ bool GamePluginsWrapper::lightPluginsAreSupported()
MappingType LocalSavegamesWrapper::mappings(const QDir & profileSaveDir) const
{
return basicWrapperFunctionImplementation<LocalSavegamesWrapper, MappingType>(this, "mappings", profileSaveDir);
return basicWrapperFunctionImplementation<MappingType>(this, "mappings", profileSaveDir);
}
bool LocalSavegamesWrapper::prepareProfile(MOBase::IProfile * profile)
{
return basicWrapperFunctionImplementation<LocalSavegamesWrapper, bool>(this, "prepareProfile", boost::python::ptr(profile));
return basicWrapperFunctionImplementation<bool>(this, "prepareProfile", boost::python::ptr(profile));
}
/// end LocalSavegames Wrapper
/////////////////////////////
/// ModDataChecker Wrapper
bool ModDataCheckerWrapper::dataLooksValid(std::shared_ptr<const MOBase::IFileTree> fileTree) const {
return basicWrapperFunctionImplementation<bool>(this, "dataLooksValid", fileTree);
}
/// end ModDataChecker Wrapper
/////////////////////////////
/// SaveGameInfo Wrapper
MOBase::ISaveGame const * SaveGameInfoWrapper::getSaveGameInfo(QString const & file) const
{
return basicWrapperFunctionImplementation<SaveGameInfoWrapper, MOBase::ISaveGame const *>(this, "getSaveGameInfo", file);
return basicWrapperFunctionImplementation<MOBase::ISaveGame*>(this, m_SaveGames[file], "getSaveGameInfo", file);
}
SaveGameInfoWrapper::MissingAssets SaveGameInfoWrapper::getMissingAssets(QString const & file) const
{
return basicWrapperFunctionImplementation<SaveGameInfoWrapper, SaveGameInfoWrapper::MissingAssets>(this, "getMissingAssets", file);
return basicWrapperFunctionImplementation<SaveGameInfoWrapper::MissingAssets>(this, "getMissingAssets", file);
}
MOBase::ISaveGameInfoWidget * SaveGameInfoWrapper::getSaveGameWidget(QWidget * parent) const
MOBase::ISaveGameInfoWidget* SaveGameInfoWrapper::getSaveGameWidget(QWidget* parent) const
{
qCritical("Calling method with unimplemented from_python converter.");
return basicWrapperFunctionImplementation<SaveGameInfoWrapper, MOBase::ISaveGameInfoWidget *>(this, "getSaveGameWidget", boost::python::ptr(parent));
return basicWrapperFunctionImplementation<MOBase::ISaveGameInfoWidget*>(this, m_SaveGameWidget, "getSaveGameWidget", parent);
}
bool SaveGameInfoWrapper::hasScriptExtenderSave(QString const & file) const
{
return basicWrapperFunctionImplementation<SaveGameInfoWrapper, bool>(this, "hasScriptExtenderSave", file);
return basicWrapperFunctionImplementation<bool>(this, "hasScriptExtenderSave", file);
}
/// end SaveGameInfo Wrapper
/////////////////////////////
@@ -130,42 +136,42 @@ bool SaveGameInfoWrapper::hasScriptExtenderSave(QString const & file) const
QString ScriptExtenderWrapper::BinaryName() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QString>(this, "BinaryName");
return basicWrapperFunctionImplementation<QString>(this, "BinaryName");
}
QString ScriptExtenderWrapper::PluginPath() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QString>(this, "PluginPath");
return basicWrapperFunctionImplementation<QString>(this, "PluginPath");
}
QString ScriptExtenderWrapper::loaderName() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QString>(this, "loaderName");
return basicWrapperFunctionImplementation<QString>(this, "loaderName");
}
QString ScriptExtenderWrapper::loaderPath() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QString>(this, "loaderPath");
return basicWrapperFunctionImplementation<QString>(this, "loaderPath");
}
QStringList ScriptExtenderWrapper::saveGameAttachmentExtensions() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QStringList>(this, "saveGameAttachmentExtensions");
return basicWrapperFunctionImplementation<QStringList>(this, "saveGameAttachmentExtensions");
}
bool ScriptExtenderWrapper::isInstalled() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, bool>(this, "isInstalled");
return basicWrapperFunctionImplementation<bool>(this, "isInstalled");
}
QString ScriptExtenderWrapper::getExtenderVersion() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QString>(this, "getExtenderVersion");
return basicWrapperFunctionImplementation<QString>(this, "getExtenderVersion");
}
WORD ScriptExtenderWrapper::getArch() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, WORD>(this, "getArch");
return basicWrapperFunctionImplementation<WORD>(this, "getArch");
}
/// end ScriptExtender Wrapper
@@ -175,31 +181,26 @@ WORD ScriptExtenderWrapper::getArch() const
QStringList UnmanagedModsWrapper::mods(bool onlyOfficial) const
{
return basicWrapperFunctionImplementation<UnmanagedModsWrapper, QStringList>(this, "mods", onlyOfficial);
return basicWrapperFunctionImplementation<QStringList>(this, "mods", onlyOfficial);
}
QString UnmanagedModsWrapper::displayName(const QString & modName) const
{
return basicWrapperFunctionImplementation<UnmanagedModsWrapper, QString>(this, "displayName", modName);
return basicWrapperFunctionImplementation<QString>(this, "displayName", modName);
}
QFileInfo UnmanagedModsWrapper::referenceFile(const QString & modName) const
{
return basicWrapperFunctionImplementation<UnmanagedModsWrapper, QFileInfo>(this, "referenceFile", modName);
return basicWrapperFunctionImplementation<QFileInfo>(this, "referenceFile", modName);
}
QStringList UnmanagedModsWrapper::secondaryFiles(const QString & modName) const
{
return basicWrapperFunctionImplementation<UnmanagedModsWrapper, QStringList>(this, "secondaryFiles", modName);
return basicWrapperFunctionImplementation<QStringList>(this, "secondaryFiles", modName);
}
/// end UnmanagedMods Wrapper
/////////////////////////////
template<typename T>
void insertGameFeature(std::map<std::type_index, boost::any> &map, const boost::python::object &pyObject)
{
map[std::type_index(typeid(T))] = boost::python::extract<T*>(pyObject)();
}
game_features_map_from_python::game_features_map_from_python()
{
@@ -211,6 +212,12 @@ void * game_features_map_from_python::convertible(PyObject * objPtr)
return PyDict_Check(objPtr) ? objPtr : nullptr;
}
template<typename T>
void insertGameFeature(std::map<std::type_index, boost::any>& map, const boost::python::object& pyObject)
{
map[std::type_index(typeid(T))] = boost::python::extract<T*>(pyObject)();
}
void game_features_map_from_python::construct(PyObject * objPtr, boost::python::converter::rvalue_from_python_stage1_data * data)
{
void *storage = ((boost::python::converter::rvalue_from_python_storage<std::map<std::type_index, boost::any>>*)data)->storage.bytes;
@@ -221,22 +228,18 @@ void game_features_map_from_python::construct(PyObject * objPtr, boost::python::
for (int i = 0; i < len; ++i)
{
boost::python::object pyKey = keys[i];
// pyKey should be a Boost.Python.class corresponding to a game feature.
std::string className = boost::python::extract<std::string>(pyKey.attr("__name__"))();
if (className == "BSAInvalidation")
insertGameFeature<BSAInvalidation>(*result, source[pyKey]);
else if (className == "DataArchives")
insertGameFeature<DataArchives>(*result, source[pyKey]);
else if (className == "GamePlugins")
insertGameFeature<GamePlugins>(*result, source[pyKey]);
else if (className == "LocalSavegames")
insertGameFeature<LocalSavegames>(*result, source[pyKey]);
else if (className == "SaveGameInfo")
insertGameFeature<SaveGameInfo>(*result, source[pyKey]);
else if (className == "ScriptExtender")
insertGameFeature<ScriptExtender>(*result, source[pyKey]);
else if (className == "UnmanagedMods")
insertGameFeature<UnmanagedMods>(*result, source[pyKey]);
boost::python::object pyValue = source[pyKey];
boost::mp11::mp_for_each<
// Must user pointers because mp_for_each construct object:
boost::mp11::mp_transform<std::add_pointer_t, MpGameFeaturesList>
>([&](auto* pt) {
using T = std::remove_pointer_t<decltype(pt)>;
boost::python::extract<T*> extract(pyValue);
if (extract.check()) {
(*result)[std::type_index(typeid(T))] = extract();
}
});
}
data->convertible = storage;
@@ -274,10 +277,14 @@ void registerGameFeaturesPythonConverters()
.def("prepareProfile", bpy::pure_virtual(&LocalSavegames::prepareProfile))
;
bpy::class_<ModDataCheckerWrapper, boost::noncopyable>("ModDataChecker")
.def("dataLooksValid", bpy::pure_virtual(&ModDataChecker::dataLooksValid))
;
bpy::class_<SaveGameInfoWrapper, boost::noncopyable>("SaveGameInfo")
.def("getSaveGameInfo", bpy::pure_virtual(&SaveGameInfo::getSaveGameInfo), bpy::return_value_policy<bpy::manage_new_object>())
.def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets))
.def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy<bpy::manage_new_object>())
.def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy<bpy::manage_new_object>(), "[optional]")
.def("hasScriptExtenderSave", bpy::pure_virtual(&SaveGameInfo::hasScriptExtenderSave))
;
+30
View File
@@ -1,16 +1,32 @@
#ifndef GAMEFEATURESWRAPPERS_H
#define GAMEFEATURESWRAPPERS_H
#include <map>
#include <bsainvalidation.h>
#include <dataarchives.h>
#include <gameplugins.h>
#include <localsavegames.h>
#include <moddatachecker.h>
#include <savegameinfo.h>
#include <scriptextender.h>
#include <unmanagedmods.h>
// this might need turning off if Q_MOC_RUN is defined
#include <boost/python.hpp>
#include <boost/mp11.hpp>
// This is a simple MPL list that contains all the game features in one place:
using MpGameFeaturesList = boost::mp11::mp_list<
BSAInvalidation,
DataArchives,
GamePlugins,
LocalSavegames,
ModDataChecker,
SaveGameInfo,
ScriptExtender,
UnmanagedMods
>;
/////////////////////////////
/// Wrapper declarations
@@ -61,6 +77,15 @@ public:
virtual bool prepareProfile(MOBase::IProfile *profile) override;
};
class ModDataCheckerWrapper : public ModDataChecker, public boost::python::wrapper<ModDataChecker>
{
public:
static constexpr const char* className = "ModDataCheckerWrapper";
using boost::python::wrapper<ModDataChecker>::get_override;
virtual bool dataLooksValid(std::shared_ptr<const MOBase::IFileTree> fileTree) const;
};
class SaveGameInfoWrapper : public SaveGameInfo, public boost::python::wrapper<SaveGameInfo>
{
public:
@@ -71,6 +96,11 @@ public:
virtual MissingAssets getMissingAssets(QString const &file) const override;
virtual MOBase::ISaveGameInfoWidget *getSaveGameWidget(QWidget *parent = 0) const override;
virtual bool hasScriptExtenderSave(QString const &file) const override;
private:
// We need to keep the python objects alive:
mutable std::map<QString, boost::python::object> m_SaveGames;
mutable boost::python::object m_SaveGameWidget;
};
class ScriptExtenderWrapper : public ScriptExtender, public boost::python::wrapper<ScriptExtender>
+76 -71
View File
@@ -31,37 +31,37 @@ using namespace MOBase;
#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(class_name) \
bool class_name::init(MOBase::IOrganizer *moInfo) \
{ \
return basicWrapperFunctionImplementation<class_name, bool>(this, "init", boost::python::ptr(moInfo)); \
return basicWrapperFunctionImplementation<bool>(this, "init", boost::python::ptr(moInfo)); \
} \
\
QString class_name::name() const \
{ \
return basicWrapperFunctionImplementation<class_name, QString>(this, "name"); \
return basicWrapperFunctionImplementation<QString>(this, "name"); \
} \
\
QString class_name::author() const \
{ \
return basicWrapperFunctionImplementation<class_name, QString>(this, "author"); \
return basicWrapperFunctionImplementation<QString>(this, "author"); \
} \
\
QString class_name::description() const \
{ \
return basicWrapperFunctionImplementation<class_name, QString>(this, "description"); \
return basicWrapperFunctionImplementation<QString>(this, "description"); \
} \
\
MOBase::VersionInfo class_name::version() const \
{ \
return basicWrapperFunctionImplementation<class_name, MOBase::VersionInfo>(this, "version"); \
return basicWrapperFunctionImplementation<MOBase::VersionInfo>(this, "version"); \
} \
\
bool class_name::isActive() const \
{ \
return basicWrapperFunctionImplementation<class_name, bool>(this, "isActive"); \
return basicWrapperFunctionImplementation<bool>(this, "isActive"); \
} \
\
QList<MOBase::PluginSetting> class_name::settings() const \
{ \
return basicWrapperFunctionImplementation<class_name, QList<MOBase::PluginSetting>>(this, "settings"); \
return basicWrapperFunctionImplementation<QList<MOBase::PluginSetting>>(this, "settings"); \
}
/// end COMMON_I_PLUGIN_WRAPPER_DEFINITIONS
@@ -79,33 +79,29 @@ COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginDiagnoseWrapper)
std::vector<unsigned int> IPluginDiagnoseWrapper::activeProblems() const
{
return basicWrapperFunctionImplementation<IPluginDiagnoseWrapper, std::vector<unsigned int>>(this, "activeProblems");
return basicWrapperFunctionImplementation<std::vector<unsigned int>>(this, "activeProblems");
}
QString IPluginDiagnoseWrapper::shortDescription(unsigned int key) const
{
return basicWrapperFunctionImplementation<IPluginDiagnoseWrapper, QString>(this, "shortDescription", key);
return basicWrapperFunctionImplementation<QString>(this, "shortDescription", key);
}
QString IPluginDiagnoseWrapper::fullDescription(unsigned int key) const
{
return basicWrapperFunctionImplementation<IPluginDiagnoseWrapper, QString>(this, "fullDescription", key);
return basicWrapperFunctionImplementation<QString>(this, "fullDescription", key);
}
bool IPluginDiagnoseWrapper::hasGuidedFix(unsigned int key) const
{
return basicWrapperFunctionImplementation<IPluginDiagnoseWrapper, bool>(this, "hasGuidedFix", key);
return basicWrapperFunctionImplementation<bool>(this, "hasGuidedFix", key);
}
void IPluginDiagnoseWrapper::startGuidedFix(unsigned int key) const
{
basicWrapperFunctionImplementation<IPluginDiagnoseWrapper, void>(this, "startGuidedFix", key);
basicWrapperFunctionImplementation<void>(this, "startGuidedFix", key);
}
void IPluginDiagnoseWrapper::invalidate()
{
IPluginDiagnose::invalidate();
}
/// end IPluginDiagnose Wrapper
/////////////////////////////////////
/// IPluginFileMapper Wrapper
@@ -115,7 +111,7 @@ COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginFileMapperWrapper)
MappingType IPluginFileMapperWrapper::mappings() const
{
return basicWrapperFunctionImplementation<IPluginFileMapperWrapper, MappingType>(this, "mappings");
return basicWrapperFunctionImplementation<MappingType>(this, "mappings");
}
/// end IPluginFileMapper Wrapper
/////////////////////////////////////
@@ -124,178 +120,178 @@ MappingType IPluginFileMapperWrapper::mappings() const
QString IPluginGameWrapper::gameName() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QString>(this, "gameName");
return basicWrapperFunctionImplementation<QString>(this, "gameName");
}
void IPluginGameWrapper::initializeProfile(const QDir & directory, ProfileSettings settings) const
{
basicWrapperFunctionImplementation<IPluginGameWrapper, void>(this, "initializeProfile", directory, settings);
basicWrapperFunctionImplementation<void>(this, "initializeProfile", directory, settings);
}
QString IPluginGameWrapper::savegameExtension() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QString>(this, "savegameExtension");
return basicWrapperFunctionImplementation<QString>(this, "savegameExtension");
}
QString IPluginGameWrapper::savegameSEExtension() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QString>(this, "savegameSEExtension");
return basicWrapperFunctionImplementation<QString>(this, "savegameSEExtension");
}
bool IPluginGameWrapper::isInstalled() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, bool>(this, "isInstalled");
return basicWrapperFunctionImplementation<bool>(this, "isInstalled");
}
QIcon IPluginGameWrapper::gameIcon() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QIcon>(this, "gameIcon");
return basicWrapperFunctionImplementation<QIcon>(this, "gameIcon");
}
QDir IPluginGameWrapper::gameDirectory() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QDir>(this, "gameDirectory");
return basicWrapperFunctionImplementation<QDir>(this, "gameDirectory");
}
QDir IPluginGameWrapper::dataDirectory() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QDir>(this, "dataDirectory");
return basicWrapperFunctionImplementation<QDir>(this, "dataDirectory");
}
void IPluginGameWrapper::setGamePath(const QString & path)
{
basicWrapperFunctionImplementation<IPluginGameWrapper, void>(this, "setGamePath", path);
basicWrapperFunctionImplementation<void>(this, "setGamePath", path);
}
QDir IPluginGameWrapper::documentsDirectory() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QDir>(this, "documentsDirectory");
return basicWrapperFunctionImplementation<QDir>(this, "documentsDirectory");
}
QDir IPluginGameWrapper::savesDirectory() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QDir>(this, "savesDirectory");
return basicWrapperFunctionImplementation<QDir>(this, "savesDirectory");
}
QList<MOBase::ExecutableInfo> IPluginGameWrapper::executables() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QList<MOBase::ExecutableInfo>>(this, "executables");
return basicWrapperFunctionImplementation<QList<MOBase::ExecutableInfo>>(this, "executables");
}
QList<MOBase::ExecutableForcedLoadSetting> IPluginGameWrapper::executableForcedLoads() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QList<MOBase::ExecutableForcedLoadSetting>>(this, "executableForcedLoads");
return basicWrapperFunctionImplementation<QList<MOBase::ExecutableForcedLoadSetting>>(this, "executableForcedLoads");
}
QString IPluginGameWrapper::steamAPPId() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QString>(this, "steamAPPId");
return basicWrapperFunctionImplementation<QString>(this, "steamAPPId");
}
QStringList IPluginGameWrapper::primaryPlugins() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QStringList>(this, "primaryPlugins");
return basicWrapperFunctionImplementation<QStringList>(this, "primaryPlugins");
}
QStringList IPluginGameWrapper::gameVariants() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QStringList>(this, "gameVariants");
return basicWrapperFunctionImplementation<QStringList>(this, "gameVariants");
}
void IPluginGameWrapper::setGameVariant(const QString & variant)
{
basicWrapperFunctionImplementation<IPluginGameWrapper, void>(this, "setGameVariant", variant);
basicWrapperFunctionImplementation<void>(this, "setGameVariant", variant);
}
QString IPluginGameWrapper::binaryName() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QString>(this, "binaryName");
return basicWrapperFunctionImplementation<QString>(this, "binaryName");
}
QString IPluginGameWrapper::gameShortName() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QString>(this, "gameShortName");
return basicWrapperFunctionImplementation<QString>(this, "gameShortName");
}
QStringList IPluginGameWrapper::primarySources() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QStringList>(this, "primarySources");
return basicWrapperFunctionImplementation<QStringList>(this, "primarySources");
}
QStringList IPluginGameWrapper::validShortNames() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QStringList>(this, "validShortNames");
return basicWrapperFunctionImplementation<QStringList>(this, "validShortNames");
}
QString IPluginGameWrapper::gameNexusName() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QString>(this, "gameNexusName");
return basicWrapperFunctionImplementation<QString>(this, "gameNexusName");
}
QStringList IPluginGameWrapper::iniFiles() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QStringList>(this, "iniFiles");
return basicWrapperFunctionImplementation<QStringList>(this, "iniFiles");
}
QStringList IPluginGameWrapper::DLCPlugins() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QStringList>(this, "DLCPlugins");
return basicWrapperFunctionImplementation<QStringList>(this, "DLCPlugins");
}
QStringList IPluginGameWrapper::CCPlugins() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QStringList>(this, "CCPlugins");
return basicWrapperFunctionImplementation<QStringList>(this, "CCPlugins");
}
IPluginGame::LoadOrderMechanism IPluginGameWrapper::loadOrderMechanism() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, IPluginGame::LoadOrderMechanism>(this, "loadOrderMechanism");
return basicWrapperFunctionImplementation<IPluginGame::LoadOrderMechanism>(this, "loadOrderMechanism");
}
IPluginGame::SortMechanism IPluginGameWrapper::sortMechanism() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, IPluginGame::SortMechanism>(this, "sortMechanism");
return basicWrapperFunctionImplementation<IPluginGame::SortMechanism>(this, "sortMechanism");
}
int IPluginGameWrapper::nexusModOrganizerID() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, int>(this, "nexusModOrganizerID");
return basicWrapperFunctionImplementation<int>(this, "nexusModOrganizerID");
}
int IPluginGameWrapper::nexusGameID() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, int>(this, "nexusGameID");
return basicWrapperFunctionImplementation<int>(this, "nexusGameID");
}
bool IPluginGameWrapper::looksValid(QDir const & dir) const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, bool>(this, "looksValid", dir);
return basicWrapperFunctionImplementation<bool>(this, "looksValid", dir);
}
QString IPluginGameWrapper::gameVersion() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QString>(this, "gameVersion");
return basicWrapperFunctionImplementation<QString>(this, "gameVersion");
}
QString IPluginGameWrapper::getLauncherName() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, QString>(this, "getLauncherName");
return basicWrapperFunctionImplementation<QString>(this, "getLauncherName");
}
COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginGameWrapper)
std::map<std::type_index, boost::any> IPluginGameWrapper::featureList() const
{
return basicWrapperFunctionImplementation<IPluginGameWrapper, std::map<std::type_index, boost::any>>(this, "_featureList");
return basicWrapperFunctionImplementation<std::map<std::type_index, boost::any>>(this, "_featureList");
}
/// end IPluginGame Wrapper
/////////////////////////////////////
/// IPluginInstaller macro
#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(class_name) \
unsigned int class_name::priority() const { return basicWrapperFunctionImplementation<class_name, unsigned int>(this, "priority"); } \
bool class_name::isManualInstaller() const { return basicWrapperFunctionImplementation<class_name, bool>(this, "isManualInstaller"); } \
bool class_name::isArchiveSupported(std::shared_ptr<const IFileTree> tree) const { return basicWrapperFunctionImplementation<class_name, bool>(this, "isArchiveSupported", tree); }
unsigned int class_name::priority() const { return basicWrapperFunctionImplementation<unsigned int>(this, "priority"); } \
bool class_name::isManualInstaller() const { return basicWrapperFunctionImplementation<bool>(this, "isManualInstaller"); } \
bool class_name::isArchiveSupported(std::shared_ptr<const IFileTree> tree) const { return basicWrapperFunctionImplementation<bool>(this, "isArchiveSupported", tree); }
/// end IPluginInstaller macro
/////////////////////////////////////
@@ -314,7 +310,7 @@ IPluginInstaller::EInstallResult IPluginInstallerSimpleWrapper::install(
IPluginInstaller::EInstallResult,
std::shared_ptr<IFileTree>,
std::tuple<IPluginInstaller::EInstallResult, std::shared_ptr<IFileTree>, QString, int>> ;
auto ret = basicWrapperFunctionImplementation<IPluginInstallerSimpleWrapper, return_type>(this, "install", boost::ref(modName), tree, version, nexusID);
auto ret = basicWrapperFunctionImplementation<return_type>(this, "install", boost::ref(modName), tree, version, nexusID);
return std::visit([&](auto const& t) {
using type = std::decay_t<decltype(t)>;
@@ -342,12 +338,12 @@ COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(IPluginInstallerCustomWrapper)
bool IPluginInstallerCustomWrapper::isArchiveSupported(const QString &archiveName) const
{
return basicWrapperFunctionImplementation<IPluginInstallerCustomWrapper, bool>(this, "isArchiveSupported", archiveName);
return basicWrapperFunctionImplementation<bool>(this, "isArchiveSupported", archiveName);
}
std::set<QString> IPluginInstallerCustomWrapper::supportedExtensions() const
{
return basicWrapperFunctionImplementation<IPluginInstallerCustomWrapper, std::set<QString>>(this, "supportedExtensions");
return basicWrapperFunctionImplementation<std::set<QString>>(this, "supportedExtensions");
}
IPluginInstaller::EInstallResult IPluginInstallerCustomWrapper::install(
@@ -355,7 +351,7 @@ IPluginInstaller::EInstallResult IPluginInstallerCustomWrapper::install(
{
// Note: This requires far more less trouble than the "Simple" installer version since 1) there is no tree
// and 2) there version and modId cannot be modified:
return basicWrapperFunctionImplementation<IPluginInstallerCustomWrapper, IPluginInstaller::EInstallResult>(
return basicWrapperFunctionImplementation<IPluginInstaller::EInstallResult>(
this, "install", boost::ref(modName), gameName, archiveName, version, modID);
}
@@ -368,32 +364,32 @@ COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginModPageWrapper)
QString IPluginModPageWrapper::displayName() const
{
return basicWrapperFunctionImplementation<IPluginModPageWrapper, QString>(this, "displayName");
return basicWrapperFunctionImplementation<QString>(this, "displayName");
}
QIcon IPluginModPageWrapper::icon() const
{
return basicWrapperFunctionImplementation<IPluginModPageWrapper, QIcon>(this, "icon");
return basicWrapperFunctionImplementation<QIcon>(this, "icon");
}
QUrl IPluginModPageWrapper::pageURL() const
{
return basicWrapperFunctionImplementation<IPluginModPageWrapper, QUrl>(this, "pageURL");
return basicWrapperFunctionImplementation<QUrl>(this, "pageURL");
}
bool IPluginModPageWrapper::useIntegratedBrowser() const
{
return basicWrapperFunctionImplementation<IPluginModPageWrapper, bool>(this, "useIntegratedBrowser");
return basicWrapperFunctionImplementation<bool>(this, "useIntegratedBrowser");
}
bool IPluginModPageWrapper::handlesDownload(const QUrl & pageURL, const QUrl & downloadURL, MOBase::ModRepositoryFileInfo & fileInfo) const
{
return basicWrapperFunctionImplementation<IPluginModPageWrapper, bool>(this, "handlesDownload", pageURL, downloadURL, fileInfo);
return basicWrapperFunctionImplementation<bool>(this, "handlesDownload", pageURL, downloadURL, fileInfo);
}
void IPluginModPageWrapper::setParentWidget(QWidget * widget)
{
basicWrapperFunctionImplementation<IPluginModPageWrapper, void>(this, "setParentWidget", widget);
basicWrapperFunctionImplementationWithDefault<void>(this, &IPluginModPageWrapper::setParentWidget_Default, "setParentWidget", widget);
}
/// end IPluginModPage Wrapper
/////////////////////////////
@@ -404,7 +400,7 @@ COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginPreviewWrapper)
std::set<QString> IPluginPreviewWrapper::supportedExtensions() const
{
return basicWrapperFunctionImplementation<IPluginPreviewWrapper, std::set<QString>>(this, "supportedExtensions");
return basicWrapperFunctionImplementation<std::set<QString>>(this, "supportedExtensions");
}
QWidget *IPluginPreviewWrapper::genFilePreview(const QString &fileName, const QSize &maxSize) const
@@ -414,12 +410,21 @@ QWidget *IPluginPreviewWrapper::genFilePreview(const QString &fileName, const QS
GILock lock;
boost::python::override implementation = this->get_override("genFilePreview");
if (!implementation)
throw MissingImplementation(this->className, "genFilePreview");
throw pyexcept::MissingImplementation(this->className, "genFilePreview");
boost::python::object pyVersion = implementation(fileName, maxSize);
// We need responsibility for deleting the QWidget to be transferred to C++
sipAPIAccess::sipAPI()->api_transfer_to(pyVersion.ptr(), Py_None);
return boost::python::extract<QWidget *>(pyVersion)();
} PYCATCH;
}
catch (const boost::python::error_already_set&) {
throw pyexcept::PythonError();
}
catch (pyexcept::MissingImplementation const& missingImplementation) {
throw missingImplementation;
}
catch (...) {
throw pyexcept::UnknownException();
}
}
/// end IPluginPreview Wrapper
/////////////////////////////
@@ -430,27 +435,27 @@ COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginToolWrapper)
QString IPluginToolWrapper::displayName() const
{
return basicWrapperFunctionImplementation<IPluginToolWrapper, QString>(this, "displayName");
return basicWrapperFunctionImplementation<QString>(this, "displayName");
}
QString IPluginToolWrapper::tooltip() const
{
return basicWrapperFunctionImplementation<IPluginToolWrapper, QString>(this, "tooltip");
return basicWrapperFunctionImplementation<QString>(this, "tooltip");
}
QIcon IPluginToolWrapper::icon() const
{
return basicWrapperFunctionImplementation<IPluginToolWrapper, QIcon>(this, "icon");
return basicWrapperFunctionImplementation<QIcon>(this, "icon");
}
void IPluginToolWrapper::setParentWidget(QWidget *parent)
{
basicWrapperFunctionImplementation<IPluginToolWrapper, void>(this, "setParentWidget", parent);
basicWrapperFunctionImplementationWithDefault<void>(this, &IPluginToolWrapper::setParentWidget_Default, "setParentWidget", parent);
}
void IPluginToolWrapper::display() const
{
basicWrapperFunctionImplementation<IPluginToolWrapper, void>(this, "display");
basicWrapperFunctionImplementation<void>(this, "display");
}
/// end IPluginTool Wrapper
+22 -8
View File
@@ -51,14 +51,14 @@ public:
static constexpr const char* className = "IPluginDiagnoseWrapper";
using boost::python::wrapper<MOBase::IPluginDiagnose>::get_override;
// Bring in public scope:
using IPluginDiagnose::invalidate;
virtual std::vector<unsigned int> activeProblems() const override;
virtual QString shortDescription(unsigned int key) const override;
virtual QString fullDescription(unsigned int key) const override;
virtual bool hasGuidedFix(unsigned int key) const override;
virtual void startGuidedFix(unsigned int key) const override;
// Other functions exist, but shouldn't need wrapping as a default implementation exists
// This was protected, but Python doesn't have that, so it needs making public
virtual void invalidate();
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
};
@@ -186,12 +186,19 @@ public:
static constexpr const char* className = "IPluginModPageWrapper";
using boost::python::wrapper<MOBase::IPluginModPage>::get_override;
// Bring in public scope:
using IPluginModPage::parentWidget;
virtual QString displayName() const override;
virtual QIcon icon() const override;
virtual QUrl pageURL() const override;
virtual bool useIntegratedBrowser() const override;
virtual bool handlesDownload(const QUrl &pageURL, const QUrl &downloadURL, MOBase::ModRepositoryFileInfo &fileInfo) const override;
virtual void setParentWidget(QWidget *widget) override;
void setParentWidget_Default(QWidget* parent) {
IPluginModPage::setParentWidget(parent);
}
};
@@ -220,13 +227,20 @@ public:
static constexpr const char* className = "IPluginToolWrapper";
using boost::python::wrapper<MOBase::IPluginTool>::get_override;
virtual QString displayName() const;
virtual QString tooltip() const;
virtual QIcon icon() const;
virtual void setParentWidget(QWidget *parent);
// Bring in public scope:
using IPluginTool::parentWidget;
virtual QString displayName() const override;
virtual QString tooltip() const override;
virtual QIcon icon() const override;
virtual void setParentWidget(QWidget *parent) override;
void setParentWidget_Default(QWidget* parent) {
IPluginTool::setParentWidget(parent);
}
public Q_SLOTS:
virtual void display() const;
virtual void display() const override;
};
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -70,11 +70,12 @@ namespace utils {
bpy::list pyList;
try {
for (auto& item : container)
for (auto& item : container) {
pyList.append(item);
}
}
catch (const bpy::error_already_set&) {
reportPythonError();
throw pyexcept::PythonError();
}
return bpy::incref(pyList.ptr());
@@ -115,7 +116,7 @@ namespace utils {
pyList.append(item);
}
catch (const bpy::error_already_set&) {
reportPythonError();
throw pyexcept::PythonError();
}
return bpy::incref(pyList.ptr());
+103 -19
View File
@@ -1,32 +1,116 @@
#ifndef PYTHONWRAPPERUTILITIES_H
#define PYTHONWRAPPERUTILITIES_H
#include <functional>
#include <boost/python.hpp>
#include <log.h>
#include <utility.h>
#include "error.h"
#include "gilock.h"
class MissingImplementation : public MOBase::MyException {
public:
MissingImplementation(QString className, QString methodName) : MyException("Python class implementing \"" +
className +
"\" has no implementation of method \"" +
methodName + "\"") {}
};
namespace details {
#define PYCATCH catch (const boost::python::error_already_set &) { reportPythonError(); throw MOBase::MyException("unhandled exception"); }\
catch (const MissingImplementation &missingImplementationException) { throw missingImplementationException; }\
catch (...) { throw MOBase::MyException("An unknown exception was thrown in python code"); }
template <typename WrapperType, typename ReturnType, typename... Args>
ReturnType basicWrapperFunctionImplementation(const WrapperType *wrapper, const char *methodName, Args... args)
{
try {
/**
* @brief Common stuffs for all basicWrapperFunction methods.
*/
template <class ReturnType, class WrapperTypePtr, class Fn, class... Args>
ReturnType wrapperFunctionImplementation(WrapperTypePtr wrapper, Fn fn, boost::python::object* objPtr, const char *methodName, Args... args) {
GILock lock;
boost::python::override implementation = wrapper->get_override(methodName);
if (!implementation)
throw MissingImplementation(wrapper->className, methodName);
return implementation(args...).as<ReturnType>();
} PYCATCH;
if (!implementation) {
if constexpr (std::is_same_v<Fn, std::nullptr_t>) {
throw pyexcept::MissingImplementation(wrapper->className, methodName);
}
else {
return std::invoke(fn, wrapper, args...);
}
}
try {
boost::python::object result = implementation(args...);
if (objPtr) {
*objPtr = result;
}
if constexpr (!std::is_same_v<ReturnType, void>) {
return boost::python::extract<ReturnType>(result)();
}
}
catch (const boost::python::error_already_set&) {
throw pyexcept::PythonError();
}
catch (...) {
throw pyexcept::UnknownException();
}
}
}
/**
* @brief Call the given method on the wrapper with the given arguments, with proper
* exception handling.
*
* @param wrapper The wrapper object to use to retrieve the python method. Must have a publicly
* available `className` attribute.
* @param methodName The name of the method.
* @param args... Arguments for the method.
*
* @return the result of calling the given Python method on the wrapper.
*
* @throw pyexcept::MissingImplementation if the method does not exist.
* @throw pyexcept::PythonError if an error occurs while executing the python method.
* @throw pyexecpt::UnknownException if an unknown error occurs.
*/
template <class ReturnType, class WrapperType, class... Args>
ReturnType basicWrapperFunctionImplementation(const WrapperType *wrapper, const char *methodName, Args... args)
{
return details::wrapperFunctionImplementation<ReturnType>(wrapper, nullptr, nullptr, methodName, args...);
}
/**
* @brief Call the given method on the wrapper with the given arguments, with proper
* exception handling, and store the intermediate result in the given python object.
*
* @param wrapper The wrapper object to use to retrieve the python method. Must have a publicly
* available `className` attribute.
* @param ref Python object to which the result of `get_override()` should be stored.
* @param methodName The name of the method.
* @param args... Arguments for the method.
*
* @return the result of calling the given Python method on the wrapper.
*
* @throw pyexcept::MissingImplementation if the method does not exist.
* @throw pyexcept::PythonError if an error occurs while executing the python method.
* @throw pyexecpt::UnknownException if an unknown error occurs.
*/
template <class ReturnType, class WrapperType, class... Args>
ReturnType basicWrapperFunctionImplementation(const WrapperType* wrapper, boost::python::object &ref, const char* methodName, Args... args)
{
return details::wrapperFunctionImplementation<ReturnType>(wrapper, nullptr, &ref, methodName, args...);
}
/**
* @brief Call the given method on the wrapper with the given arguments, with proper
* exception handling, falling back to the given function if the method does not exist.
*
* @param wrapper The wrapper object to use to retrieve the python method. Must have a publicly
* available `className` attribute.
* @param fn The function to call if the method does not exists.
* @param methodName The name of the method.
* @param args... Arguments for the method.
*
* Note: `fn` does not have to be a member-function of `wrapper` but `std::invoke(fn, wrapper, args...)` must be valid.
*
* @return the result of calling the given Python method on the wrapper.
*
* @throw pyexcept::PythonError if an error occurs while executing the python method.
* @throw pyexecpt::UnknownException if an unknown error occurs.
*/
template <class ReturnType, class WrapperTypePtr, class Fn, class... Args>
ReturnType basicWrapperFunctionImplementationWithDefault(WrapperTypePtr wrapper, Fn fn, const char* methodName, Args... args)
{
return details::wrapperFunctionImplementation<ReturnType>(wrapper, fn, nullptr, methodName, args...);
}
#endif // PYTHONWRAPPERUTILITIES_H
File diff suppressed because it is too large Load Diff