gio-qt-async.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. #define GI_INLINE 1
  2. #include <gio/gio.hpp>
  3. #include <functional>
  4. #include <iostream>
  5. #include <memory>
  6. #include <QCoreApplication>
  7. #include <QFuture>
  8. #include <QFutureWatcher>
  9. #include <QObject>
  10. namespace GLib = gi::repository::GLib;
  11. namespace GObject_ = gi::repository::GObject;
  12. namespace Gio = gi::repository::Gio;
  13. template<typename T>
  14. using ResultExtractor = std::function<void(
  15. GObject_::Object, Gio::AsyncResult result, QFutureInterface<T> &fut)>;
  16. template<typename T>
  17. class qt_future
  18. {
  19. struct CallbackData
  20. {
  21. QFutureInterface<T> promise;
  22. QFutureWatcher<T> watcher;
  23. Gio::Cancellable cancel;
  24. ResultExtractor<T> handler;
  25. };
  26. std::shared_ptr<CallbackData> data_;
  27. public:
  28. qt_future(ResultExtractor<T> h)
  29. {
  30. data_ = std::make_shared<CallbackData>();
  31. data_->handler = h;
  32. }
  33. QFuture<T> future() { return data_->promise.future(); }
  34. QFutureInterface<T> promise() { return data_->promise; }
  35. operator Gio::AsyncReadyCallback()
  36. {
  37. auto d = data_;
  38. return [d](GObject_::Object obj, Gio::AsyncResult result) {
  39. assert(d->handler);
  40. d->handler(obj, result, d->promise);
  41. };
  42. }
  43. Gio::Cancellable cancellable()
  44. {
  45. if (!data_->cancel) {
  46. auto cancel = data_->cancel = Gio::Cancellable::new_();
  47. data_->watcher.setFuture(future());
  48. auto h = [cancel]() mutable { cancel.cancel(); };
  49. data_->watcher.connect(
  50. &data_->watcher, &decltype(data_->watcher)::finished, std::move(h));
  51. }
  52. return data_->cancel;
  53. }
  54. // why not ... ??
  55. operator Gio::Cancellable() { return cancellable(); }
  56. };
  57. #if GI_CONST_METHOD
  58. #define CONST_METHOD const
  59. #else
  60. #define CONST_METHOD
  61. #endif
  62. template<typename Result, typename Object>
  63. qt_future<Result> make_future(
  64. Result (Object::*mf)(Gio::AsyncResult, GLib::Error *) CONST_METHOD)
  65. {
  66. ResultExtractor<Result> f;
  67. f = [mf](GObject_::Object cbobj, Gio::AsyncResult result,
  68. QFutureInterface<Result> &fut) mutable {
  69. auto obj = gi::object_cast<Object>(cbobj);
  70. GLib::Error error{};
  71. auto res = (obj.*mf)(result, &error);
  72. if (error.gobj_()) {
  73. // FIXME use std::expected<Result> as a result type ??
  74. // reportException might be an option, if enabled, but leads to throw
  75. fut.reportFinished();
  76. qWarning() << "error code " << error.code_();
  77. qWarning() << "error message "
  78. << QString::fromUtf8(error.message_().c_str());
  79. } else {
  80. fut.reportFinished(&res);
  81. }
  82. };
  83. return {f};
  84. }
  85. // ====
  86. // some small future-centric API wrappers using above helper class
  87. QFuture<Gio::FileInfo>
  88. file_query_file_system_info(
  89. Gio::File f, const std::string &attributes, gint io_priority)
  90. {
  91. auto fut = make_future(&Gio::File::query_filesystem_info_finish);
  92. f.query_filesystem_info_async(attributes, io_priority, fut, fut);
  93. return fut.future();
  94. }
  95. QFuture<bool>
  96. file_copy(Gio::File src, Gio::File destination, Gio::FileCopyFlags flags,
  97. gint io_priority)
  98. {
  99. auto fut = make_future(&Gio::File::copy_finish);
  100. auto promise = fut.promise();
  101. auto p = [promise](
  102. goffset current_num_bytes, goffset total_num_bytes) mutable {
  103. promise.setProgressRange(0, total_num_bytes);
  104. promise.setProgressValue(current_num_bytes);
  105. };
  106. src.copy_async(destination, flags, io_priority, fut, p, fut);
  107. return fut.future();
  108. }
  109. // ====
  110. // save on typing elsewhere
  111. // also makes it movable in a way
  112. template<typename T>
  113. std::shared_ptr<QFutureWatcher<T>>
  114. make_watcher(QFuture<T> fut)
  115. {
  116. auto watcher = std::make_shared<QFutureWatcher<T>>();
  117. watcher->setFuture(fut);
  118. return watcher;
  119. }
  120. // type-erased owner/cleanup type
  121. using Retainer = std::shared_ptr<std::nullptr_t>;
  122. Retainer
  123. do_query(QCoreApplication &app, const std::string &fpath)
  124. {
  125. auto file = Gio::File::new_for_commandline_arg(fpath);
  126. auto fut = file_query_file_system_info(file, "*", GLib::PRIORITY_DEFAULT_);
  127. auto watcher = make_watcher(fut);
  128. auto h = [fut, &app]() {
  129. Q_ASSERT(fut.isFinished());
  130. std::cout << "query finished" << std::endl;
  131. if (fut.resultCount()) {
  132. auto finfo = fut.result();
  133. for (auto attr : {Gio::FILE_ATTRIBUTE_FILESYSTEM_TYPE_,
  134. Gio::FILE_ATTRIBUTE_FILESYSTEM_FREE_,
  135. Gio::FILE_ATTRIBUTE_FILESYSTEM_SIZE_}) {
  136. std::cout << attr << ": " << finfo.get_attribute_as_string(attr)
  137. << std::endl;
  138. }
  139. } else {
  140. std::cout << "... but no results" << std::endl;
  141. }
  142. app.quit();
  143. };
  144. watcher->connect(
  145. watcher.get(), &decltype(watcher)::element_type::finished, &app, h);
  146. return {watcher, nullptr};
  147. }
  148. Retainer
  149. do_copy(QCoreApplication &app, const std::string &src, const std::string &dest)
  150. {
  151. auto fsrc = Gio::File::new_for_commandline_arg(src);
  152. auto fdest = Gio::File::new_for_commandline_arg(dest);
  153. auto fut = file_copy(
  154. fsrc, fdest, Gio::FileCopyFlags::ALL_METADATA_, GLib::PRIORITY_DEFAULT_);
  155. auto watcher = make_watcher(fut);
  156. auto h = [fut, &app]() {
  157. Q_ASSERT(fut.isFinished());
  158. std::cout << "copy finished" << std::endl;
  159. if (fut.resultCount()) {
  160. auto ok = fut.result();
  161. if (ok) {
  162. std::cout << "copy ok" << std::endl;
  163. } else {
  164. std::cout << "copy failed" << std::endl;
  165. }
  166. } else {
  167. std::cout << "... but no results" << std::endl;
  168. }
  169. app.quit();
  170. };
  171. watcher->connect(
  172. watcher.get(), &decltype(watcher)::element_type::finished, &app, h);
  173. // also monitor progress
  174. auto progress = [fut](int) {
  175. auto max = fut.progressMaximum();
  176. auto min = fut.progressMinimum();
  177. auto current = fut.progressValue();
  178. std::cout << "copy progress " << current << " (" << min << " -> " << max
  179. << ")" << std::endl;
  180. };
  181. watcher->connect(watcher.get(),
  182. &decltype(watcher)::element_type::progressValueChanged, &app, progress);
  183. return {watcher, nullptr};
  184. }
  185. int
  186. main(int argc, char **argv)
  187. {
  188. QCoreApplication app(argc, argv);
  189. if (argc < 2)
  190. return -1;
  191. Retainer r;
  192. if (argc == 2) {
  193. r = do_query(app, argv[1]);
  194. } else if (argc == 3) {
  195. r = do_copy(app, argv[1], argv[2]);
  196. }
  197. return app.exec();
  198. }