I'm trying to do a templated user-defined-conversion that uses design by introspection.
In C 20 I can do the following:
template<typename T>
operator T() const
{
if constexpr( requires { PFMeta<T>::from_cursor(*this); } )
{
return PFMeta<T>::from_cursor(*this);
}
else
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
}
i.e. if PFMeta<T>::from_cursor(const struct PFCursor&)
is defined then use it else do a memcpy. (longer example here in godbolt: https://godbolt.org/z/abed1vsK3)
I love this approach but unfortunately this library will need to work on C 17 too.
So I've been trying SFINAE as an alternative to the concepts but it's very tricky.
I finally managed to get something similar but with a templated method as
rather than the user-defined-conversion operator itself:
template<typename T, typename = void>
struct has_from_cursor : std::false_type { };
template<typename T>
struct has_from_cursor<T, decltype( PFMeta<T>::from_cursor(std::declval<struct PFCursor>()), void() ) > : std::true_type { };
// ...
template<class T>
std::enable_if_t<has_from_cursor<T>::value, T> as() const
{
return PFMeta<T>::from_cursor(*this);
}
template<class T>
std::enable_if_t<!has_from_cursor<T>::value, T> as() const
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
I tried the following which compiles but does not work (I can't cast with it):
template<typename T>
operator std::enable_if_t<has_from_cursor<T>::value, T>() const
{
return PFMeta<T>::from_cursor(*this);
}
template<typename T>
operator std::enable_if_t<!has_from_cursor<T>::value, T>() const
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
Longer example here: https://godbolt.org/z/5r9Mbo18h
So two questions:
- Can I do effectively what I can do with concepts with just SFINAE in C 17 for the user defined conversion operator?
- Is there a simpler way to do the
as
approach than the type traits &enable_if
? Ideally I'd like to do something like define the default templated method and then have a specialisation to be preferred if the condition is there (i.e. there is a meta class with a static member function defined).
Thanks!
CodePudding user response:
Can I do effectively what I can do with concepts with just SFINAE in C 17 for the user defined conversion operator?
Consider that C 17 support if constepr
. Given that you've developed a has_from_cursor
custom type traits that inherit from std::true_type
or from std::false_type
, you can use it for if constexp
.
I mean (caution: code not tested)
template<typename T>
operator T() const
{ // .............VVVVVVVVVVVVVVVVVVVVVVVVV
if constexpr( has_from_cursos<T>::value )
{
return PFMeta<T>::from_cursor(*this);
}
else
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
}
Is there a simpler way to do the as approach than the type traits & enable_if? Ideally I'd like to do something like define the default templated method and then have a specialisation to be preferred if the condition is there (i.e. there is a meta class with a static member function defined).
I suppose you can try tag-dispatching...
I mean something as calling 'as()', from operator T()
with an additional int
argument
template<typename T>
operator T() const
{ return as<T>(0); } // <-- call as() with a int
where there is a as()
specific for from_cursos
class enabled, that receive an unused int
and is simply SFINAE enabled/disabled through decltype()
template <typename T> // ........accept an int; best match
decltype(PFMeta<T>::from_cursor(*this)) as (int) const
{ return PFMeta<T>::from_cursor(*this); }
and a generic as()
, receiving a long
template <typename T>
T as (long) const // <-- accept a long; worst match
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
The trick is the unused argument: a int
.
When the specialized as()
is enabled, if preferred because accept a int
so is a better match.
When the specialized as()
is disabled, remain the generic as()
as better-than-nothig match.