Thread: Vapoursynth
View Single Post
Old 22nd January 2021, 11:22   #4237  |  Link
feisty2
I'm Siri
 
feisty2's Avatar
 
Join Date: Oct 2012
Location: void
Posts: 2,633
the latest version of all 3 mainstream compilers (GCC, Clang, MSVC) have supported many C++20 features, both GCC and Clang have implemented full support for concepts, and MSVC has partial support for it. I can imagine that MSVC should be able to catch up in the following months and my project should work with all mainstream compilers by the time I finish writing the documentation. CentOS is dead so not supporting it is no big deal, regardless of that, GCC is capable of bootstrapping, just download the source code of the latest version and build it with whatever version you already have, you certainly don't need to reinstall your OS to run the latest version of GCC. what's the point really to use Linux if you can't even compile GCC from scratch...

concepts is a must-have in order to design a flexible interface in C++, it is by far the only facility to express type-level equivariance in C++, take the following polymorphic function f() for example:
Code:
auto f(auto&& x) {
    if constexpr (requires { { x.g() }->Iterable; })
        if constexpr (requires { { *x.g().begin() }->SubtypeOf<VideoInfo>; })
            return std::vector<VideoNode>{};
        else if constexpr (requires { { *x.g().begin() }->SubtypeOf<AudioInfo>; })
            return std::vector<AudioNode>{};
        else
            static_assert(AlwaysFalse<decltype(x)>, "Type Error!");
    else if constexpr (requires { { x.g() }->SubtypeOf<VideoInfo>; })
        return VideoNode{};
    else if constexpr (requires { { x.g() }->SubtypeOf<AudioInfo>; })
        return AudioNode{};
    else
        static_assert(AlwaysFalse<decltype(x)>, "Type Error!");
}
the "if constexpr (requires { ... })" construct defines an equivariant map from the return type of x.g() to the return type of f(), and therefore extends polymorphism to the type level for f(). As a result, the user defined function x.g() no longer needs to be constrained by an invariant interface. the user is allowed to define g() however he/she likes at the type level, whatever works the best for his/her particular use case, and the framework function f() always self-adapts to whatever the user wants.

you simply cannot write such f() without concepts. you can try mimicking it using SFINAE in older versions of C++ and I guarantee that your code will be an unreadable and unmaintainable mess in no time. therefore concepts is absolutely essential if you agree that the user's freewill matters.

edit: simple proof that shows f() is indeed equivariant at the type level for types that it can handle.
let F() denote f() at the type level, [] denote a type operator that transforms any type T to std::vector<T>.

we have that:
F([VideoInfo]) = [VideoNode]
[F(VideoInfo)] = [VideoNode]
F([AudioInfo]) = [AudioNode]
[F(AudioInfo)] = [AudioNode]

therefore F() satisfies F(G∙T) = G∙F(T), where G = [], T = VideoInfo, AudioInfo.

Last edited by feisty2; 22nd January 2021 at 13:47.
feisty2 is offline   Reply With Quote