2

I have Base class and some Derived classes. Certain bit patterns that come in from external code can make these, and there is generic code which builds them. That generic code has to use an explicit template "factory":

auto d = factory<D>(data); // where D is some Derived class

This factory is SFINAE-enabled for classes that are derived from Base:

template<
    class T,
    typename = typename std::enable_if<
        std::is_base_of<Base, T>::value
    >::type
>
T factory(ExternalData &data) {
    T result;
    if (!result.initFromData(&data))
        throw std::runtime_error("data is not set");
    return result;
}

I'd like to add another variant that works with std::experimental::optional<Derived> for the cases when the bit patterns indicate things are unset. In this case the generic code would be passing in either type:

auto dod = factory<DOD>(data); // DOD can be Derived or std::optional<Derived>

Using explicit parameterization like this, how would I create another SFINAE variant which would "pattern match" into existence only for for std::experimental::optional<any class whose base is Base>?

The desire would be if I could write:

template<
    std::experimental::optional<class T>, // imaginary
    typename = typename std::enable_if<
        std::is_base_of<Base, T>::value
    >::type
>
std::optional<T> factory(ExternalData &data) {
    T result;
    if (!result.initFromData(&data))
        return nullopt;
    return result;
}

Is there a non-imaginary version of implementing the desire? I looked at "using SFINAE for template class specialisation" (and several others) and it seems like maybe there are some ways to cooperatively hack something together to do it with a container if I wrote it (?), but am I missing an easy answer?

Community
  • 1
  • 1

1 Answers1

1
#include <type_traits>

template <typename T>
struct extract_optional;

template <typename T>
struct extract_optional<std::experimental::optional<T>>
{
    using type = T;
};

template <typename T>
using extract_optional_t = typename extract_optional<T>::type;

template <
    typename T,
    typename OP = extract_optional_t<T>,
    typename = typename std::enable_if<
        std::is_base_of<Base, OP>::value
    >::type
>
std::experimental::optional<OP> factory(ExternalData &data) {
    OP result;
    if (!result.initFromData(&data))
        return nullopt;
    return result;
}

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • Problems solved in real-time! :-) Thanks very much. It seems to do the trick and makes good sense...I'll need to test it a little more to be sure it works. One thing I do recall about these kinds of things is that in order to keep them from getting fouled up if someone happens to provide an explicit parameterization for OP is a static assert. Something like that T is equal to std::experimental::optional... – HostileFork says dont trust SE Sep 30 '15 at 07:08
  • 2
    @HostileFork or just [inline all occurences of `extract_optional_t`](http://coliru.stacked-crooked.com/a/f7bd80c9ee7b9ae9) – Piotr Skotnicki Sep 30 '15 at 08:13
  • Could you elaborate a little more? I'm very interested in the point of your answer unfortunately it's too succinct for me to understand – Rerito Sep 30 '15 at 08:41
  • 2
    @Rerito `extract_optional` is established as a prototype of a templated structure. If there were no definitions of it anywhere, you couldn't use it with anything, and [SFINAE](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error) would gracefully fail in all cases that tried to use it. But it is specialized as being allowed to be used with one thing: `optional`...so it only succeeds in instantiating there. We get the two things we wanted: the OP contained type for checking and use, plus assurance that T was a std::optional (with the caveat I noted, fixed via inlining.) – HostileFork says dont trust SE Sep 30 '15 at 12:01