Question

I have a simple wrapper class for a GTK GUI like this:

template <class T>
class LabeledEntry
{
    string name;
    T var;

    Gtk::HBox hbox;
    Gtk::Label label;
    Gtk::Entry entry;

    public:
    LabeledEntry( string _name, T _var, Gtk::VBox* vbox):
        name( _name ),
        var(_var)
    {
        label.set_text( name.c_str() );
        ostringstream os;
        os << var ;

        entry.set_text( os.str().c_str() );
        hbox.add( label );
        hbox.add( entry );

        vbox->add( hbox);
    }

    T Get()
    {
        string valString( entry.get_text());
        istringstream is( valString);
        is >> noskipws >> var;

        return var;
    }
};

Now I need a special implementation for T Get() if the type of T is string, because skipping white space for strings is not working. So here I need getline in the method.

I found a lot of std::is__xxx templates to check against a lot of properties like is_integral and so on. But I need to compare against a given type directly. Any chance?

And how is the syntax to write the both implementations inside the class? Something like:

class ...
{ 
    std::enable_if( true, XXX_IS_STRING)::type Get()
    {
    }

    std::enable_if ( false, XXX_IS_SRING)::type Get()
    {
    }
};

Sorry, I am a bit confused using SFINAE without template parameters in the member parameter list.

Was it helpful?

Solution

You should make Get function templated and use std::enable_if like this:

#include <type_traits>
#include <iostream>
#include <string>

template <class T>
class LabeledEntry
{
    // ...
public:
    template <class U = T>
    typename std::enable_if<std::is_same<U, std::string>::value, U>::type
        Get()
    {
        return {"string"};
    }

    template <class U = T>
    typename std::enable_if<!std::is_same<U, std::string>::value, U>::type
        Get()
    {
        return {42};
    }
};

int main()
{
    LabeledEntry<std::string> sle;
    std::cout << sle.Get() << std::endl;

    LabeledEntry<int> ile;
    std::cout << ile.Get() << std::endl;
    return 0;
}

OTHER TIPS

If only member functions need special implementations based on the class template type, then it may be easier to simply define a specialization for the member function, rather than using SFINAE.

#include <iostream>
#include <string>

template <class T>
class LabeledEntry
{
  // ...
public:
  T Get()
  {
    return 42;
  }
};

template <>
std::string LabeledEntry<std::string>::Get()
{
  return "string";
}

int main()
{
  std::cout << LabeledEntry<std::string>().Get() << "\n"
            << LabeledEntry<int>().Get() << std::endl;
}

Which results in the following output:

string
42

But I need to compare against a given type directly. Any chance?

Use std::is_same<T, std::string> to test whether T is std::string.

You can define an alias template to simplify that:

  template<typename T>
    using is_string = std::is_same<T, std::string>;

Sorry, I am a bit confused using SFINAE without template parameters in the member parameter list.

You can't do SFINAE on non-templates, so if the member function is not a template function you can't use SFINAE.

Your choices include:

  • make the member function a template (see soon's answer)

  • forward to another function which is a template:


    T Get() const
    { return Get_impl( entry.get_text() ); }

  private:
    template<typename P>
      using enable_if = typename std::enable_if<P::value>::type;

    template<typename U, typename Requires = enable_if<is_string<U>>>
      U Get_impl( const U& u ) const
      { ... }

    template<typename U, typename Requires = enable_if<!is_string<U>>>
      U Get_impl( const U& u ) const
      { ... }
  • or define a template specialization of LabeledEntry<std::string>, or of some other class that LabeledEntry makes use of to do the conversion.

One way to do it is to use a functor outside of your class that you delegate the work to. Here is a simple example that is not doing anything useful, but you should get the picture:

template <typename U>
struct get_functor
{
    std::string operator()(U const & val) const
    {
        std::stringstream ss;
        ss << val;
        return ss.str();
    }
};

template <>
struct get_functor<std::string>
{
    std::string operator()(std::string const &) const
    {
        return "something else";
    }
};

template <typename T>
struct demo
{
    explicit demo(T val) : val_(val) {}

    decltype(get_functor<T>()(std::declval<T>())) get() const
    {
        return get_functor<T>()(val_);
    }

    private:
        T const val_;
};

or you can go with enable_if, but there you will have to create a dummy template typename U = T to enable SFINAE:

enum class enable_if_helper {};

template <bool C> 
using enable_if = typename std::enable_if<C, enable_if_helper>::type;

template <bool C>
using disable_if = typename std::enable_if<!C, enable_if_helper>::type;

template <typename T>
struct demo
{
    explicit demo(T val) : val_(val) {}

    template <
        typename U = T, 
        enable_if<std::is_same<U, std::string>::value>...
    >
    std::string get() const
    {
        return "something else"; 
    }

    template <
        typename U = T,
        disable_if<std::is_same<U, std::string>::value>...
    >
    std::string get() const
    {
        std::stringstream ss;
        ss << val_;
        return ss.str();
    }

    private:
        T const val_;
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top