/* Check cf5-opt.vim defs.
VIM: let g:lcppflags="-std=c++11 -O2 -pthread"
VIM: let g:wcppflags="/O2 /EHsc /DWIN32"
VIM: let g:cppflags=g:Iboost.g:Itbb
VIM: let g:ldflags=g:Lboost.g:Ltbb.g:tbbmalloc.g:tbbmproxy
VIM: let g:ldlibpath=g:Bboost.g:Btbb
VIM: let g:argv=""
VIM-: let g:cf5output=0
*/
#include <iostream>
#include <type_traits>
#include <utility>
#include <exception>
#include <sstream>

struct A
{
	void touch( const char * )
	{
		std::cout << "A::touch";
	}
};

struct B
{
};

struct C : public A
{
};

struct D
{
	void touch( int )
	{
		std::cout << "D::touch";
	}
};


struct call_touch
{
	template <class C>
	struct has_touch
	{
		template <typename T, T t>
		class helper{}; 
		template <typename T> 
		static int test(T*, helper<void (T::*)(const char *), &T::touch>* = 0); 
		template <typename T> 
		static char test(...);

		static const bool value = sizeof(test<C>(0))!=sizeof(char);
	};

	template <class S>
	static
	typename std::enable_if<has_touch<S>::value,void>::type
		touch( S * s, const char * p)
	{
		std::cout << "call_touch: calling -> ";
		return s->touch(p);
	}

	static void touch(...)
	{
		std::cout << "call_touch: no method to call";
	}
};

struct call_touch2
{
	template <class S>
	static void touch( S * s, const char * p, decltype(&S::touch)* =0)
	{
		std::cout << "call_touch2: calling -> ";
		return s->touch(p);
	}

	static void touch(...)
	{
		std::cout << "call_touch2: no method to call";
	}
};

struct call_touch3
{
	template <typename T, T t>
	class helper{};

	template <class S>
	static void touch( S * s, const char * p, helper<void (S::*)(const char*),&S::touch>* =0)
	{
		std::cout << "call_touch3: calling -> ";
		return s->touch(p);
	}

	static void touch(...)
	{
		std::cout << "call_touch3: no method to call";
	}
};

struct call_touch4
{
	template <class S>
	static decltype( ((S*)0)->touch((const char *)(0)) ) 
			touch( S * s, const char * p )
	{
		std::cout << "call_touch4: calling -> ";
		return s->touch(p);
	}

	static void touch(...)
	{
		std::cout << "call_touch4: no method to call";
	}
};

int main ( void )
{try{

	A a;
	B b;
	C c;
	D d;

	std::cout << std::endl;
	std::cout << "test a: ";
	call_touch::touch( &a, 0 );
	std::cout << std::endl;

	std::cout << "test b: ";
	call_touch::touch( &b, 0 );	
	std::cout << std::endl;
	
	std::cout << "test c: ";
	call_touch::touch( &c, 0 );	
	std::cout << std::endl;
	
	std::cout << "test d: ";
	call_touch::touch( &d, 0 );	
	std::cout << std::endl;



	std::cout << std::endl;
	std::cout << "test a: ";
	call_touch2::touch( &a, 0 );	
	std::cout << std::endl;
	
	std::cout << "test b: ";
	call_touch2::touch( &b, 0 );	
	std::cout << std::endl;

	std::cout << "test c: ";
	call_touch2::touch( &c, 0 );	
	std::cout << std::endl;

//	Compiler error is generated.
//		
//	std::cout << "test d: ";
//	call_touch2::touch( &d, 0 );	
//	std::cout << std::endl;
	
	
	
	
	std::cout << std::endl;
	std::cout << "test a: ";
	call_touch3::touch( &a, 0 );
	std::cout << std::endl;

	std::cout << "test b: ";
	call_touch3::touch( &b, 0 );	
	std::cout << std::endl;
	
	std::cout << "test c: ";
	call_touch3::touch( &c, 0 );	
	std::cout << std::endl;
	
	std::cout << "test d: ";
	call_touch3::touch( &d, 0 );	
	std::cout << std::endl;



	std::cout << std::endl;
	std::cout << "test a: ";
	call_touch4::touch( &a, 0 );
	std::cout << std::endl;

	std::cout << "test b: ";
	call_touch4::touch( &b, 0 );	
	std::cout << std::endl;
	
	std::cout << "test c: ";
	call_touch4::touch( &c, 0 );	
	std::cout << std::endl;
	
	std::cout << "test d: ";
	call_touch4::touch( &d, 0 );	
	std::cout << std::endl;




	return 0;
}
catch ( const std::exception& e )
{
	std::cerr << std::endl
			<< "std::exception(\"" << e.what() << "\")." << std::endl;
	return 2;
}
catch ( ... )
{
	std::cerr  << std::endl
			<< "unknown exception." << std::endl;
	return 1;
}}

