Question

To be honest, I dislike virtual dispatching, Interface class. For that reason I want implement own classes without any base abstract class. To be image, I am implementing MyCustomWidget and its some methods have been implemented, others no, because it's not necessary.

// here is my custom widget class, which 'show' method is implemented, but 'close' method is not.
struct MyCustomWidget
{
    void show(){ std::cout << "Hey" << std::endl; }
    //void close(){ std::cout << "Bye"  << std::endl; }
};

// here is your custom widget class, which 'show' is not implemented but 'close' is .
struct YourCustomWidget
{
    //void show(){}
    void close(){ std::cout << "Bye" << std::endl;}
};

// common widget class, which may stored within any custom widgets.
struct Widget
{
    Widget() = default;

    template< typename CustomWidget >
    void add(CustomWidget cw)
    {
        auto child = std::make_unique< proxy<CustomWidget> >( std::move( cw ) )
        childs.push_back( std::move(child ) );
    }
    void show()
    {
        for(auto & e : childs) 
            e->show();
    }
    void close()
    {
        for(auto& e : childs)
            e->close();
    }
private:
    struct proxy_base
    {
        virtual void show() = 0;
        virtual void close() = 0;
        virtual ~proxy_base(){}
    };

    template< typename CustomWidget >
    struct proxy : public proxy_base
    {
        explicit proxy(CustomWidget cw_) : cw( std::move(cw_ ) ) 
        {}

        void show() override final
        {    // -------------->>>>>> (1)
            // call cw.show()  if cw has 'show' method, otherwise nothing.
        }
        void close() override final
        {     /// ---------------->>>> (2)
            // call cw.close if cw has a 'close' method, otherwise nothing.
        }

        CustomWidget cw;
    };

std::vector< std::unique_ptr< proxy_base > >childs;
};

int main()
{
     Widget w;
     w.add( MyCustomWidget() );
     w.add( YourCustomWidget() );

     w.show();
     //.... a lot of code

     w.close();
}

My Question is simple: how me implement that (1) and (2) virtual methods ?

Edit: I see that question already has been answered. Let me change my question. Q2: (1) and (2) methods are 'final' and in base class they were declared as pure virtual, for this situation compiler can optimize virtual table, and avoid it ? I'm interesting in GCC, CLang and Visual Studio 2013.

Was it helpful?

Solution

You can put these in the private section of proxy class:

template<typename T>
auto show_helper(int) -> decltype( std::declval<T>().show(), void())
{
    cw.show();
}

template<typename T>
void show_helper(...) { }

and call them like this from show:

show_helper<CustomWidget>(0);

The first overload gets instantiated only if expression in trailing return type is well formed, that is, when T has got show method.

This is called expression SFINAE and is much shorter than pre-C++11 version from pmr's answer. It's also more flexible as it lets you specify the signature of show more easily. The other answer can give you positive result only to discover that you can't call show without arguments. Pick your poison.

Live example.

OTHER TIPS

You can get a SFINAE traits class to check and then use this to dispatch on an close_impl. Alternatively, you also use the traits class in combination with enable_if to chose the right version of close.

#include <iostream>
#include <type_traits>

template <typename T>
class has_close
{
  typedef char one;
  typedef long two;

  template <typename C> static one test( decltype(&C::close) ) ;
  template <typename C> static two test(...);
public:
  enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

struct X { void close() {} };
struct X1 { };

template<typename T>
struct XX {
  T t;

  void close() {
    close_impl(std::integral_constant<bool, has_close<T>::value>{});
  }

  void close_impl(std::true_type) { std::cout << "call close" << std::endl;t.close(); }
  void close_impl(std::false_type) { std::cout << "no close" << std::endl;}
};

int main()
{
  XX<X> x; x.close();
  XX<X1> x1; x1.close();
  return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top