KDAB / KDAlgorithms

Algorithm wrappers
Other
82 stars 15 forks source link

transform compile error in Visual Studio #43

Closed hmoffatt closed 1 year ago

hmoffatt commented 1 year ago

I'm using transformed to convert a QVector into a QJsonArray of QJsonObjects as per the code below. It compiles OK with g++ 10 and clang++ 11, but not in Visual Studio 2022.

#include <kdalgorithms.h>

#include <QVector>
#include <QJsonArray>
#include <QJsonObject>

void f()
{
    struct S { int a, b; };
    QVector<S> s;

    auto j = kdalgorithms::transformed<QJsonArray>(s, [](const auto& v)
        {
            return QJsonObject{
                { QStringLiteral("a"), v.a },
                { QStringLiteral("b"), v.b }
            };
        });
}

The VS compiler reports

1>test.cpp(973,30): error C2039: 'a': is not a member of 'QJsonValue'
1>C:\qt\Qt5.15.12\5.15.12\msvc2019_64\include\QtCore\qjsonvalue.h(59,21): message : see declaration of 'QJsonValue'
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1470,1): message : see reference to function template instantiation 'auto f::<lambda_1>::operator ()<QJsonValue>(const _T1 &) const' being compiled
1>        with
1>        [
1>            _T1=QJsonValue
1>        ]
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1675,38): message : see reference to alias template instantiation 'std::_Decltype_invoke_nonzero<f::<lambda_1>,QJsonValue,>' being compiled
1>...\include\kdalgorithms\bits\transform.h(82,19): message : see reference to variable template 'const bool is_invocable_v<`f'::`2'::<lambda_1>,QJsonValue>' being compiled
1>test.cpp(974,30): error C2039: 'b': is not a member of 'QJsonValue'
1>C:\qt\Qt5.15.12\5.15.12\msvc2019_64\include\QtCore\qjsonvalue.h(59,21): message : see declaration of 'QJsonValue'
1>test.cpp(972,22): error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'QJsonObject'
1>test.cpp(972,22): message : No constructor could take the source type, or constructor overload resolution was ambiguous

However if I replace the const auto& v parameter in the lambda with const S& v then it does compile.

It seems that with auto a QJsonValue is being passed, being the output container's value_type perhaps?

jesperkdab commented 1 year ago

This is indeed odd. Looking at the code on ...\include\kdalgorithms\bits\transform.h(82,19), I see that it is evaluating the requirements of this function:

template <typename InputContainer, typename Transform> auto transformed(InputContainer &&input, Transform &&transform)

if __cplusplus >= 202002L

requires std::is_invocable_v<Transform, ValueType<InputContainer>>

endif

but the type you provide as the first template argument (QJsonArray) would make the first argument provided (QVector< S >) not match in the first place, and it should discard this version.

Sorry, this is beyond me.

hmoffatt commented 1 year ago

It works with the compiler set to c++17, but not c++20 or c++2a, which confirms that code is firing. Although I don't understand why it would compile just by removing that is_invocable_v test.

But why is it trying that version of transformed() anyway? It should be the version at line 91 with a different ResultContainer type, since I have explicitly specified a different result container than the input container.

hmoffatt commented 1 year ago

This change has not solved my Visual Studio compile errors. The problem cases are all when the output container is QJsonArray.

aambrosano commented 1 year ago

Adding the /Zc:__cplusplus compile flag fixes the build for me. See here for more details.

aambrosano commented 1 year ago

PR #47 will make adding the flag unnecessary once merged.

hmoffatt commented 1 year ago

Adding the /Zc:__cplusplus compile flag fixes the build for me. See here for more details.

I'm afraid it doesn't fix it for me - the code in my report still does not compile. (In fact, qmake in Qt 5.15.12 already enabled that switch for me.)

hmoffatt commented 1 year ago

However if I replace the const auto& v parameter in the lambda with const S& v then it does compile.

It also works if I specify the return type of the lambda explicitly:

void f()
{
    struct S { int a, b; };
    QVector<S> s;

    auto j = kdalgorithms::transformed<QJsonArray>(s, [](const auto& v) -> QJsonObject
        {
            return QJsonObject{
                { QStringLiteral("a"), v.a },
                { QStringLiteral("b"), v.b }
        };
        });
}
hmoffatt commented 1 year ago

I also have a similar problem using filtered_transform; weirdly, I can use auto for the transform but not the filter. This compiles:

    class ContentItem
    {
    public:
        int num = 0;
        int attempt_num = 0;
        QDateTime purchased;
        QDateTime expires;
    };

    QVector<ContentItem> user_courses;

    auto x = kdalgorithms::filtered_transformed<std::vector<int>>(user_courses,
        [](const auto& item) { return item.num; },
        [expiry](const ContentItem& item) { return item.expires.isNull() || item.expires >= expiry; });

But using auto in the filter makes it fail:

    auto x = kdalgorithms::filtered_transformed<std::vector<int>>(user_courses,
        [](const auto& item) { return item.num; },
        [expiry](const auto& item) { return item.expires.isNull() || item.expires >= expiry; });

with error:

1>test.cpp(2685,44): error C2228: left of '.expires' must have class/struct/union
1>test.cpp(2685,44): message : type is 'const _T1'
1>        with
1>        [
1>            _T1=int
1>        ]
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1470,1): message : see reference to function template instantiation 'auto TUser::currentCourseLicences::<lambda_2>::operator ()<_Ty1>(const _T1 &) const' being compiled
1>        with
1>        [
1>            _Ty1=int,
1>            _T1=int
1>        ]
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1675,38): message : see reference to alias template instantiation 'std::_Decltype_invoke_nonzero<TUser::currentCourseLicences::<lambda_2>,int,>' being compiled
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1748,42): message : see reference to alias template instantiation 'std::_Is_invocable_r_<bool,TUser::currentCourseLicences::<lambda_2>,int>' being compiled
1>include\kdalgorithms\bits\shared.h(74,10): message : see reference to variable template 'const bool is_invocable_r_v<bool,`TUser::currentCourseLicences'::`2'::<lambda_2>,int>' being compiled
1>include\kdalgorithms\bits\transform.h(196,14): message : see reference to variable template 'bool UnaryPredicateOnContainerValues<`TUser::currentCourseLicences'::`2'::<lambda_2>,std::vector<int,std::allocator<int> > >' being compiled

Again adding the return types for the lambda makes it compile:

    auto x = kdalgorithms::filtered_transformed<std::vector<int>>(user_courses,
        [](const auto& item) -> int { return item.num; },
        [expiry](const auto& item) -> bool { return item.expires.isNull() || item.expires >= expiry; });
aambrosano commented 1 year ago

Indeed, the issue with /Zc:__cplusplus was there but it was unrelated.

I managed to reproduce your error with /std:c++latest (but not with /std:c++20 somehow).

My impression is that when concepts are introduced that error is legitimate because the requirement that the lambda is invocable with the valuetype or QJsonArray is met (type-wise auto can be replaced with QJsonValue just fine, and the body of the lambda is not taken into account with std::is_invocable). That would explain why it does work when you switch from auto to S.

Will have to dig a bit more to figure out more details.

hmoffatt commented 1 year ago

I managed to reproduce your error with /std:c++latest (but not with /std:c++20 somehow).

Odd, I have only /std:c++20, not c++latest. Visual Studio 17.4.4 (2022).

hmoffatt commented 1 year ago

Is there another change other than the /Zc:__cplusplus switch? Because as I wrote above I still have the problem and I thought you were able to reproduce it also.