kdab / kdbindings Goto Github PK
View Code? Open in Web Editor NEWReactive programming & data binding in C++
Reactive programming & data binding in C++
Often, I have to deal with a Property<T*> p where T itself contains properties. Given the property p is using a pointer type, I cannot binding directly to p->someProperty given p might be null. Therefore I would need to have a way to create a binding to a function that can itself return binding to another property or a value depending on whether p() is null.
// GIVEN
struct TypeA {
Property<float> value { 1.0f };
};
struct TypeB {
Property<TypeA *> a { nullptr };
};
// WHEN
TypeB myB;
// In a perfect world I would do Property<float> v = makeBinding(myB.a->value);
// Except I can't if myB.a is null
// THEN
CHECk(myB.a() == nullptr);
// WHEN
Property<float> v = makeBinding([] (TypeA *a) {
if (a)
return makeBinding(a->value);
return 0.0f; // or a binding to a default property
}, myB.a);
// THEN -> Returns default value
CHECK(v() == 0.0f);
// WHEN
TypeA myA;
myB.a = &myA;
// THEN -> Return bound value
CHECK(v() == 1.0f);
Even if clazy generates warnings in CI the runner isn't considered to have failed.
How do we fix this?
As of #77 , we need to test on windows-2019, in order for tests to pass, this seems to stem from an issue with GH Actions, and not our code.
According to actions/runner-images#10019 , there were issues with the new runner images that caused this.
Once the images are updated again, this should hopefully be resolved and we can switch back to -latest
Hi,
As a developer it would be nice to be able to connect the signals to Javascript functions in Qt.
Can we achieve following in KDBindings?
Basically wanted to check if KDBindings support multithreaded property binding evaluation?
When T is an aggregate type, eg struct Rect { int x; int y; int width; int height; }
It would be great to be able to access members of Property, i.e that:
Property<Rect> r;
auto widthProp = makeBoundProperty(r.width);
This slightly overlaps with what @lemirep is asking about in #28, except in my case the aggregate does not contain Property<>
members. The desired behaviour can be done using a function argument to makeBoundProperty
: the question is whether some overload of operator-dot (and ->
, presumably) could synthesise the access.
The move constructor of Property is marked noexcept
, but - as long as it emmits public moved
signal - it can throw from any slot. Simple example:
TEST_CASE("Nothrow move")
{
try {
std::set_terminate([] {
std::cerr << "TERMINATED" << std::endl;
});
Property<int> p;
p.moved().connect([&] { throw 0; });
Property<int> p2 = std::move(p);
} catch (int) {
}
}
The program will call std::terminate
as it throws from noexcept
function.
Forgetting to disconnect a connection can lead to serious issues.
So we should allow users to at least get a warning in that case.
However, we likely want to put this behind a flag of some sort to not break existing code.
When a property is assigned a binding, trying to assign a value or another binding to it will result in an exception.
Having a small getter e.g isBinding()
would be a nice convenience to allow users to check whether reassigning to a property is safe or not without having to try/catch.
Hi!
I wanted to use KSBindings in a project where I also use Qt. At a certain point I needed to include kdbindings/signal.h as well as QObject. This then results in a compile error (on gcc as well as clang). As I investigated further it turned out that the order of includes matter. If KDBindings is included first, it works. If QObject is first then it doesn’t.
I was surprised and wanted to ask if this is a known limitation.
The compile error looks like the following:
[ 9%] Building CXX object CMakeFiles/workshop_episode_001.dir/src/Translator.cpp.o
In file included from src/Translator.cpp:3:
In file included from include/IResourceManager.hpp:3:
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:285:23: error: expected ')'
void emit(Args... p) const
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:285:18: note: to match this '('
void emit(Args... p) const
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:285:19: error: declaration of 'Args' shadows template parameter
void emit(Args... p) const
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:213:22: note: template parameter is declared here
template<typename... Args>
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:285:19: error: field has incomplete type 'void'
void emit(Args... p) const
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:285:29: error: expected ';' at end of declaration list
void emit(Args... p) const
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:308:49: error: use of undeclared identifier 'Connection'
mutable Private::GenerationalIndexArray<Connection> m_connections;
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:488:19: error: expected ')'
void emit(Args... p) const
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:488:14: note: to match this '('
void emit(Args... p) const
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:488:15: error: declaration of 'Args' shadows template parameter
void emit(Args... p) const
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:213:22: note: template parameter is declared here
template<typename... Args>
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:488:15: error: field has incomplete type 'void'
void emit(Args... p) const
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:488:25: error: expected ';' at end of declaration list
void emit(Args... p) const
^
conan_cache/.conan/data/kdbindings/1.0.1/conan_toolchain_catalog/build/package/e7a4df623f2f69d6974a12334b94709d4f79466f/include/kdbindings/signal.h:259:13: error: unknown type name 'Connection'
Connection *connection = m_connections.get(id);
^
10 errors generated.
make[3]: *** [CMakeFiles/workshop_episode_001.dir/build.make:161: CMakeFiles/workshop_episode_001.dir/src/Translator.cpp.o] Error 1
make[2]: *** [CMakeFiles/Makefile2:1247: CMakeFiles/workshop_episode_001.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:1254: CMakeFiles/workshop_episode_001.dir/rule] Error 2
make: *** [Makefile:712: workshop_episode_001] Error 2
Currently, there is no way for a framework like KDUtils to wake up the associated thread when something is queued on the ConnectionEvaluator.
This is problematic, as it may mean the thread may remain dormant, even if there is usueful work to be done.
So we want to give users of ConnectionEvaluator a way to react whenever something new was queued onto the evaluator.
Currently we have thought of two ways to do this:
Subclassing seems to be the more flexible option, as it allows the ConnectionEvaluator to add condition variables or other data that may be required.
As is commonly used in the js world or for e.g. https://github.com/KDAB/KDToolBox/tree/master/qt/KDSignalThrottler
Not sure which type of syntax would be most useful.
Maybe:
mySignal.debounce(100).connect(&MyClass::doSomething, myObj);
?
Use Thread 1 with normal signal evaluation to mark a binding as dirty.
Then have all the dependent properties associated with a BindingEvaluator.
This binding evaluator would then be evaluated by Thread 2 (typically GUI Thread), so the evaluation actually happens on a separate thread.
For this to work the following would need to be thread-safe:
It may be unexpected for users that a signal emission doesn't reach its slot when an emission is still queued up on disconnect.
Therefore, document that this is happening by adding some kind of logging.
Assuming #41 is merged with this behavior.
A single shot connection could possibly be implemented by creating a binding that captures its own ConnectionHandle and disconnects it when the signal is emitted.
Open question: How to capture the ConnectionHandle in the slot itself?
At the moment I cannot use a const Property in a binding.
const Property<float> someValue = makeBinding(someExpression);
Property<float> boundValue = makeBinding(someValue); // This does not compile
A const Property means it cannot be assigned to again but its value still might change (if the value of the const property is a binding). Therefore, being able to use a const Property in a binding would make sense.
Having the ability to control when a connection is emitted could be advantageous, especially in a multi-threaded context, where a worker thread might emit a signal, but the slot should be called in an event loop on a GUI thread.
To control when exactly a connection is evaluated, implement a ConnectionEvaluator, similar to the BindingEvaluator, that can control when exactly a slot is called.
Add a function connectDeferred
(or similar) that takes a ConnectionEvaluator as the first argument, then takes the same arguments as the normal connect function.
This slot would then queue the actual function to be called in the ConnectionEvaluator. The queued functions in the ConnectionEvaluator could then be called at any time, similar to how evaluateAll works on the BindingEvaluator.
The difference here would be that connections might actually be emitted multiple times between calls to the evaluator, so these calls would then also need to be emitted multiple times.
It would be nice to be able to do:
#include <KDBindings/signal.h>
on systems with case-sensitive filesystems so that it matches the other KD libraries that we have.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.