msgpack / msgpack-c

MessagePack implementation for C and C++ / msgpack.org[C/C++]
Other
3.01k stars 877 forks source link

std::tuple of no default constructible class makes compile error on MSVC2015 C++11 build #343

Open redboltz opened 9 years ago

redboltz commented 9 years ago

See: https://github.com/msgpack/msgpack-c/pull/339#issuecomment-131367798

#include <msgpack.hpp>

struct no_def_con {
    no_def_con() = delete;
    no_def_con(int i) :i(i) {}
    int i;
    MSGPACK_DEFINE(i);
};

namespace msgpack {
    MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {
        namespace adaptor {
            template <>
            struct as<no_def_con> {
                no_def_con operator()(msgpack::object const& o) const {
                    if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
                    if (o.via.array.size != 1) throw msgpack::type_error();
                    return no_def_con(o.via.array.ptr[0].as<int>());
                }
            };
        } // adaptor
    } // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS)
} // msgpack

int main() {
    {   // OK
        std::tuple<no_def_con> val1{ 1 };
        msgpack::zone z;
        msgpack::object o(val1, z);
        o.as<std::tuple<no_def_con>>();
    }
    {   // ERROR
        std::tuple<no_def_con, no_def_con> val1{ 1, 2 };
        msgpack::zone z;
        msgpack::object o(val1, z);
        // o.as<std::tuple<no_def_con, no_def_con>>(); // ERROR *1
    }
}

When the line *1 uncomment, I got the following compile error:

1>------ Build started: Project: no_def_con_tuple, Configuration: Debug Win32 ------
1>  Source.cpp
1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\tuple(69): error C2280: 'no_def_con::no_def_con(void)': attempting to reference a deleted function
1>  c:\users\kondo\documents\visual studio 2015\projects\no_def_con_tuple\no_def_con_tuple\source.cpp(4): note: see declaration of 'no_def_con::no_def_con'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\tuple(67): note: while compiling class template member function 'std::_Tuple_val<_This>::_Tuple_val(void)'
1>          with
1>          [
1>              _This=no_def_con
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\tuple(181): note: see reference to function template instantiation 'std::_Tuple_val<_This>::_Tuple_val(void)' being compiled
1>          with
1>          [
1>              _This=no_def_con
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\tuple(462): note: see reference to class template instantiation 'std::_Tuple_val<_This>' being compiled
1>          with
1>          [
1>              _This=no_def_con
1>          ]
1>  c:\users\kondo\documents\visual studio 2015\projects\no_def_con_tuple\no_def_con_tuple\source.cpp(27): note: see reference to class template instantiation 'std::tuple<no_def_con>' being compiled
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

The error message indicates line 27:

std::tuple<no_def_con> val1{ 1 };

but the line 36:

o.as<std::tuple<no_def_con, no_def_con>>(); // ERROR *1

causes the compile error.

If the elements of the std::tuple is one, no errors are occurred, if the elements of the std::tuple are two or more, the compile error occurs.

This compile error only happens C++11 msgpack-c, So you can avoid the compile error with MSGPACK_USE_CPP03 macro definition.

I analyzed msgpack-c code. I think that https://github.com/msgpack/msgpack-c/blob/master/include/msgpack/adaptor/cpp11/tuple.hpp#L75 is something relate to the problem. This code works well on Linux with libstdc++ and osx with libc++. I think that MSVC2015's tuple implementation might have a problem. But I couldn't write a minimal code that reproduces the problem.

Any help is welcome.

redboltz commented 9 years ago

I analyzed a little and created a minimal code that reproduce the problem. See the following code:

#include <tuple>
#include <utility>

template<bool...> struct bool_pack;

template<bool...values> struct all_of_imp
    : std::is_same<bool_pack<values..., true>, bool_pack<true, values...>> {};

template<template <class> class T, class... U>
using all_of = all_of_imp<T<U>::value...>;

template <typename T, typename Enabler = void>
struct as;

template <typename T>
struct has_as {
private:
    template <typename U>
    static auto check(U*) -> typename std::is_same<decltype(as<U>()()), T>::type;
    template <typename>
    static std::false_type check(...);
public:
    using type = decltype(check<T>(nullptr));
    static constexpr bool value = type::value;
};

template <typename... Args>
struct as<std::tuple<Args...>, typename std::enable_if<all_of<has_as, Args...>::value>::type> {
    std::tuple<Args...> operator()() const {
        return std::tuple<Args...>();
    }
};

struct my {};

template <>
struct as<my> {
    my operator()() const {
        return my();
    }
};

template <typename T, typename U>
typename std::enable_if<all_of<has_as, T, U>::value>::type foo(std::tuple<T, U> const&) {}

template <typename... Args>
typename std::enable_if<all_of<has_as, Args...>::value>::type bar(std::tuple<Args...> const&) {}

int main() {
    static_assert(all_of<has_as, my, my>::value, ""); // OK
    foo(std::tuple<my, my>()); // OK
    bar(std::tuple<my>());     // OK
    bar(std::tuple<my, my>()); // ERROR on MSVC2015
}

clang++ and g++ have no compile errors. http://melpon.org/wandbox/permlink/anHrLJzwWvnLC5cn http://melpon.org/wandbox/permlink/xbYL2J8UFSlyEPZe

However, MSVC2015 produced the following compile error:

source1.cpp(55): error C2893: Failed to specialize function template 'std::enable_if<all_of_imp<has_as<Args...>::value>::value,void>::type bar(const std::tuple<_Types...> &)'
source1.cpp(55): note: With the following template arguments:
source1.cpp(55): note: 'Args={my, my}'

The original problem that requires default constructor is the result of specialization failure. When the specialization failure, 'as' is fallbacked into 'convert'. 'convert' requires a default constructor.

This code reproduce the specialization failure.

I guess that there are some variadic template parameters related problems on MSVC2015. Consider the following code:

    foo(std::tuple<my, my>()); // OK
    bar(std::tuple<my>());     // OK
    bar(std::tuple<my, my>()); // ERROR on MSVC2015

foo() with two template arguments compiled successfully. foo()'s enable_if has two template parameters. bar() with one template argument compiled successfully. bar()'s enable_if has variadic template parameters.

template <typename T, typename U>
typename std::enable_if<all_of<has_as, T, U>::value>::type foo(std::tuple<T, U> const&) {}

template <typename... Args>
typename std::enable_if<all_of<has_as, Args...>::value>::type bar(std::tuple<Args...> const&) {}
redboltz commented 9 years ago

I found the reason of this behavior. First, MSVC2015 doesn't support Expression SFINAE yet. See: https://msdn.microsoft.com/en-us/library/hh567368.aspx https://msdn.microsoft.com/en-us/library/hh567368.aspx#corelanguagetable Expression SFINAE http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html is not supported.

And according to the following post, they will support it in a future update: http://blogs.msdn.com/b/vcblog/archive/2015/06/19/c-11-14-17-features-in-vs-2015-rtm.aspx [1] We're planning to start implementing Expression SFINAE in the compiler immediately after 2015 RTM, and we're planning to deliver it in an Update to 2015, supported for production use. (But not necessarily 2015 Update 1. It might take longer.)

msgpack-c checks 'as' class template specialization that has operator()(msgpack::object const&). A User can define the specialization for a user defined classe to support non-default-constructible class or to achive move based efficient implementation on C++11. If the specialization is not found, msgpack-c uses 'convert' class template as a fallback. And 'convert' class template requires default constructor of the user type. To check 'as' class template specialization existance, msgpack-c uses Expression SFINAE.

Conclusion

We can use msgpack-c's C++11 functionality on MSVC2015 except non-default-constructible support using 'as' class template specialization. In the current implementation, when the number of template arguments is one, Expression SFINAE works as expected by chance. So 'as' class template specialization's operator() is dispatched as expected. However, we shouldn't depend on this implementation because Microsoft offically said that they don't support Expression SFINAE yet.

When we use MSVC2015 with C++11 msgpack-c, don't define 'as' class template specialization until MSVC2015 would support Expression SFINAE.

redboltz commented 9 years ago

I updated documents. https://github.com/msgpack/msgpack-c/wiki#08172015 https://github.com/msgpack/msgpack-c/wiki/v1_1_cpp_adaptor#non-default-constructible-class-support-c11-only