KDBindings

repository·main·Indexed 43 days ago

https://github.com/kdab/kdbindings

A header-only C++17 library providing reactive programming features, including signals, slots, and property bindings with lazy or immediate evaluation. It allows developers to implement data binding and the observer pattern without requiring the Qt framework, though it remains compatible with it.

Tokens
7.7K
Snippets
22
Records
36
Agent score
53%

What's inside KDBindings

  1. What is KDBindings?

    main
    KDBindings is a standalone, header-only C++ library that provides reactive programming and data binding capabilities. It allows developers to implement Signals + Slots, templated Properties, and reactive Property bindings with lazy evaluation. It is designed to be lightweight and does not require a heavy Qt dependency, though it remains compatible with Qt.
  2. Use KDBindings Signals to implement the observer pattern

    main

    KDBindings Signals are templated objects that can be used anywhere in your code without requiring the Qt Meta Object Compiler (MOC). They allow you to declare events that can be observed by 'Slots' (functions, lambdas, or objects implementing operator()).

    To use a Signal:

    1. Define a KDBindings::Signal<T> where T is the type of value emitted.
    2. Connect a slot using .connect().
    3. Emit the signal using .emit() with the appropriate arguments.
    #include <kdbindings/signal.h>
    
    using namespace KDBindings;
    Signal<const std::string&> mySignal;
    
    // Connect a lambda as a slot
    mySignal.connect([](const std::string& value){
      std::cout << value << std::endl;
    });
    
    // Emit the signal
    mySignal.emit("Hello World!");
  3. Use deferred evaluation for bindings

    main

    To control the frequency of updates or avoid unnecessary re-evaluations (e.g., in UI applications to prevent 60Hz updates), you can use deferred evaluation.

    To enable this, pass a KDBindings::BindingEvaluator to makeBoundProperty. The binding will only be recalculated when evaluateAll() is called on the evaluator instance.

  4. Avoid and handle broken bindings

    main

    In KDBindings, assigning a concrete value to a property that is currently the result of a data binding is prohibited to prevent accidentally breaking the binding. This operation will throw a KDBindings::ReadOnlyProperty error.

    • To prevent errors: Do not assign values to properties created via makeBoundProperty unless you intend to replace the binding.
    • To intentionally remove a binding: Use the reset() method on the Property instance.
    #include <kdbindings/binding.h>
    
    using namespace KDBindings;
    
    Property<int> value = 2;
    Property<int> result = makeBoundProperty(2 * value);
    // result is now 4
    
    result = 5; // this will throw a KDBindings::ReadOnlyProperty error!
  5. Best practices for Signal mutability in classes

    main

    Because functions that modify a Signal (like connect) are not const, adding a Signal as a member of a class typically requires that the class itself be non-const to allow connections.

    To allow connecting to a Signal within a const context (for example, when the Signal is part of a Property), it is recommended to use the C++ mutable keyword for the Signal member. This accurately reflects that connecting to the signal does not change the logical state of the enclosing object.

  6. Link KDBindings using CMake

    main

    Once installed, you can integrate KDBindings into your CMake projects using the find_package directive. This exposes the KDAB::KDBindings target.

    Note: KDBindings requires C++17 or later. Ensure you set CMAKE_CXX_STANDARD to at least 17 in your CMakeLists.txt.

    set(CMAKE_CXX_STANDARD 17)
    
    find_package(KDBindings)
    target_link_libraries(${PROJECT_NAME} KDAB::KDBindings)
  7. Reassign a binding to an existing property

    main

    If you need to change the logic of an existing binding rather than creating a new property, you have two options:

    1. KDBindings::makeBinding: Use this to add a new binding to an existing Property. This replaces the current binding logic without throwing an exception.
    2. KDBindings::makeBoundProperty: You can move-assign a new property into an existing one. Warning: This completely replaces the existing Property instance, meaning all existing signals will be disconnected and any other bindings depending on the old property will be removed.

    Rule of thumb: Use makeBoundProperty to create a new property; use makeBinding to update the binding of an existing property.

    #include <kdbindings/binding.h>
    
    using namespace KDBindings;
    
    Property<int> value = 2;
    auto result = makeBoundProperty(2 * value);
    // result is 4
    
    result = makeBinding(3 * value); // Replaces binding logic, no exception. result is 6
    result = makeBoundProperty(4 * value); // Overrides the entire property instance. result is 8
  8. How to use KDBindings with Qt

    main

    KDBindings uses emit as a function name. Because Qt defines emit as a keyword, they are incompatible by default.

    To use both in the same project, you must define QT_NO_EMIT or QT_NO_KEYWORDS and omit the emit keyword in your Qt code.

    If you are using CMake, you can easily enable this compatibility by setting the following option:

    KDBindings_QT_NO_EMIT

    This option defines the necessary macros for all dependents of KDBindings.

  9. Perform basic data binding with makeBoundProperty

    main

    Data binding in KDBindings allows you to create properties that automatically update when their input dependencies change. You can use arithmetic operators or combinations of properties and constants to define these relationships. Use the KDBindings::makeBoundProperty function to create a new property that is bound to an expression.

    Supported operations include most arithmetic operators and combinations of properties with constant values.

    #include <kdbindings/binding.h>
    
    using namespace KDBindings;
    
    Property<int> width(2);
    Property<int> height(3);
    Property<int> area = makeBoundProperty(width * height);
    
    std::cout << area << std::endl; // will print 6
    width = 5;
    std::cout << area << std::endl; // will print 15
  10. Manage connections with ConnectionHandle

    main

    The connect() function returns a KDBindings::ConnectionHandle. Unlike Qt, KDBindings does not automatically manage the lifecycle of connections.

    Critical: You must manually disconnect a connection using the ConnectionHandle if any of the bound arguments (like the object containing the slot) are destroyed. If you do not call disconnect before the slot's owner goes out of scope, emitting the signal will result in a dangling pointer and undefined behavior.