Skip to content

Instantly share code, notes, and snippets.

@DasNaCl
Created July 6, 2018 06:40
Show Gist options
  • Save DasNaCl/fa2d0c6a61e98da4af104b7011e4e035 to your computer and use it in GitHub Desktop.
Save DasNaCl/fa2d0c6a61e98da4af104b7011e4e035 to your computer and use it in GitHub Desktop.
/*
Copyright 2018 Matthis Kruse
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <iostream>
#include <set>
#include <type_traits>
namespace me
{
template<class... T>
struct X;
template<class H, class... T>
struct X<H, T...>;
template<class H>
struct X<H>;
// checks whether the given thing is a X or not
template<typename T>
struct is_list : public std::false_type
{ };
template<typename... Args>
struct is_list<X<Args...>> : public std::true_type
{ };
// adds an element to a list
template<class H, class T>
struct cons_helper;
template<class H, class... T>
struct cons_helper<H, X<T...> >
{
using type = X<H, T...>;
};
template<class H, class T>
using cons = typename cons_helper<H, T>::type;
// glues two lists together
template<class H, class T>
struct conc_helper;
template<class... H, class... T>
struct conc_helper<X<H...>, X<T...> >
{
using type = X<H..., T...>;
};
template<class H, class T>
using conc = typename conc_helper<H, T>::type;
// yields the head of a list
template<class H>
struct head_helper;
template<class H, class... Tail>
struct head_helper<X<H, Tail...>>
{
using type = H;
};
template<class H>
using head = typename head_helper<H>::type;
// yields the tail of a list
template<class H>
struct tail_helper;
template<class H, class... Tail>
struct tail_helper<X<H, Tail...>>
{
using type = X<Tail...>;
};
template<class H>
using tail = typename tail_helper<H>::type;
// yields true if the elem is in a list
template<class E, class H>
struct find_helper;
template<class E, class... H>
struct find_helper<E, X<H...>> : public std::conditional_t<std::is_same<E, head<X<H...>>>::value,
std::true_type,
typename find_helper<E, tail<X<H...>>>::type>
{ };
template<class E>
struct find_helper<E, X<>> : public std::false_type
{ };
// yields true if the list has a list as elem
template<class H>
struct find_list;
template<class... H>
struct find_list<X<H...>> : public std::conditional_t<is_list<head<X<H...>>>::value,
std::true_type,
typename find_list<tail<X<H...>>>::type>
{ };
template<>
struct find_list<X<>> : public std::false_type
{ };
// removes an element from a list
template<class E, class H>
struct rm_helper;
template<class E, class... H>
struct rm_helper<E, X<H...>>
{
using type = std::conditional_t<std::is_same<E, head<X<H...>>>::value,
tail<X<H...>>,
cons<head<X<H...>>,
typename rm_helper<E, tail<X<H...>>>::type>
>;
};
template<class E>
struct rm_helper<E, X<>>
{
using type = X<>;
};
template<class E, class H>
using rm = typename rm_helper<E, H>::type;
// eliminates an element from a list
template<class E, class H>
struct elim_helper;
template<class E, class... H>
struct elim_helper<E, X<H...>>
{
using el = typename elim_helper<E, tail<X<H...>>>::type;
using type = std::conditional_t<std::is_same<head<X<H...>>, E>::value,
el, cons<head<X<H...>>, el>
>;
};
template<class E>
struct elim_helper<E, X<>>
{
using type = X<>;
};
template<class E, class H>
using elim = typename elim_helper<E, H>::type;
// makes a set out of the type list
template<class H>
struct make_set_helper;
template<class... H>
struct make_set_helper<X<H...>>
{
using type = cons<head<X<H...>>,
typename make_set_helper<elim<head<X<H...>>, X<H...>>>::type>;
};
template<>
struct make_set_helper<X<>>
{
using type = X<>;
};
template<class H>
using make_set = typename make_set_helper<H>::type;
// computes the diff of two lists : a in A -> not a in inter
template<class H1, class H2>
struct inter_helper;
template<class... H1, class... H2>
struct inter_helper<X<H1...>, X<H2...>>
{
using type = typename inter_helper<tail<X<H1...>>, rm<head<X<H1...>>, X<H2...>>>::type;
};
template<class... H2>
struct inter_helper<X<>, X<H2...>>
{
using type = X<H2...>;
};
template<class H1, class H2>
using inter = typename inter_helper<H1, H2>::type;
// checks whether two lists share the same elements
template<class H1, class H2>
struct same_elements;
template<class... H1, class... H2>
struct same_elements<X<H1...>, X<H2...>>
{
using set1 = make_set<X<H1...>>;
using set2 = make_set<X<H2...>>;
using type1 = inter<set1, set2>;
using type2 = inter<set2, set1>;
static constexpr const bool value = std::is_same<type1, type2>::value;
};
/// Takes a list possibly containing lists and makes a single list out of them
template<class T>
struct universify_helper
{ using type = void; /*ugly*/ };
template<typename... T>
struct universify_helper<X<T...>>
{
using e = std::conditional_t<is_list<head<X<T...>>>::value,
typename universify_helper<head<X<T...>>>::type, X<head<X<T...>>>>;
using type = conc<e, typename universify_helper<tail<X<T...>>>::type>;
};
template<>
struct universify_helper<X<>>
{
using type = X<>;
};
template<class T>
using universify = typename universify_helper<T>::type;
}
namespace crtp
{
using me::X;
using me::head;
using me::tail;
using me::conc;
using me::make_set;
using me::is_list;
using me::universify;
template<typename T>
struct apply_skills_base;
template<typename... Skills>
struct apply_skills_base<X<Skills...>> : public Skills...
{ };
template<typename T>
struct apply_skills_helper;
template<typename... T>
struct apply_skills_helper<X<T...>>
{
using hd = head<X<T...>>;
using tl = tail<X<T...>>;
using list = std::conditional_t<is_list<hd>::value, hd, X<hd>>;
using type0 = conc<list, typename apply_skills_helper<tl>::type0>;
using type = make_set<type0>;
};
template<>
struct apply_skills_helper<X<>>
{
using type0 = X<>;
};
template<template<template<typename> typename...> typename Derived, template<typename> typename... Skills>
using apply_skills_detail = typename apply_skills_helper<universify<X<Skills<Derived<Skills...>>...>>>::type;
template<template<template<typename> typename...> typename Derived, template<typename> typename... Skills>
using apply_skills = apply_skills_base<apply_skills_detail<Derived, Skills...>>;
template<typename Derived, template<typename> typename... Skills>
using skill_packer = X<Skills<Derived>...>;
}
template<typename Derived>
class ExtraFeature1
{
public:
void extraMethod1()
{
auto derived = static_cast<Derived&>(*this);
std::cout << "extra feature 1\n";
derived.basicMethod();
}
};
template<typename Derived>
class ExtraFeature2
{
public:
void extraMethod2()
{
auto derived = static_cast<Derived&>(*this);
std::cout << "extra feature 2\n";
derived.basicMethod();
}
};
template<typename Derived>
class ExtraFeature3
{
public:
void extraMethod3()
{
auto derived = static_cast<Derived&>(*this);
std::cout << "extra feature 3\n";
derived.basicMethod();
}
};
template<typename Derived>
using ExtraFeaturesA = crtp::skill_packer<Derived, ExtraFeature1, ExtraFeature2>;
template<typename Derived>
using ExtraFeaturesB = crtp::skill_packer<Derived, ExtraFeature2, ExtraFeature3>;
template<typename Derived>
using ExtraFeaturesC = crtp::skill_packer<Derived, ExtraFeaturesA, ExtraFeature1>;
template<template<typename> typename... Skills>
class XYZ : public crtp::apply_skills<XYZ, Skills...>
{
public:
void basicMethod(){};
};
int main()
{
using XAB = XYZ<ExtraFeaturesA, ExtraFeaturesB, ExtraFeaturesC>;
XAB x;
x.extraMethod1();
x.extraMethod2();
x.extraMethod3();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment