Skip to content

compile-time check if adaptor for type exists #1051

@timblechmann

Description

@timblechmann

more a question than a bug, but something that i've been wondering without having found a good answer, yet:

i'd like to detect at compile time via a constexpr function if an adaptor for a specific type exists:

template <typename T>
constexpr bool msgpack_can_pack();

currently we have:

template <typename T, typename Enabler>
struct convert {
    msgpack::object const& operator()(msgpack::object const& o, T& v) const;
};

operator() is either implemented by a specialization or it is using the default implementation at:

template <typename T, typename Enabler>
inline
msgpack::object const&
adaptor::convert<T, Enabler>::operator()(msgpack::object const& o, T& v) const {
    v.msgpack_unpack(o.convert());
    return o;
}

so afaict we may need two answer two question:

  • can we use enable_if to check if the operator() can be implemented for a specific type?
  • can we check if operator() exists in order to implement msgpack_can_pack?

thoughts?


i've done a few experiments:

diff --git a/include/msgpack/v1/adaptor/adaptor_base.hpp b/include/msgpack/v1/adaptor/adaptor_base.hpp
index 489362b..b2291ec 100644
--- a/include/msgpack/v1/adaptor/adaptor_base.hpp
+++ b/include/msgpack/v1/adaptor/adaptor_base.hpp
@@ -18,17 +18,43 @@ namespace msgpack {
 MSGPACK_API_VERSION_NAMESPACE(v1) {
 /// @endcond
 
 
 namespace adaptor {
 
+namespace impl {
+
+template < typename T >
+using msgpack_unpack_t
+    = decltype( std::declval< T >().msgpack_unpack( std::declval< msgpack::object::implicit_type >() ) );
+
+template < typename T, typename = std::void_t<> >
+struct has_msgpack_unpack : std::false_type
+{};
+
+template < typename T >
+struct has_msgpack_unpack< T, std::void_t< msgpack_unpack_t< T > > > : std::true_type
+{};
+
+template < typename T >
+inline constexpr bool has_msgpack_unpack_v = has_msgpack_unpack< T >::value;
+
+} // namespace impl
+
+
 // Adaptor functors
 
-template <typename T, typename Enabler>
-struct convert {
-    msgpack::object const& operator()(msgpack::object const& o, T& v) const;
+template<typename T, typename Enabler>
+struct convert
+{
+};
+
+template<typename T>
+struct convert<T, std::enable_if_t<impl::has_msgpack_unpack_v<T>>>
+{
+    msgpack::object const &operator()(msgpack::object const &o, T &v) const;
 };
 
 template <typename T, typename Enabler>
 struct pack {
     template <typename Stream>
     msgpack::packer<Stream>& operator()(msgpack::packer<Stream>& o, T const& v) const;
diff --git a/include/msgpack/v1/object.hpp b/include/msgpack/v1/object.hpp
index fda54be..a9ac15a 100644
--- a/include/msgpack/v1/object.hpp
+++ b/include/msgpack/v1/object.hpp
@@ -637,16 +637,16 @@ struct packer_serializer {
         return o;
     }
 };
 } // namespace detail
 
 // Adaptor functors' member functions definitions.
-template <typename T, typename Enabler>
+template <typename T>
 inline
 msgpack::object const&
-adaptor::convert<T, Enabler>::operator()(msgpack::object const& o, T& v) const {
+adaptor::convert<T, std::enable_if_t<adaptor::impl::has_msgpack_unpack_v<T>>>::operator()(msgpack::object const& o, T& v) const {
     v.msgpack_unpack(o.convert());
     return o;
 }
 
 template <typename T, typename Enabler>
 template <typename Stream>

this would allow me to write something like:

template < typename T, typename = void >
struct has_msgpack_convert_helper : std::false_type
{};

template < typename T >
struct has_msgpack_convert_helper< T,
                                   std::void_t< decltype( std::declval< msgpack::v3::adaptor::convert< T > >()(
                                       std::declval< msgpack::object const& >(), std::declval< T& >() ) ) > >
    : std::true_type
{};

template < typename T >
constexpr bool has_msgpack_convert = has_msgpack_convert_helper< T >::value;

any thoughts on such functionality in the library?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions