Here\'s what I\'m trying to do:
template struct Model
{
vector vertices ;
#if T has a .normal member
void transform(
You need a meta function to detect your member so that you can use enable_if
. The idiom to do this is called Member Detector. It's a bit tricky, but it can be done!
I know this question already has some answers but I think my solution to this problem is a bit different and could help someone.
The following example checks whether passed type contains c_str()
function member:
template <typename, typename = void>
struct has_c_str : false_type {};
template <typename T>
struct has_c_str<T, void_t<decltype(&T::c_str)>> : std::is_same<char const*, decltype(declval<T>().c_str())>
{};
template <typename StringType,
typename std::enable_if<has_c_str<StringType>::value, StringType>::type* = nullptr>
bool setByString(StringType const& value) {
// use value.c_str()
}
In case there is a need to perform checks whether passed type contains specific data member, following can be used:
template <typename, typename = void>
struct has_field : std::false_type {};
template <typename T>
struct has_field<T, std::void_t<decltype(T::field)>> : std::is_convertible<decltype(T::field), long>
{};
template <typename T,
typename std::enable_if<has_field<T>::value, T>::type* = nullptr>
void fun(T const& value) {
// use value.field ...
}
UPDATE C++20
C++20 introduced constraints and concepts, core language features in this C++ version.
If we want to check whether template parameter contains c_str
member function, then, the following will do the work:
template<typename T>
concept HasCStr = requires(T t) { t.c_str(); };
template <HasCStr StringType>
void setByString(StringType const& value) {
// use value.c_str()
}
Furthermore, if we want to check if the data member, which is convertible to long
, exists, following can be used:
template<typename T>
concept HasField = requires(T t) {
{ t.field } -> std::convertible_to<long>;
};
template <HasField T>
void fun(T const& value) {
// use value.field
}
By using C++20, we get much shorter and much more readable code that clearly expresses it's functionality.
This isn't an answer to your exact case, but it is an alternative answer to the question title and problem in general.
#include <iostream>
#include <vector>
struct Foo {
size_t length() { return 5; }
};
struct Bar {
void length();
};
template <typename R, bool result = std::is_same<decltype(((R*)nullptr)->length()), size_t>::value>
constexpr bool hasLengthHelper(int) {
return result;
}
template <typename R>
constexpr bool hasLengthHelper(...) { return false; }
template <typename R>
constexpr bool hasLength() {
return hasLengthHelper<R>(0);
}
// function is only valid if `.length()` is present, with return type `size_t`
template <typename R>
typename std::enable_if<hasLength<R>(), size_t>::type lengthOf (R r) {
return r.length();
}
int main() {
std::cout <<
hasLength<Foo>() << "; " <<
hasLength<std::vector<int>>() << "; " <<
hasLength<Bar>() << ";" <<
lengthOf(Foo()) <<
std::endl;
// 1; 0; 0; 5
return 0;
}
Relevant https://ideone.com/utZqjk.
Credits to dyreshark on the freenode IRC #c++.
template<
typename HTYPE,
typename = std::enable_if_t<std::is_same<decltype(HTYPE::var1), decltype(HTYPE::var1)>::value>
>
static void close_release
(HTYPE* ptr) {
ptr->var1;
}
Using enable_if and decltype to let compiler to check variable, hope to help.
This has become way easier with C++11.
template <typename T> struct Model
{
vector<T> vertices;
void transform( Matrix m )
{
for(auto &&vertex : vertices)
{
vertex.pos = m * vertex.pos;
modifyNormal(vertex, m, special_());
}
}
private:
struct general_ {};
struct special_ : general_ {};
template<typename> struct int_ { typedef int type; };
template<typename Lhs, typename Rhs,
typename int_<decltype(Lhs::normal)>::type = 0>
void modifyNormal(Lhs &&lhs, Rhs &&rhs, special_) {
lhs.normal = rhs * lhs.normal;
}
template<typename Lhs, typename Rhs>
void modifyNormal(Lhs &&lhs, Rhs &&rhs, general_) {
// do nothing
}
};
Things to note:
decltype
and sizeof
without needing an object. I know that it's little late, however...
typedef int Matrix;
struct NormalVertex {
int pos;
int normal;
};
struct Vertex {
int pos;
};
template <typename T> struct Model
{
typedef int No;
typedef char Yes;
template<typename U> static decltype (declval<U>().normal, Yes()) has_normal(U a);
static No has_normal(...);
vector<T> vertices ;
template <typename U = T>
typename enable_if<sizeof(has_normal(declval<U>())) == sizeof(Yes), void>::type
transform( Matrix m )
{
std::cout << "has .normal" << std::endl;
for (auto vertex : vertices)
{
vertex.pos = m * vertex.pos ;
vertex.normal = m * vertex.normal ;
}
}
template <typename U = T>
typename enable_if<sizeof(has_normal(declval<U>())) == sizeof(No), void>::type
transform( Matrix m )
{
std::cout << "has no .normal" << std::endl;
for (auto vertex : vertices)
{
vertex.pos = m * vertex.pos ;
}
}
} ;
int main()
{
Matrix matrix;
Model <NormalVertex> normal_model;
Vertex simple_vertex;
Model <Vertex> simple_model;
simple_model.transform(matrix);
normal_model.transform(matrix);
return 0;
}