Created
January 16, 2021 08:40
-
-
Save hutorny/e5785a34a1de5d5e56b9bcfa7fa092b0 to your computer and use it in GitHub Desktop.
Multimethods for C++, example 1, calculus multidispatcher
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 <stdexcept> | |
#define TRACE() \ | |
std::cout << __PRETTY_FUNCTION__ << std::endl; | |
namespace details { | |
template<class Parameter> | |
struct expected { | |
template<class Argument> | |
static constexpr bool matches(const Argument& argument) noexcept { | |
return typeid(Parameter) == typeid(argument); | |
} | |
}; | |
template<auto Method> | |
struct entry; | |
template<class Return, class ... Parameter, Return (*Function)(Parameter...)> | |
struct entry<Function> { | |
template<class ... Argument> | |
static constexpr bool matches(const Argument& ... argument) noexcept { | |
return (expected<Parameter>::matches(argument) and ...); | |
} | |
template<class ... Argument> | |
static Return call(Argument& ... argument) noexcept(noexcept(Function)) { | |
return (*Function)((Parameter)(argument)...); | |
} | |
}; | |
} //namespace details | |
template<auto Entry, auto ... Entries> | |
struct multidispatcher { | |
template<class ... Arguments> | |
static auto dispatch(Arguments& ... arguments) { | |
if (details::entry<Entry>::matches(arguments...)) { | |
return details::entry<Entry>::call(arguments...); | |
} | |
if constexpr (sizeof...(Entries)>0) { | |
return multidispatcher<Entries...>::dispatch(arguments...); | |
} | |
throw std::logic_error("Dispatcher failure"); | |
} | |
}; | |
namespace calculus { | |
struct Expression { | |
virtual ~Expression() {} | |
}; | |
struct Constant : Expression {}; | |
struct Integer : Constant {}; | |
struct Float : Constant {}; | |
template<class A, class B> | |
Expression* add(const A&, const B&); | |
template<class A, class B> | |
Expression* sub(const A&, const B&); | |
template<> | |
inline Expression* add<Integer, Integer>(const Integer& a, const Integer& b) { TRACE(); return nullptr; } | |
template<> | |
inline Expression* add<Float, Float>(const Float& a, const Float& b) { TRACE(); return nullptr; } | |
template<> | |
inline Expression* add<Float, Integer>(const Float& a, const Integer& b) { TRACE(); return nullptr; } | |
template<> | |
inline Expression* add<Integer, Float>(const Integer& a, const Float& b) { TRACE(); return nullptr; } | |
template<> | |
inline Expression* sub<Integer, Integer>(const Integer& a, const Integer& b) { TRACE(); return nullptr; } | |
template<> | |
inline Expression* sub<Float, Float>(const Float& a, const Float& b) { TRACE(); return nullptr; } | |
template<> | |
inline Expression* sub<Float, Integer>(const Float& a, const Integer& b) { TRACE(); return nullptr; } | |
template<> | |
inline Expression* sub<Integer, Float>(const Integer& a, const Float& b) { TRACE(); return nullptr; } | |
template<> | |
inline Expression* add<Expression,Expression>(const Expression& a, const Expression& b) { | |
return multidispatcher< | |
add<Integer, Integer>, | |
add<Float, Integer>, | |
add<Integer, Float>, | |
add<Float, Float>>::dispatch(a,b); | |
} | |
template<> | |
inline Expression* sub<Expression,Expression>(const Expression& a, const Expression& b) { | |
return multidispatcher< | |
sub<Integer, Integer>, | |
sub<Float, Integer>, | |
sub<Integer, Float>, | |
sub<Float, Float>>::dispatch(a,b); | |
} | |
inline void test(const Expression& a, const Expression& b) { | |
add(a, b); | |
sub(a, b); | |
} | |
inline void test() { | |
Integer a{}; | |
Integer b{}; | |
Float c{}; | |
test(a, b); | |
test(c, b); | |
test(a, c); | |
test(c, c); | |
} | |
} // namespace calculus | |
int main() { | |
calculus::test(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment