For detecting if a class has a method or member variable with certain name, you can use a classical SFINAE approach:
template<typename T>
class has_x {
private:
typedef char yes[1];
typedef char no[2];
template<typename U> static yes& test_member(decltype(U::x));
template<typename U> static no& test_member(...);
template<typename U, U> struct check;
template<typename U> static yes& test_method(check<float (U::*)(), &U::x>*);
template<typename U> static no& test_method(...);
public:
static const bool as_method = (sizeof(test_method<T>(0)) == sizeof(yes));
static const bool as_member = (sizeof(test_member<T>(0)) == sizeof(yes));
};
Which can be tested in a tiny test suite:
struct something_else {
};
struct fn_vec {
float x() const { return 66; };
};
struct mem_vec {
mem_vec() : x(55) {};
float x;
};
int main(int argc, char*argv[]) {
std::cout << "fv: " << has_x<fn_vec>::as_method << std::endl;
std::cout << "mv: " << has_x<mem_vec>::as_method << std::endl;
std::cout << "se: " << has_x<something_else>::as_method << std::endl;
std::cout << std::endl;
std::cout << "fv: " << has_x<fn_vec>::as_member << std::endl;
std::cout << "mv: " << has_x<mem_vec>::as_member << std::endl;
std::cout << "se: " << has_x<something_else>::as_member << std::endl;
return 0;
}
which outputs:
fv: 1
mv: 0
se: 0
fv: 0
mv: 1
se: 0
You can then use it with std::enable_if
to write variants of functions or structures specific to each of the types. You can also use logical operators in std::enable_if
's condition to create any combination of conditions you need.
A silly example in the same test framework as above:
template<typename T>
typename std::enable_if<has_x<T>::as_method, float>::type getX(const T& v) {
return v.x();
}
template<typename T>
typename std::enable_if<has_x<T>::as_member, float>::type getX(const T& v) {
return v.x;
}
template<typename T>
void foo(const T& val) {
std::cout << getX(val) << std::endl;
}
int main(int argc, char*argv[]) {
fn_vec fn;
mem_vec mem;
foo(fn);
foo(mem);
return 0;
}
which outputs:
66
55
That should hopefully give you all tools you need to create your generic framework. Because everything is templated, most of it should get optimized-away by your compiler, and end up reasonably efficient.
All tested in GCC 4.8.2 on Linux, but it should work in any C++11 compiler.