QtPromise Documentation

website·Indexed 29 days ago

https://qtpromise.netlify.app/

A thread-safe promise library for Qt that integrates with QtConcurrent and Qt Signals. Features the QPromise class with methods for asynchronous flow control including then, fail, finally, timeout, and delay, as well as helper functions like QtPromise::all, connect, and attempt.

Tokens
10.7K
Snippets
52
Records
57
Agent score
74%

What's inside QtPromise

  1. Convert the resolved value type of a QPromise

    The convert<U>() method changes the resolved value of a QPromise<T> to type U. The conversion mechanism depends on the types involved:

    1. Fundamental Types/Enums: Performs a static_cast<U>.
    2. Converting Constructors: If U has a non-explicit constructor accepting a single argument of type T, that constructor is used.
    3. QVariant Conversion: If the promise is QPromise<QVariant>, it uses the QVariant conversion algorithm. If the conversion fails, the promise is rejected with a QPromiseConversionException.
    4. General Case: Other combinations are converted via QVariant. Custom types must be registered with Q_DECLARE_METATYPE and may require a registered converter via QMetaType::registerConverter to avoid QPromiseConversionException.

    Special Cases:

    • If U is void, the resolved value is dropped.
    • Calling convert on a QPromise<void> is not supported.
    // Example 1: Fundamental type conversion (int -> double)
    QPromise<int> input = ...;
    auto output = input.convert<double>();
    output.then([](double value) { /* value is static_cast<double> */ });
    
    // Example 2: Converting constructor (QByteArray -> QString)
    QPromise<QByteArray> input = ...;
    auto output = input.convert<QString>();
    output.then([](const QString& value) { /* calls QString(QByteArray) */ });
    
    // Example 3: QVariant conversion
    QPromise<QVariant> input = ...;
    auto output = input.convert<int>();
    output.then([](int value) { /* success */ })
          .fail([](const QPromiseConversionException& e) { /* conversion failed */ });
  2. Understand QPromise thread-safety and data sharing

    QPromise is thread-safe and can be copied and accessed across different threads. It utilizes explicit data sharing, meaning that copying a promise (e.g., auto p2 = p1) creates a new instance that refers to the same underlying promise. When the promise resolves, all handlers registered on any of its copies are executed, and the fulfilled value is shared across all instances.
  3. Convert QFuture to QPromise

    To convert a QFuture<T> (such as one returned by QtConcurrent::run) into a QPromise<T>, use the QtPromise::resolve() helper function.
    QFuture<int> future = QtConcurrent::run([]() {
        // ...
        return 42;
    });
    
    QPromise<int> promise = QtPromise::resolve(future);
    
    // Or in a single line:
    auto promise = QtPromise::resolve(QtConcurrent::run([]() {
        // ...
    }));
  4. Install QtPromise via manual download

    QtPromise can be downloaded as a zip or tar.gz archive from the GitHub release page. On Linux, you can use the following commands to download and extract version 0.7.0 into a project subdirectory:
    cd <your/project/repository>
    wget -q -O qtpromise.tar.gz https://github.com/simonbrunel/qtpromise/archive/v0.7.0.tar.gz
    tar xzf qtpromise.tar.gz --strip 1 --one-top-level=3rdparty/qtpromise
    rm qtpromise.tar.gz
  5. Install QtPromise using Git submodule

    To install QtPromise version 0.7.0 as a Git submodule into a specific directory (e.g., 3rdparty/qtpromise), run the following commands:
    cd <your/project/repository>
    git submodule add https://github.com/simonbrunel/qtpromise.git 3rdparty/qtpromise
    cd 3rdparty/qtpromise
    git checkout v0.7.0
    cd ../..
    git add 3rdparty/qtpromise
    git commit -m "Add QtPromise v0.7.0"
  6. Create a promise resolved by a Qt signal

    Use the QtPromise::connect() helper to create a promise that resolves when a specific Qt signal is emitted. The promise is resolved only once; after the first signal emission, the promise is automatically disconnected from all associated signals.

    Note: Only single-argument signals are supported. If a signal has multiple arguments, only the first one is used to fulfill the promise; others are ignored.

    // Case 1: Signal with an argument [signal] Object::finished(const QByteArray&)
    auto output = QtPromise::connect(obj, &Object::finished);
    // output type: QPromise<QByteArray>
    output.then([](const QByteArray& data) {
        // Handle data
    });
    
    // Case 2: Signal without arguments [signal] Object::done()
    auto output = QtPromise::connect(obj, &Object::done);
    // output type: QPromise<void>
    output.then([]() {
        // Handle completion
    });
  7. Create a promise with both resolve and reject signals

    The QtPromise::connect() helper can be configured to reject a promise if a specific error signal is emitted before the resolution signal.

    If the rejection signal provides no arguments, the promise is rejected with a QPromiseUndefinedException. Additionally, if the sender object is destroyed before the promise is fulfilled, it is automatically rejected with a QPromiseContextException.

    // Case 1: Resolve and reject signals from the same object
    // [signal] Object::finished(const QByteArray& data)
    // [signal] Object::error(ObjectError error)
    auto output = QtPromise::connect(obj, &Object::finished, &Object::error);
    
    output.then([](const QByteArray& data) {
        // Success
    }).fail([](const ObjectError& error) {
        // Handle error
    });
    
    // Case 2: Resolve and reject signals from different objects
    // [signal] ObjectA::finished(const QByteArray& data)
    // [signal] ObjectB::error(ObjectBError error)
    auto output = QtPromise::connect(objA, &ObjectA::finished, objB, &ObjectB::error);
    
    output.then([](const QByteArray& data) {
        // Success
    }).fail([](const ObjectBError& error) {
        // Handle error from ObjectB
    });
  8. Register custom type converters for QPromise::convert

    When converting between non-Qt types or custom types via QPromise::convert, you must provide a conversion function using QMetaType::registerConverter. Without a registered converter, the promise will be rejected with a QPromiseConversionException.
    struct Foo {};
    Q_DECLARE_METATYPE(Foo);
    
    // Register converter from Foo to QString
    QMetaType::registerConverter<Foo, QString>([](const Foo& foo) {
        return QString{"Converted Foo"};
    });
    
    QPromise<Foo> input = ...;
    auto output = input.convert<QString>();
    
    output.then([](const QString& value) {
        // value contains the result of the custom converter
    })
    .fail([](const QPromiseConversionException& e) {
        // Handle conversion failure
    });
  9. Install QtPromise using Git subtree

    To install QtPromise version 0.7.0 as a Git subtree into a specific directory (e.g., 3rdparty/qtpromise), run the following commands from your project repository root:
    cd <your/project/repository>
    git remote add qtpromise https://github.com/simonbrunel/qtpromise.git
    git subtree add -P 3rdparty/qtpromise qtpromise v0.7.0 --squash -m "Add QtPromise v0.7.0"
  10. Install QtPromise using CMake FetchContent

    For CMake 3.11+, you can install QtPromise at configure time using the FetchContent module. For CMake 3.14+, use FetchContent_MakeAvailable. For versions between 3.11 and 3.14, you must manually call FetchContent_GetProperties and FetchContent_Populate.
    include(FetchContent)
    
    FetchContent_Declare(qtpromise
      GIT_REPOSITORY https://github.com/simonbrunel/qtpromise.git
      GIT_TAG v0.7.0
      GIT_SHALLOW true
    )
    
    # For CMake v3.14+
    FetchContent_MakeAvailable(qtpromise)
    
    # For CMake v3.11+ (alternative to MakeAvailable)
    FetchContent_GetProperties(qtpromise)
    if(NOT qtpromise_POPULATED)
      FetchContent_Populate(qtpromise)
      add_subdirectory(
        ${qtpromise_SOURCE_DIR}
        ${qtpromise_BINARY_DIR}
      )
    endif()
    
    target_link_libraries(<target> qtpromise)
  11. Reject a QPromise without an explicit reason

    Since version 0.5.0, it is possible to call reject() without an argument. When a promise is rejected without an explicit reason, a built-in QPromiseUndefinedException is thrown. This can be caught explicitly by specifying the exception type in the .fail() handler, or implicitly using a handler with no arguments.
    QPromise<int> promise{[](const QPromiseResolve<int>& resolve, const QPromiseReject<int>& reject) {
        async_method([=](bool success, int result) {
            if (success) {
                resolve(result);
            } else {
                reject(); // Reject without explicit reason
            }
        });
    }};
    
    // Catch the undefined exception explicitly
    promise.fail([](const QPromiseUndefinedException&) {
        // Handle undefined rejection
    });
    
    // Or catch it implicitly
    promise.fail([]() {
        // Handle undefined rejection
    });