Reputation: 556
So Here is the simplified problem, suppose We have two types
struct Shape2D
{
Vec2 Position;
};
struct Shape3D
{
Vec3 Position;
};
I want to create a template class test
template<class T>
class Test
{
public:
int method1()
{
//return 1 if member T::Position is Vec2,
//return 2 if member T::Position is Vec3
}
}
such that the following code works
Test<A> ta;
Test<B> tb;
assert(ta.method1() == 1);
assert(tb.method1() == 2);
The real context of this problem is for an OpenGL Engine. I want to be able to use the same serialization class for 2D and 3D Vertices Data without having to write a 3D and 2D version.
Upvotes: 1
Views: 5283
Reputation: 50568
You can use a couple of function declarations (definitions are not required in this case), std::declval
and std::integral_constant
to solve it at compile-time.
It follows a minimal, working example:
#include<type_traits>
#include<utility>
struct Vec2 {};
struct Vec3{};
struct Shape2D { Vec2 Position; };
struct Shape3D { Vec3 Position; };
template<class T>
class Test {
static constexpr std::integral_constant<int, 1> method1(Vec2);
static constexpr std::integral_constant<int, 2> method1(Vec3);
public:
constexpr int method1() {
return decltype(method1(std::declval<T>().Position))::value;
}
};
int main() {
Test<Shape2D> ta;
Test<Shape3D> tb;
static_assert(ta.method1() == 1, "!");
static_assert(tb.method1() == 2, "!");
}
The solution above fails to compile ifT
hasn't a data member named Position
the type of which is either Vec2
or Vec3
.
Another possible approach that requires a default value instead could be this:
constexpr int method1() {
return
(std::is_same<decltype(std::declval<T>().Position), Vec2>::value
? 1 : (std::is_same<decltype(std::declval<T>().Position), Vec3>::value
? 2 : 0));
}
That is the ternary operator used in conjunction with std::is_same
, nothing more.
If you can use C++17, you can also base your solution on if/else constexpr
:
constexpr int method1() {
if constexpr(std::is_same_v<decltype(std::declval<T>().Position), Vec2>) {
return 1;
} else if constexpr(std::is_same_v<decltype(std::declval<T>().Position), Vec3>) {
return 2;
}
}
Upvotes: 1
Reputation: 30694
There are a few ways to approach this.
The simplest would be to just use normal overload resolution:
template<class T>
class Test
{
private:
T myT;
int internal(Vec2)
{
return 1;
}
int internal(Vec3)
{
return 2;
}
public:
Test() : myT{} {}
int method1()
{
return internal(myT.Position);
}
};
This requires that you actually have an instance of T
. If you don't, then you'll need to use a template based approach. That's a fairly deep topic, but one approach that does what you want in your example is this:
template <typename T>
int internal();
template <>
int internal<Vec2>()
{
return 1;
}
template <>
int internal<Vec3>()
{
return 2;
}
template<class T>
class Test
{
public:
int method1()
{
return internal<decltype(T::Position)>();
}
};
Upvotes: 2
Reputation: 144
You can have some oveload function to run different code, say you have int func (shape 2D x)
and int func (shape3D x)
, so int your method1
you just call func (T.position)
and compiler will help you to resolve the call.
Upvotes: 0