|
12 | 12 |
|
13 | 13 | #include "pybind11_qt/pybind11_qt.h"
|
14 | 14 |
|
15 |
| -#include "pybind11_utils/arg_wrapper.h" |
16 | 15 | #include "pybind11_utils/functional.h"
|
17 | 16 | #include "pybind11_utils/shared_cpp_owner.h"
|
| 17 | +#include "pybind11_utils/smart_variant_wrapper.h" |
18 | 18 |
|
19 | 19 | #include <isavegame.h>
|
20 | 20 | #include <pluginrequirements.h>
|
21 | 21 |
|
22 | 22 | namespace mo2::python {
|
23 | 23 |
|
24 |
| - // struct to wrap "path" object between C++ and Python, allowing to mix |
25 |
| - // pathlib.Path, QString, QFileInfo and QDir when possible |
26 |
| - // |
27 |
| - class BasePathWrapper { |
28 |
| - protected: |
29 |
| - // we store a std::filesystem::path because it can be converted to most thing, |
30 |
| - // even though we lose basic functionality on QDir (name filter, etc.) |
31 |
| - std::filesystem::path path_; |
| 24 | + namespace detail { |
| 25 | + |
| 26 | + template <> |
| 27 | + struct smart_variant_converter<QString> { |
32 | 28 |
|
33 |
| - public: |
34 |
| - BasePathWrapper() = default; |
35 |
| - BasePathWrapper(BasePathWrapper const&) = default; |
36 |
| - BasePathWrapper& operator=(BasePathWrapper const&) = default; |
37 |
| - BasePathWrapper(BasePathWrapper&&) = default; |
38 |
| - BasePathWrapper& operator=(BasePathWrapper&&) = default; |
| 29 | + static QString from(std::filesystem::path const& path) |
| 30 | + { |
| 31 | + return QString::fromStdWString(path.native()); |
| 32 | + } |
39 | 33 |
|
40 |
| - BasePathWrapper(std::filesystem::path const& path) : path_{path} {} |
41 |
| - BasePathWrapper(QString const& path) : path_{path.toStdWString()} {} |
| 34 | + static QString from(QFileInfo const& fileInfo) |
| 35 | + { |
| 36 | + return fileInfo.filePath(); |
| 37 | + } |
42 | 38 |
|
43 |
| - operator QString() const { return QString::fromStdWString(path_.native()); } |
44 |
| - operator std::filesystem::path() const { return path_; } |
45 |
| - }; |
| 39 | + static QString from(QDir const& dir) { return dir.path(); } |
| 40 | + }; |
46 | 41 |
|
47 |
| - class FileWrapper : public BasePathWrapper { |
48 |
| - public: |
49 |
| - using BasePathWrapper::BasePathWrapper; |
| 42 | + template <> |
| 43 | + struct smart_variant_converter<std::filesystem::path> { |
50 | 44 |
|
51 |
| - FileWrapper(QFileInfo const& fileInfo) |
52 |
| - : BasePathWrapper(fileInfo.filesystemFilePath()) |
53 |
| - { |
54 |
| - } |
| 45 | + static std::filesystem::path from(QString const& value) |
| 46 | + { |
| 47 | + return {value.toStdWString()}; |
| 48 | + } |
55 | 49 |
|
56 |
| - operator QFileInfo() const { return QFileInfo(path_); } |
57 |
| - }; |
| 50 | + static std::filesystem::path from(QFileInfo const& fileInfo) |
| 51 | + { |
| 52 | + return fileInfo.filesystemFilePath(); |
| 53 | + } |
58 | 54 |
|
59 |
| - class DirectoryWrapper : public BasePathWrapper { |
60 |
| - public: |
61 |
| - using BasePathWrapper::BasePathWrapper; |
| 55 | + static std::filesystem::path from(QDir const& dir) |
| 56 | + { |
| 57 | + return dir.filesystemPath(); |
| 58 | + } |
| 59 | + }; |
62 | 60 |
|
63 |
| - DirectoryWrapper(QDir const& dir) : BasePathWrapper(dir.filesystemPath()) {} |
| 61 | + // we do not need specialization for QFileInfo and QDir because both of them can |
| 62 | + // be constructed from std::filesystem::path and QString already |
64 | 63 |
|
65 |
| - operator QDir() const { return QDir(path_); } |
66 |
| - }; |
| 64 | + } // namespace detail |
67 | 65 |
|
| 66 | + using FileWrapper = smart_variant<QString, std::filesystem::path, QFileInfo>; |
| 67 | + using DirectoryWrapper = smart_variant<QString, std::filesystem::path, QDir>; |
| 68 | + |
| 69 | + // wrap the given function to accept FileWrapper (str | PathLike | QFileInfo) at the |
| 70 | + // given argument positions (or any valid positions if Is... is empty) |
| 71 | + // |
68 | 72 | template <std::size_t... Is, class Fn>
|
69 | 73 | auto wrap_for_filepath(Fn&& fn)
|
70 | 74 | {
|
71 | 75 | return mo2::python::wrap_arguments<FileWrapper, Is...>(std::forward<Fn>(fn));
|
72 | 76 | }
|
73 | 77 |
|
| 78 | + // wrap the given function to accept DirectoryWrapper (str | PathLike | QDir) |
| 79 | + // at the given argument positions (or any valid positions if Is... is empty) |
| 80 | + // |
74 | 81 | template <std::size_t... Is, class Fn>
|
75 | 82 | auto wrap_for_directory(Fn&& fn)
|
76 | 83 | {
|
77 | 84 | return mo2::python::wrap_arguments<DirectoryWrapper, Is...>(
|
78 | 85 | std::forward<Fn>(fn));
|
79 | 86 | }
|
80 | 87 |
|
81 |
| -} // namespace mo2::python |
| 88 | + // wrap a function-like object to return a FileWrapper instead of its return type, |
| 89 | + // useful to generate proper typing in Python |
| 90 | + // |
| 91 | + // note that QFileInfo has a __fspath__ in Python, so it is quite easy to convert |
| 92 | + // from "FileWrapper", a.k.a., str | os.PathLike | QFileInfo to Path by simply |
| 93 | + // calling Path() on the return type if necessary |
| 94 | + // |
| 95 | + // this should be combined with custom return-value in PYBIND11_OVERRIDE(_PURE), see |
| 96 | + // ISaveGame binding for an example |
| 97 | + // |
| 98 | + template <class Fn> |
| 99 | + auto wrap_return_for_filepath(Fn&& fn) |
| 100 | + { |
| 101 | + return mo2::python::wrap_return<FileWrapper>(std::forward<Fn>(fn)); |
| 102 | + } |
82 | 103 |
|
83 |
| -MO2_PYBIND11_WRAP_ARGUMENT_CASTER(mo2::python::FileWrapper, "FileWrapper", QFileInfo, |
84 |
| - std::filesystem::path, QString); |
85 |
| -MO2_PYBIND11_WRAP_ARGUMENT_CASTER(mo2::python::DirectoryWrapper, "DirectoryWrapper", |
86 |
| - QDir, std::filesystem::path, QString); |
| 104 | + // similar to wrap_return_for_filepath, except it returns a DirectoryWrapper instead |
| 105 | + // of its return type |
| 106 | + // |
| 107 | + // this is much less practical than wrap_return_for_filepath since QDir does not |
| 108 | + // expose __fspath__, so more complex things need to be done in Python, which is why |
| 109 | + // this should be used carefully (e.g., should not be used if the return type is |
| 110 | + // already QDir) |
| 111 | + // |
| 112 | + template <class Fn> |
| 113 | + auto wrap_return_for_directory(Fn&& fn) |
| 114 | + { |
| 115 | + return mo2::python::wrap_return<DirectoryWrapper>(std::forward<Fn>(fn)); |
| 116 | + } |
| 117 | + |
| 118 | + // convert a QList to QStringList - QString must be constructible from QString |
| 119 | + // |
| 120 | + template <class T> |
| 121 | + QStringList toQStringList(QList<T> const& list) |
| 122 | + { |
| 123 | + static_assert(std::is_constructible_v<QString, T>, |
| 124 | + "QString must be constructible from T."); |
| 125 | + return {list.begin(), list.end()}; |
| 126 | + } |
| 127 | + |
| 128 | + // convert a QStringList to a QList - T must be constructible from QString |
| 129 | + // |
| 130 | + template <class T> |
| 131 | + QList<T> toQList(QStringList const& list) |
| 132 | + { |
| 133 | + static_assert(std::is_constructible_v<T, QString>, |
| 134 | + "T must be constructible from QString."); |
| 135 | + return {list.begin(), list.end()}; |
| 136 | + } |
| 137 | + |
| 138 | +} // namespace mo2::python |
87 | 139 |
|
88 | 140 | MO2_PYBIND11_SHARED_CPP_HOLDER(MOBase::IPluginRequirement)
|
89 | 141 | MO2_PYBIND11_SHARED_CPP_HOLDER(MOBase::ISaveGame)
|
|
0 commit comments