Skip to content

Instantly share code, notes, and snippets.

@hutorny
Last active January 16, 2021 08:44
Show Gist options
  • Save hutorny/864f83eb716274167168e4e7336d941b to your computer and use it in GitHub Desktop.
Save hutorny/864f83eb716274167168e4e7336d941b to your computer and use it in GitHub Desktop.
Multimethods for C++, example 2, calculus multifunction
#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