- BindGeneric : **QTransmogrifier** template specialization for T
- Every `bind<T>()` starts from a QValue which is an un *unsafe* QValueStatus copy wrt well-formedness (these `unsafeItem()` copies are protected from incorrect use)
- Every `bind<T>()` starts from a QValue which is an un *unsafe* QCur copy wrt well-formedness (these `unsafeItem()` copies are protected from incorrect use)
## C++ types extensibility
QTransmogrifier is a functor templated on T type receiving a Value and T reference (either lvalue or rvalue reference) and returning the QValueStatus.
QTransmogrifier is a functor templated on T type receiving a Value and T reference (either lvalue or rvalue reference) and returning the QCur.
Template specializations can be defined for any T and optionally refined for specific Cur<TImpl> with different sets of BindNative types.
A default QTransmogrifier specialization attempts to call `T::bind(...)` to conveniently bind `T* this` without having to understand template syntax,
...
...
@@ -96,28 +96,28 @@ editor will propose to either `bind(myData.item)`, or to construct a `sequence()
## Well-formedness guarantees
Thanks to this design, the compiler will make sure that the only possibility to return a QValueStatus from a `QValue` is to traverse
Thanks to this design, the compiler will make sure that the only possibility to return a QCur from a `QValue` is to traverse
the data without backtracking, calling only and all necessary QAbstractValue virtual methods.
The addition of default and optional values take into account most data schema evolutions in a purely declarative fluent interface without
having to test schema versions and the like. The benefit is that it is not possible to introduce bugs using just the fluent interface.
The downside is that writing loops with the fluent interface is unnatural as one must never forget to follow the valid QValueStatus.
The downside is that writing loops with the fluent interface is unnatural as one must never forget to follow the valid QCur.
For instance:
```cpp
autoseq(v.sequence());
for(auto&&t:ts){
seq=seq.bind(t);// do not forget to reassign seq, or subsequent items will be `bind` to the moved-from QValueStatus and automatically ignored
seq=seq.bind(t);// do not forget to reassign seq, or subsequent items will be `bind` to the moved-from QCur and automatically ignored
}
```
## Write performance
Since `QValueStatus`, `QVal`, `QRec` and `QSeq` have no data member other than outer types and `QAbstractValue*`, calling their methods can be
Since `QCur`, `QVal`, `QRec` and `QSeq` have no data member other than outer types and `QAbstractValue*`, calling their methods can be
optimized and replaced with just the following operations:
1. test the QAbstractValue pointer validity [^1]
2. call the QAbstractValue virtual method corresponding to the possible transitions
3. return the resulting QValueStatus, QVal, QRec or QSeq with a valid or invalid QValueStatus depending on QAbstractValue method success or failure
3. return the resulting QCur, QVal, QRec or QSeq with a valid or invalid QCur depending on QAbstractValue method success or failure
[^1]:Experiments to use constexpr to bypass this step for writers that always return true did not seem to improve performance.
std::vector<bool>levels;//!< minimal dynamic context to ensure well-formedness in case QValueStatus is abandoned: true indicates Definite levels with prefixed size
std::vector<bool>levels;//!< minimal dynamic context to ensure well-formedness in case QCur is abandoned: true indicates Definite levels with prefixed size
caching=&cacheReader;// let outer QValueStatus drive QJsonReader depending on bound T
caching=&cacheReader;// let outer QCur drive QJsonReader depending on bound T
returntrue;
}
elseif(!tryItem(s)){// record() end reached
...
...
@@ -352,10 +352,10 @@ protected:
elseif(u==s){
returntrue;
}
else{// read cachedValue using a dedicated QValueStatus and QTransmogrifier<QValue> accepting value of any 'shape' (outer QTransmogrifier is specialized on outer T which is not usually generic enough)
else{// read cachedValue using a dedicated QCur and QTransmogrifier<QValue> accepting value of any 'shape' (outer QTransmogrifier is specialized on outer T which is not usually generic enough)