// introspect.h header with a bunch of evil macros
#ifndef INTROSPECT_H#define INTROSPECT_Htypedef char (&n)[1];typedef char (&y)[2];template< bool condition, typename T> struct enable_if {};template< typename T > struct enable_if<true,T> { typedef T type; };#define INTROSPECT_IMPL(id, ret, func, cflag, ...) template< typename T, ret (T::*)(__VA_ARGS__) cflag> struct has_##id##_pfn {}; template< typename T > n has_##id##_x(...); template< typename T > y has_##id##_x(int, has_##id##_pfn<T, &T::func >* p = 0); template< typename T > struct has_##id { enum { value = sizeof(has_##id##_x<T>(0)) == sizeof(y) }; }#define INTROSPECT(id, ret, func, ...) INTROSPECT_IMPL(id, ret, func,, __VA_ARGS__)#define INTROSPECT_CONST(id, ret, func, ...) INTROSPECT_IMPL(id, ret, func, const, __VA_ARGS__)#define WHEN_AVAILABLE(id, ret) template<typename T> typename enable_if<has_##id<T>::value,ret>::type#define WHEN_UNAVAILABLE(id, ret) template<typename T> typename enable_if<!has_##id<T>::value,ret>::type#define WHEN_AVAILABLE_INLINE(id, ret) template<typename T> inline typename enable_if<has_##id<T>::value,ret>::type#define WHEN_UNAVAILABLE_INLINE(id, ret) template<typename T> inline typename enable_if<!has_##id<T>::value,ret>::type#endif
// introspect.cpp with usage examples for the fast_swap case
#include <algorithm>#include <vector>#include <complex>#include "introspect.h"INTROSPECT(foo, void, swap, T);WHEN_UNAVAILABLE_INLINE(foo, void) fast_swap(T& a, T& b) { ::std::swap<T>(a,b);}WHEN_AVAILABLE_INLINE(foo, void) fast_swap(T& a, T& b) { a.swap(b);}int main() { std::vector<int> vector1, vector2; std::complex<int> complex1, complex2; double a,b; fast_swap(vector1, vector2); fast_swap(complex1, complex2); fast_swap(a,b);}
// Example of using the introspection to wrap cout: oswrapper.h
// The operator << is defined for the wrapper class to use an object's
// "void operator() (oswrapper&)" when it exists, and the usual operator <<
// for cout otherwise.
#ifndef OSWRAPPER_H#define OSWRAPPER_H// I can't do this by extending ostream, because I wouldn't be able to make a// wrapper representing cout...struct oswrapper { ostream& x; oswrapper(ostream& x) : x(x) {} // For some reason, the free-function operator<<'s won't pick up endl etc. oswrapper& operator<<(ostream& ( *pf )(ostream&));};extern oswrapper zout;#endif
// and oswrapper.cpp...
#include "introspect.h"#include "oswrapper.h"#include <iostream>using namespace std;oswrapper& oswrapper::operator<<(ostream& ( *pf )(ostream&)) { x << pf; return *this;}INTROSPECT_CONST(osf, void, operator(), oswrapper&);WHEN_UNAVAILABLE(osf, oswrapper&) operator<<(oswrapper& os, const T& thing) { os.x << thing; return os;}WHEN_AVAILABLE(osf, oswrapper&) operator<<(oswrapper& os, const T& thing) { thing(os); return os;}oswrapper zout(cout);
However, this doesn't really help with implementing the assignment operator for classes without a swap method, does it? :s
IMHO this is the truly nice thing about GCd languages - because of not worrying so much about when you need to delete something, you can avoid certain questions about pointer ownership, and not have to worry about the Rule of Three or equivalent - and you can share objects more often, because you aren't worried about double-deletion. If you really need to copy something in a "every object is a reference" language (Java/Python/etc. object model), you make a new copy, and just throw the old thing away. :)