#include <iostream>
#include <type_traits>

template<typename TF, typename Tag>
void write_debug_output( std::ostream & out, TF&& f, Tag ) {
	out << f;
}

struct tracer {
	template<typename T>
	struct tag { struct type {}; };
	
	std::ostream & out;
	tracer( std::ostream & out, char const * file, int line )
		: out( out ) {
		out << file << ":" << line << ": ";
	}
	~tracer() {
		out << std::endl;
	}
	
	template<typename TF, typename ... TR>
	void write( TF&& f, TR&& ... rest ) {
		typename tag<
			typename std::remove_cv<
				typename std::remove_reference<TF>::type
				>::type
			>::type t;
		write_debug_output( out, std::forward<TF>(f), t );
		out << " ";
		write( std::forward<TR>(rest)... );
	}
	template<typename TF>
	void write( TF&& f ) {
		typename tag<
			typename std::remove_cv<
				typename std::remove_reference<TF>::type
				>::type
			>::type t;
		write_debug_output( out, std::forward<TF>(f), t );
	}
	void write() {
		//handle the empty params case
	}
};

#define TRACE(...) tracer( std::cout, __FILE__, __LINE__ ).write( __VA_ARGS__ )

#define TRACE_OUTPUT(match_type) \
	template<typename TF> \
	void write_debug_output( std::ostream & out, TF && f, tracer::tag<match_type>::type )

struct my_object { };
TRACE_OUTPUT(my_object) {
	out << "my_object f is a " 
 		<< (std::is_const<typename std::remove_reference<TF>::type>::value ? "const " : "" )
 		<< (std::is_lvalue_reference<TF>::value ? "lvalue" : "rvalue");
}

struct other_object {
	int val;
};
TRACE_OUTPUT(other_object) {
 	out << "val=" << f.val;
}

int main()
{
 	my_object const a;
	TRACE(a);
 	TRACE("CONST",a);
  	my_object b;
  	TRACE("MUTABLE",b);
 	TRACE("RVALUE", my_object() );
 	TRACE( 123, 5.5 );
 	TRACE();
 	TRACE( "other", other_object{14} );
}
