Approach
First, let us consider all the pair-combinations of the primitive types corresponding enumerators of the enum class Value::Type
.
Since all enumerators of Value::Type
are not explicitly initialized, the first enumerator has the value of 0
, the second one has the value of 1
, and so on. Using these zero-starting integers, we can label all the type combinations by the continuous zero-starting integers as follows:
Live DEMO
std::pair<int , int > --> 4*int_type + int_type = 4*0+0 = 0
std::pair<int , float > --> 4*int_type + float_type = 4*0+1 = 1
std::pair<int , double> --> 4*int_type + double_type = 4*0+2 = 2
... ...
std::pair<bool, bool > --> 4*bool_type + bool_type = 4*3+3 = 15
Next, we introduce the following static member function template Value::check
which provides a generic comparison for each type combination:
template<class T>
static bool check(const Value& lhs, const Value& rhs)
{
return *static_cast<typename T::first_type*>(lhs.ptr)
== *static_cast<typename T::second_type*>(rhs.ptr);
}
For instance, if T = std::pair<int, float>
, this becomes the comparison of int
and float
which you write in your post.
Then I would like to propose the following O(1) approach.
At compile-time, we construct the following array which stores the pointer to function and arr[i]
is the pointer to check<T>
where T
is the i
-th type of the above type combinations:
using comp_f = bool(*)(const Value& lhs, const Value& rhs);
comp_f arr[16] = { &check<std::pair<int, int>>, &check<std::pair<int, float>>, ... };
At run-time, given Value& lhs
and Value& rhs
, we calculate the corresponding index and call the appropriately instantiated function check<T>
as follows.
This process can be done with O(1) complexity:
std::size_t idx = 4*static_cast<std::size_t>(lhs.type)
+ static_cast<std::size_t>(rhs.type); // 0 ~ 15.
return arr[idx](lhs, rhs);
Combinatorics
Now our problem is how we can simply construct all the type combinations.
I have answered to the almost same question with this problem.
In the current case, applying this method, all the possible combinations can be generated by the following struct Combinations
(and max66's approach also would be possible).
Please note that here I use std::index_sequence
and thus this works in C++14 and over.
But there are various way to implement std::index_sequence
in C++11:
template<std::size_t I, class Tuple>
using pairing = std::pair<
typename std::tuple_element<I/std::tuple_size<Tuple>::value, Tuple>::type,
typename std::tuple_element<I%std::tuple_size<Tuple>::value, Tuple>::type>;
template <class T, class Is>
struct make_combinations;
template <class Tuple, std::size_t... Is>
struct make_combinations<Tuple, std::index_sequence<Is...>>
{
using pairs = std::tuple<pairing<Is, Tuple>...>;
};
template<class ...Args>
struct Combinations
{
using types_tuple = typename make_combinations
<std::tuple<Args...>,
std::make_index_sequence<(sizeof...(Args))*(sizeof...(Args))>
>::pairs;
};
Using this Combinations
, we can generate the tuple of all the type combinations as Combinations<int, float, double, bool>::types_tuple
.
Live DEMO
Comparator
In summary, Variable::operator==
is implemented as follows.
Here make_comparator
generates the struct comparator
at compile-time passing all the type combinations to it's template parameter.
comparator
also create the array of the pointer to function check<T>
at compile-time.
Thus the comparison of two Value
would be done with O(1) complexity:
Live DEMO
template<std::size_t N, class T>
struct comparator {};
template<std::size_t N, class... Combs>
struct comparator<N, std::tuple<Combs...>>
{
using comp_f = bool(*)(const Value& lhs, const Value& rhs);
const comp_f arr[sizeof...(Combs)];
public:
constexpr comparator() : arr{ &check<Combs>... }
{}
bool operator()(const Value& lhs, const Value& rhs) const
{
const std::size_t idx = N*static_cast<std::size_t>(lhs.type)
+ static_cast<std::size_t>(rhs.type);
return arr[idx](lhs, rhs);
}
};
template<class... Ts>
static constexpr auto make_comparator()
{
return comparator<sizeof...(Ts), typename Combinations<Ts...>::types_tuple>();
}
friend bool operator==(const Value& lhs, const Value& rhs)
{
constexpr auto comp = make_comparator<int, float, double, bool>();
return comp(lhs, rhs);
}