I have come across the problem of needing a template function that will have the same output so long as it's template parameters are the same, independent of position. Given that there are always two parameters.
I have a function:
template<typename Lhs, typename Rhs>
int func();
I would like func<int, float>()
and func<float, int>()
to call the same code.
I thought about a macro, which I like to avoid, but I need to not duplicate code when the two types are the same. So a macro such as:
#define DEF_TEMPL_IMPL(lhs, rhs, ret) \
template<>\
auto func<lhs, rhs>(){ return ret; }\
template<>\
auto func<rhs, lhs>(){ return func<lhs, rhs>(); }
will fail to compile because DEF_TEMPL_IMPL(float, float, 3)
would cause a redefinition of func<>
I think SFINAE is the answer here, but can't quite think of a solution.
I will continue to ponder this, but some of the great minds of stack overflow might have a much better or more elegant solution than I can come up with before this question is answered.
So how can this be achieved?
You could just write one specialization for each type pair, then make your primary template delegate to func<Rhs,Lhs>()
if it gets called:
//This will get called if no specializations match
template<typename Lhs, typename Rhs>
int func() {
//Check if there is a specialization for the flipped pair
return func<Rhs, Lhs>();
}
//This will get called for <int,float> and <float,int>
template <>
int func<int,float>() {
return 42;
}
//Ditto for <bool,float> and <float,bool>
template <>
int func<bool,float>() {
return 0xbeef;
}
//Specializations with the same arguments are also supported by this scheme
template <>
int func<bool,bool>() {
return 12;
}
Live Demo
Here's another possible solution
template<typename A, typename B>
struct List { };
template<typename A, typename B>
struct Set {
Set(List<A, B>) { }
Set(List<B, A>) { }
};
template<typename A>
struct Set<A, A> {
Set(List<A, A>) { }
};
Now, write each as an overload for Set<A, B>
, like this
template<typename Lhs, typename Rhs>
int func() {
return func(List<Lhs, Rhs>());
}
int func(Set<int, float>) {
return 42;
}
int func(Set<bool, float>) {
return 0xbeef;
}
int func(Set<bool, bool>) {
return 12;
}
This way, you can also define func(List<float, int>)
, if for some reason, the order for float, int
is important. If you accidentally defined both Set<int,float>
and Set<float, int>
, then the call would be ambiguous.
You can also have it validate at the time of definition of the overloads that you don't have duplicate Set<A, B>
vs Set<B, A>
defined by changing the definition of the binary Set
to this
template<typename A, typename B>
struct Set {
Set(List<A, B>) { }
Set(List<B, A>) { }
friend void dupeCheck(List<A, B>) { }
friend void dupeCheck(List<B, A>) { }
};
This however is not strictly necessary for this trick. Especially since the types in your JIT compiler may be easily manually ordered in a consistent way.
You can define the order of types (i.e. int
- 1, float
- 2, char
- 3).
Then implement a function that will sort these two types:
#include <iostream>
template<typename T>
struct Order;
// There should be a better solution for getting type id in compile time
// This will let you specify the order
template<>
struct Order<int>
{
enum { Value = 1 };
};
template<>
struct Order<float>
{
enum { Value = 2 };
};
template<>
struct Order<char>
{
enum { Value = 3 };
};
template<typename A, typename B, bool swap>
struct Sort;
template<typename A, typename B>
struct Sort<A, B, false>
{
typedef A First;
typedef B Second;
};
template<typename A, typename B>
struct Sort<A, B, true>
{
typedef B First;
typedef A Second;
};
template<typename Lhs, typename Rhs>
int func_sorted()
{
return 1;
}
template<>
int func_sorted<int, float>()
{
return 2;
}
template<>
int func_sorted<float, int>()
{
return 3;
}
template<typename Lhs, typename Rhs>
int func()
{
typedef typename Sort<
Lhs,
Rhs,
((int)Order<Lhs>::Value > (int)Order<Rhs>::Value)>::First First;
typedef typename Sort<
Lhs,
Rhs,
((int)Order<Lhs>::Value > (int)Order<Rhs>::Value)>::Second Second;
return func_sorted<First, Second>();
}
so following code
int main()
{
std::cout << func<int, float>() << std::endl;
std::cout << func<float, int>() << std::endl;
}
will print:
2
2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With