woboq / verdigris

Qt without moc: set of macros to use Qt without needing moc
https://woboq.com/blog/verdigris-qt-without-moc.html
GNU Lesser General Public License v3.0
643 stars 60 forks source link

ConstExpr C++ API #69

Closed arBmind closed 5 years ago

arBmind commented 5 years ago

Implements issue #50

I created a third header. So you only use it if needed.

The following features are supported:

It might be extended if it's useful.

Limitations:

arBmind commented 5 years ago

@ogoffart Thank you for your review. It seems I have not done a very good job of transporting my reasoning.

If a user has only a single property, he should be able to use the established macro based API. The motivation for the C++ constexpr based API is to allow us to add multiple properties without too much boilerplate.

Enabling the user to add multiple properties, signals with one invocation is the whole reason this API exists.

ogoffart commented 5 years ago

I still don't see the problem with a tuple as it is easy to build and desconstruct from constexpr code.

It would probably help me to see the concreate usecase you have for this to help me understanding.

arBmind commented 5 years ago

Ok, I will try to make my use case a bit more obvious.

We use the Type Driven Approach to write a distributed software (see presentation https://github.com/arBmind/2019-types-de) and want to generate QML enabled models.

TLDR: We have a strong type for all properties and want to make them accessible via QObject properties.

For each property passed to TupleObject we store the Output type and constexpr value name.

The code looks basically like this.

template<class... Ts>
class TupleObject : public QObject {
    W_OBJECT(TupleObject)

public:
    using PropertyPack = TypePack<typename Ts::Output ...>; // + some extra Properties

    template<size_t I>
    constexpr static auto propertyName() {
        using T = UnwrapType< TypeAt< Index<I>, TypePack<Ts...> >>;
        return T::name;
    }

    template<class T>
    auto propertyValue() const;

    template<size_t I>
    void notifyPropertyChanged() W_CPP_SIGNAL_IMPL(decltype(&TupleObject::notifyPropertyChanged<I>), Signals, I, 0)

    template<size_t I, class = std::enable_if_t<(I < meta17::count_types<PropertyPack>)>>
    struct Signals {
        constexpr static auto signalName = meta_verdigris::stringAddNull(
        string17::concat(TupleObject::propertyName<I>(), string17::storeLiteral("Changed")));
        constexpr static auto signal =
            w_cpp::makeSignalBuilder(toVerdigrisString(signalName), &TupleObject::notifyPropertyChanged<I>).build();
    };
    W_CPP_SIGNAL(Signals)

    template<size_t I, class = std::enable_if_t<(I < meta17::count_types<PropertyPack>)>>
    struct Properties {
        using T = UnwrapType< TypeAt< Index<I>, PropertyPack >>;
        using PropertyType = decltype(std::declval<TupleObject>().template propertyValue<T>());

        constexpr static auto propertyName = meta_verdigris::stringAddNull(TupleObject::propertyName<I>());

        constexpr static auto property = [] {
            return w_cpp::makeProperty<PropertyType>(
                       toVerdigrisString(propertyName), w_cpp::viewLiteral("Custom"))
                .setGetter(&TupleObject::propertyValue<T>)
                .setNotify(&TupleObject::notifyPropertyChanged<I>);
        }();
    };
    W_CPP_PROPERTY(Properties)
};

If we would accept the Properties with a tuple-like interface. The user code will have to run a loop through all the properties. The verdigris side will have to implement a way to store each value as w_state. So both sides would have to do more work. And more work also compiles slower.

Also keep in mind that std::tuple is not constexpr enabled. We would have to implement a constexpr enabled version just for this use case.

It's doable no question, but from my experience it wont be any better. Just easier to document, but harder to use and implement.

arBmind commented 5 years ago

Ok, I added a tutorial. For now it's only compiled with Qbs. I forgot how to write QML applications with QMake.