#include <boost/mpl/if.hpp>
#include <boost/type_traits/is_base_of.hpp>
namespace fruit
{
struct apple
{
typedef void sourness;
std::string name;
apple(std::string const& n) : name(n) {}
};
struct pear
{
typedef void juiciness;
std::string other_name;
pear(std::string const& n) : other_name(n) {}
};
struct orange
{
std::string name;
orange(std::string const& n) : name(n) {}
};
struct lime
{
std::string name;
lime(std::string const& n) : name(n) {}
};
struct banana
{
std::string name;
banana(std::string const& n) : name(n) {}
};
struct citrus_tag {};
struct pome_tag {};
struct apple_tag : pome_tag {};
struct pear_tag : pome_tag {};
struct banana_tag {};
struct orange_tag : citrus_tag {};
struct lemon_tag : citrus_tag {};
struct lime_tag : citrus_tag {};
// Attach tags:
template <typename T> struct tag {};
template <> struct tag<apple> { typedef apple_tag type; };
template <> struct tag<pear> { typedef pear_tag type; };
template <> struct tag<orange> { typedef orange_tag type; };
template <> struct tag<lime> { typedef lime_tag type; };
template <> struct tag<banana> { typedef banana_tag type; };
//First version, without list:
/*
template <typename Tag, typename BaseTag>
struct tag_cast
{
typedef typename boost::mpl::if_
<
typename boost::is_derived<Tag, BaseTag>::type,
BaseTag,
Tag
>::type type;
};
*/
// tag_cast: downcasts a tag to a specified base-tag,
// or to the first encountered in a list
template
<
typename Tag, typename BaseTag = void,
typename BT2 = void, typename BT3 = void, typename BT4 = void,
typename BT5 = void, typename BT6 = void, typename BT7 = void
>
struct tag_cast
{
typedef typename boost::mpl::if_
<
typename boost::is_base_of<BaseTag, Tag>::type,
BaseTag,
// Try next one in line:
typename tag_cast<Tag, BT2, BT3, BT4, BT5, BT6, BT7, void>::type
>::type type;
};
template <typename Tag>
struct tag_cast<Tag, void, void, void, void, void, void, void>
{
// If not found, take specified tag, so do not cast
typedef Tag type;
};
namespace dispatch
{
template <typename T> struct has_vesicles : boost::false_type {};
template <> struct has_vesicles<citrus_tag> : boost::true_type {};
template <typename T> struct species
{
static std::string apply() { return "unknown"; }
};
template <> struct species<citrus_tag>
{
static std::string apply() { return "citrus"; }
};
template <> struct species<pome_tag>
{
static std::string apply() { return "pome"; }
};
template <typename T>
struct eat {};
template <> struct eat<apple_tag>
{
static void apply(apple const& a)
{
std::cout << "apple: " << a.name << std::endl;
}
};
template <> struct eat<pear_tag>
{
static void apply(pear const& p)
{
std::cout << "pear: " << p.other_name << std::endl;
}
};
} // ns dispatch
// Generic functions
template <typename Fruit>
void eat(Fruit const& fruit)
{
dispatch::eat<typename tag<Fruit>::type>::apply(fruit);
}
template <typename Fruit>
std::string has_vesicles(Fruit const& fruit)
{
// (Potentially) go up in hierachy: take tag corresponding to Fruit,
// downcast to citrus_tag if possible
typedef typename tag_cast<typename tag<Fruit>::type, citrus_tag>::type tag;
return std::string("has vesicles: ")
+ (dispatch::has_vesicles<tag>::value ? "true" : "false");
}
template <typename Fruit>
std::string species(Fruit const& fruit)
{
typedef typename tag_cast<typename tag<Fruit>::type, citrus_tag, pome_tag>::type tag;
return std::string("species: ") + dispatch::species<tag>::apply();
}
} // ns fruit
int main()
{
using namespace fruit;
apple a("my apple");
pear p("my pear");
eat(a);
eat(p);
orange o("my orange");
std::cout << has_vesicles(a) << std::endl;
std::cout << has_vesicles(p) << std::endl;
std::cout << has_vesicles(o) << std::endl;
banana b("my banana");
std::cout << species(a) << std::endl;
std::cout << species(p) << std::endl;
std::cout << species(o) << std::endl;
std::cout << species(b) << std::endl;
return 0;
}