Skip to content

Instantly share code, notes, and snippets.

@jspahrsummers
Last active August 20, 2016 14:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jspahrsummers/0834f93cac6d5efa2418ddddf7244b16 to your computer and use it in GitHub Desktop.
Save jspahrsummers/0834f93cac6d5efa2418ddddf7244b16 to your computer and use it in GitHub Desktop.
Example of using RTTI and template specialization for multiple dispatch in C++
template<typename Left, typename Right>
struct Intersect
{
// This will fail to compile if there's no specialization for Intersect<Right, Left>,
// thereby verifying that we've handled all combinations.
typename Intersect<Right, Left>::Result operator() (const Left &lhs, const Right &rhs) const
{
return Intersect<Right, Left>()(rhs, lhs);
}
};
template<typename Other>
struct Intersect<Any, Other>
{
using Result = std::unique_ptr<Requirement>;
Result operator() (const Any &any, const Other &) const
{
return any.clone();
}
};
template<>
struct Intersect<AtLeast, AtLeast>
{
using Result = std::unique_ptr<Requirement>;
Result operator() (const AtLeast &lhs, const AtLeast &rhs) const
{
return std::make_unique<AtLeast>(std::max(lhs._minimumVersion, rhs._minimumVersion));
}
};
template<>
struct Intersect<AtLeast, CompatibleWith>
{
using Result = std::unique_ptr<Requirement>;
Result operator() (const AtLeast &atLeast, const CompatibleWith &compatibleWith) const
{
// >= 1.2.3 vs ~> 2.0.0
if (atLeast.satisfiedBy(compatibleWith._baseVersion)) {
return compatibleWith.clone();
// ~> 1.2.3 vs >= 1.3
} else if (compatibleWith.satisfiedBy(atLeast._minimumVersion)) {
return std::make_unique<CompatibleWith>(atLeast._minimumVersion, compatibleWith._strictness);
} else {
return nullptr;
}
}
};
template<>
struct Intersect<CompatibleWith, CompatibleWith>
{
using Result = std::unique_ptr<Requirement>;
Result operator() (const CompatibleWith &lhs, const CompatibleWith &rhs) const
{
// ~> 1.2.3 vs ~> 1.4.5
if (lhs.satisfiedBy(rhs._baseVersion)) {
return std::make_unique<CompatibleWith>(rhs._baseVersion, strictestStrictness(lhs._strictness, rhs._strictness));
} else if (rhs.satisfiedBy(lhs._baseVersion)) {
return std::make_unique<CompatibleWith>(lhs._baseVersion, strictestStrictness(lhs._strictness, rhs._strictness));
} else {
return nullptr;
}
}
};
template<typename Other>
struct Intersect<Exactly, Other>
{
using Result = std::unique_ptr<Requirement>;
Result operator() (const Exactly &exactly, const Other &other) const
{
if (other.satisfiedBy(exactly._version)) {
return exactly.clone();
} else {
return nullptr;
}
}
};
template<>
struct Intersect<Exactly, Exactly>
{
using Result = std::unique_ptr<Requirement>;
Result operator() (const Exactly &lhs, const Exactly &rhs) const
{
if (lhs == rhs) {
return lhs.clone();
} else {
return nullptr;
}
}
};
const std::type_info &any = typeid(Any);
const std::type_info &atLeast = typeid(AtLeast);
const std::type_info &compatibleWith = typeid(CompatibleWith);
const std::type_info &exactly = typeid(Exactly);
template<typename Left>
std::unique_ptr<Requirement> intersectRight (const Left &lhs, const Requirement &rhs)
{
if (typeid(rhs) == any) {
return Intersect<Left, Any>()(lhs, dynamic_cast<const Any &>(rhs));
} else if (typeid(rhs) == atLeast) {
return Intersect<Left, AtLeast>()(lhs, dynamic_cast<const AtLeast &>(rhs));
} else if (typeid(rhs) == compatibleWith) {
return Intersect<Left, CompatibleWith>()(lhs, dynamic_cast<const CompatibleWith &>(rhs));
} else if (typeid(rhs) == exactly) {
return Intersect<Left, Exactly>()(lhs, dynamic_cast<const Exactly &>(rhs));
} else {
throw std::invalid_argument("Unrecognized type for requirement: " + toString(rhs));
}
}
std::unique_ptr<Requirement> intersect (const Requirement &lhs, const Requirement &rhs)
{
if (typeid(lhs) == any) {
return intersectRight<Any>(dynamic_cast<const Any &>(lhs), rhs);
} else if (typeid(lhs) == atLeast) {
return intersectRight<AtLeast>(dynamic_cast<const AtLeast &>(lhs), rhs);
} else if (typeid(lhs) == compatibleWith) {
return intersectRight<CompatibleWith>(dynamic_cast<const CompatibleWith &>(lhs), rhs);
} else if (typeid(lhs) == exactly) {
return intersectRight<Exactly>(dynamic_cast<const Exactly &>(lhs), rhs);
} else {
throw std::invalid_argument("Unrecognized type for requirement: " + toString(lhs));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment