Last active
January 16, 2021 08:44
-
-
Save hutorny/864f83eb716274167168e4e7336d941b to your computer and use it in GitHub Desktop.
Multimethods for C++, example 2, calculus multifunction
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"); | |
} | |
}; | |
template<typename ReturnType, auto ... Entries> | |
struct multimethod { | |
template<class ... Arguments> | |
static auto dispatch(Arguments& ... arguments) { | |
ReturnType value; | |
if (((details::entry<Entries>::matches(arguments...) | |
and ((value = details::entry<Entries>::call(arguments...)),true)) or ...)) | |
return value; | |
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; } | |
inline Float* subf(const Float&, const Float&) { TRACE(); return nullptr; } | |
inline Integer* subi(const Integer&, const Integer&) { TRACE(); return nullptr; } | |
template<> | |
inline Expression* add<Expression,Expression>(const Expression& a, const Expression& b) { | |
return multimethod<Expression*, | |
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 multimethod<Expression*, | |
subi, | |
sub<Float, Integer>, | |
sub<Integer, Float>, | |
subf>::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