Skip to content

Instantly share code, notes, and snippets.

@kris7t
Last active August 29, 2015 14:12
Show Gist options
  • Save kris7t/09f83dd122010a7a46dc to your computer and use it in GitHub Desktop.
Save kris7t/09f83dd122010a7a46dc to your computer and use it in GitHub Desktop.
#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_
#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