Last active
August 29, 2015 14:12
-
-
Save kris7t/09f83dd122010a7a46dc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef DISTANCES_H_ | |
#define DISTANCES_H_ | |
#include <map> | |
#include <typeindex> | |
#include <typeinfo> | |
#include <utility> | |
using Distance = double; | |
class Distances; | |
using GetDistanceFunc = Distance (*)(const Distances &, | |
const void *, const void *); | |
template <typename Impl, typename T, typename U> | |
struct GetDistanceImpl { | |
static Distance Get(const Distances &impl, const void *a, const void *b) { | |
return dynamic_cast<const Impl &>(impl).Impl::operator()( | |
*static_cast<const T *>(a), *static_cast<const U *>(b)); | |
} | |
}; | |
template <typename T, typename U> | |
class GetDistance { | |
public: | |
Distance operator()(const T &a, const U &b) const { | |
return func_(*impl_, &a, &b); | |
} | |
private: | |
GetDistance(const Distances *impl, GetDistanceFunc func) | |
: impl_(impl), func_(func) { | |
} | |
const Distances *impl_; | |
GetDistanceFunc func_; | |
friend class Distances; | |
}; | |
class TypePair : public std::pair<std::type_index, std::type_index> { | |
public: | |
using std::pair<std::type_index, std::type_index>::pair; | |
bool operator<(const TypePair &other) const { | |
return first < other.first | |
|| (first == other.first && second < other.second); | |
} | |
}; | |
using GetDistanceMap = std::map<TypePair, GetDistanceFunc>; | |
template <typename Child, typename T, typename U> | |
static void AddToGetDistanceMap(GetDistanceMap &map) { | |
map.insert(std::make_pair(TypePair(typeid(T), typeid(U)), | |
&GetDistanceImpl<Child, T, U>::Get)); | |
} | |
class Distances { | |
public: | |
Distances(const Distances &) = default; | |
Distances(Distances &&) = default; | |
Distances &operator=(const Distances &) = default; | |
Distances &operator=(Distances &&) = default; | |
virtual ~Distances() = default; | |
template <typename T, typename U> | |
GetDistance<T, U> cast() const { | |
auto it = map_->find(TypePair(typeid(T), typeid(U))); | |
if (it == map_->end()) { | |
const Distances *inner = GetInnerDistances(); | |
if (inner == nullptr) { | |
throw std::bad_cast(); | |
} else { | |
return inner->cast<T, U>(); | |
} | |
} else { | |
return GetDistance<T, U>(this, it->second); | |
} | |
} | |
protected: | |
explicit Distances(const GetDistanceMap *map) | |
: map_(map) { | |
} | |
private: | |
virtual const Distances *GetInnerDistances() const { | |
return nullptr; | |
} | |
const GetDistanceMap *map_; | |
}; | |
template <typename Child, typename T, typename... Us> | |
struct PopulateGetDistanceMapImpl2 { | |
static void Populate(GetDistanceMap &) { } | |
}; | |
template <typename Child, typename T, typename U, typename... Us> | |
struct PopulateGetDistanceMapImpl2<Child, T, U, Us...> { | |
static void Populate(GetDistanceMap &map) { | |
if (!std::is_same<const T &, const U &>::value) { | |
AddToGetDistanceMap<Child, T, U>(map); | |
AddToGetDistanceMap<Child, U, T>(map); | |
} | |
PopulateGetDistanceMapImpl2<Child, T, Us...>::Populate(map); | |
} | |
}; | |
template <typename Child, typename... Ts> | |
struct PopulateGetDistanceMapImpl { | |
static void Populate(GetDistanceMap &) { } | |
}; | |
template <typename Child, typename T, typename... Ts> | |
struct PopulateGetDistanceMapImpl<Child, T, Ts...> { | |
static void Populate(GetDistanceMap &map) { | |
AddToGetDistanceMap<Child, T, T>(map); | |
PopulateGetDistanceMapImpl2<Child, T, Ts...>::Populate(map); | |
PopulateGetDistanceMapImpl<Child, Ts...>::Populate(map); | |
} | |
}; | |
template <typename Child, typename... Ts> | |
struct PopulateGetDistanceMap { | |
PopulateGetDistanceMap() { | |
PopulateGetDistanceMapImpl<Child, Ts...>::Populate(map_); | |
} | |
GetDistanceMap map_; | |
}; | |
template <typename Child, typename... Ts> | |
class DistancesImpl { | |
public: | |
static const GetDistanceMap *GetMap() { | |
return &populate_map.map_; | |
} | |
private: | |
static const PopulateGetDistanceMap<Child, Ts...> populate_map; | |
}; | |
template <typename Child, typename... Ts> | |
const PopulateGetDistanceMap<Child, Ts...> | |
DistancesImpl<Child, Ts...>::populate_map; | |
#define DISTANCES_IMPL(...) Distances(DistancesImpl<__VA_ARGS__>::GetMap()) | |
//////////////////////////////////////////////////////////////////////// | |
class IntDistances : public virtual Distances { | |
public: | |
IntDistances() | |
: DISTANCES_IMPL(IntDistances, int) { | |
} | |
virtual Distance operator()(const int &a, const int &b) const { | |
return a - b; | |
} | |
}; | |
class Foo { | |
}; | |
class FooIntDistances : public IntDistances { | |
public: | |
FooIntDistances() | |
: DISTANCES_IMPL(FooIntDistances, Foo, int) { | |
} | |
using IntDistances::operator(); | |
virtual Distance operator()(const int &a, const Foo &b) const { | |
return a; | |
} | |
virtual Distance operator()(const Foo &a, const int &b) const { | |
return operator()(b, a); | |
} | |
virtual Distance operator()(const Foo &a, const Foo &b) const { | |
return 3.14; | |
} | |
}; | |
#endif // DISTANCES_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include "distances.h" | |
int main() { | |
IntDistances *id = new FooIntDistances(); | |
std::cout << (*id)(2, 1) << std::endl; | |
std::cout << id->cast<Foo, int>()(Foo(), 2) << std::endl; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment