Search code examples
c++templatessfinaestringstreamenable-if

Prevent instantiation of template class for types not supported by stringstream extraction operator (>>)


I'm trying to learn a bit about templates and metafunctions, namely std::enable_if. I'm making a menu system for our school assignments (extracurricular, mind you), and need a way of getting input from the user. I'd like to define a template class for various types of input - something used along the lines of:

std::string userInput = Input<std::string>("What's your name?").Show();
float userHeight = Input<float>("How tall are you?").Show();

I'd like to (and I'm sure there are reasons not to, but nevertheless) do this generalized sort of conversion using a std::stringstream: get input from user, feed into SS, extract into variable of type T.

It's easy enough to see if the conversion failed during runtime, but I'd like to use std::enable_if to prevent people from using my Input<> class for cases where conversion is impossible, say:

std::vector<Boats> = Input<std::vector<>>("Example").Show();

Obviously a std::stringstream cannot convert a string to a vector, so it will always fail.

My question is this:

Can I format an std::enable_if clause to ONLY allow instantiation of my template class for the types listed above? Alternatively, is there a better way to go about it? Have I got things completely the wrong way around?

What I've done so far

I believe I have found a list of allowed types that std::stringstream can "convert" a string into:

http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/

I've been using std::enable_if like this up until this point:

template <typename T, typename = typename 
std::enable_if<std::is_arithmetic<T>::value, T>::type>

However, now I'd like to extend it to allow not only arithmetic values, but all values supported by the sstream >> operator.


Solution

  • If you prefer to use a SFINAE with a class template parameter, then you want

    template <
        typename T,
        typename = decltype(std::declval<std::istringstream &>() >> std::declval<T &>(), void())
    >
    class Input /*...*/