Type Driven User Interfaces
with Qt and C++
Qt World Summit 2019 Berlin
Distributed System
- complex!
- (note: simplified for slides!)
How can we design this?
- data flow is clear
- ensure data compatibility
- communicate with developers
Strong Types
struct Point {
double x;
double y;
double z;
};
struct Point {
double x;
double y;
double z;
double weight;
};
struct Point {
double x;
double y;
double z;
double weight;
double texX;
double texY;
};
struct X { double v{}; };
struct Y { double v{}; };
struct Z { double v{}; };
struct Point {
X x;
Y y;
Z z;
};
struct X { double v{}; };
struct Y { double v{}; };
struct Z { double v{}; };
using Point = std::tuple<X, Y, Z>;
template<class V, class /*Tag*/ >
struct Strong {
V v{};
};
using X = Strong<double, struct XTag>;
using Y = Strong<double, struct YTag>;
using Z = Strong<double, struct ZTag>;
struct Point {
X x;
Y y;
Z z;
};
using X = Strong<double, struct XTag>;
using Y = Strong<double, struct YTag>;
using Z = Strong<double, struct ZTag>;
using Point = std::tuple<X, Y, Z>;
using X = Strong<double, struct XTag>;
using Y = Strong<double, struct YTag>;
using Z = Strong<double, struct ZTag>;
using Point = AllOf<X, Y, Z>;
Data Schema
Existing Data Schemas
- XML schema
- JSON schema
- data definition language (DDL)
using Point = AllOf<X, Y, Z>;
using Points = SequenceOf<Point>;
using Geometry = AllOf<Points, Faces, Shaders>;
using Object = OneOf<Geometry, Light, Camera>;
using Scene = Hierarchy<ObjectId, Object>;
using Document = AllOf<Name, Scene>;
using Documents = EntitySet<DocId, Document>;
// recursive schema primitives:
template<class...> struct AllOf {};
template<class...> struct OneOf {};
template<class> struct SequenceOf {};
template<class Id, class>
struct EntitySet {};
template<class Id, class>
struct Hierarchy {};
Type driven Code generation
AllOf<...>
-> std::tuple<...>;
template<class... Ts>
AllOf<Ts...>
-> std::tuple<...>;
template<class... Ts>
StorageFor(AllOf<Ts...>)
-> std::tuple<...>;
template<class... Ts>
StorageFor(AllOf<Ts...>)
-> std::tuple<StorageFor<Ts>...>;
template<class T>
using StorageFor =
decltype(storageFor(adl, ptr<T>));
template<class... Ts>
auto storageFor(ADL, AllOf<Ts...> *)
-> std::tuple<StorageFor<Ts>...>;
ViewModel
template<class T>
using ViewModelFor = decltype(
viewModelFor(adl, ptr<T>));
template<class... Ts>
auto viewModelFor(ADL, AllOf<Ts...> *)
-> AllOfView<Ts...>;
template<class... Ts>
class AllOfView : public QObject {
};
template<class... Ts>
class AllOfView : public QObject {
W_OBJECT(AllOfView)
};
W_OBJECT_IMPL(AllOfView<Ts...>,
template<class... Ts>)
template<class... Ts>
class AllOfView : public QObject {
template<size_t I,
class = std::enable_if_t<(I < sizeof...(Ts))>>
struct RegisterProperties {
constexpr static auto property =
w_cpp::makeProperty<QVariant>(
property_name<I>, qvariant_name)
.setGetter(&AllOfView::getPropertyValue<I>)
.setSetter(&AllOfView::setPropertyValue<I>)
.setNotify(&AllOfView::propertyChanged<I>);
};
W_CPP_PROPERTY(RegisterProperties)
};
Recap
- distributed system
- strong types
- data schemas
- generate code
- build QObjects
++ Advantages ++
- central schema definition
- tailored to domain
- separation of data and logic
- testability
-- Disadvantages --
- uncommon + learning curve
- C++ requires some boilerplate
- long type names (workaround)
- Andreas Reischuck
- @arBmind
Work with us…
Give a Talk
⇒ get a Dresden tour
Rebuild language project
Collaborate
Try out Type Driven Development!
Thank you!
co_await question_ready()
struct PersonId
: Strong<int, struct PersonIdTag>
{
using Strong::Strong;
};
constexpr auto makeOpaque(
Strong<int, struct PersonIdTag>)
-> PersonId;
#define STRONG_OPAQUE(name, type, ...) \
struct name : Strong<type, name##Tag, ##__VA_ARGS__> { \
using Strong::Strong; \
}; \
constexpr auto makeOpaqueType(Strong< \
type, name##Tag, ##__VA_ARGS__>) -> name \
STRONG_OPAQUE(PersonId, int);