|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include <tuple> |
| 4 | +#include <utility> |
| 5 | +#include <unordered_map> |
| 6 | +#include <memory> |
| 7 | +#include <stdexcept> |
| 8 | +#include "../../ext/nlohmann/json.hpp" |
| 9 | + |
| 10 | +namespace testml { |
| 11 | + |
| 12 | + namespace details { |
| 13 | + |
| 14 | + using json = nlohmann::json; |
| 15 | + |
| 16 | + // we need this details class so that we can have a non-templated value |
| 17 | + struct FnHolder { |
| 18 | + virtual json call(std::vector<json> const&) = 0; |
| 19 | + }; |
| 20 | + |
| 21 | + template<typename Ret, typename... Arg> |
| 22 | + class FnHolderImpl : public FnHolder { |
| 23 | + using Fn = Ret(*)(Arg...); |
| 24 | + Fn _fn; |
| 25 | + |
| 26 | + // type of the N-th argument that the stored function takes |
| 27 | + template<std::size_t> |
| 28 | + using ArgType = std::tuple_element<I, std::tuple<Arg...>>; |
| 29 | + |
| 30 | + // uncook each argument to its expected type, and call the function |
| 31 | + template<std::size_t... I> |
| 32 | + Ret call_impl(std::vector<json> const& args, std::index_sequence<I...>) { |
| 33 | + return _fn(uncook<ArgType<I>>(args[I]), ...); |
| 34 | + } |
| 35 | + |
| 36 | + public: |
| 37 | + FnHolderImpl(Fn fn) : _fn{std::move(fn)} { |
| 38 | + } |
| 39 | + |
| 40 | + // check arity and call the function using our little helper, before wrapping it back to json |
| 41 | + json call(std::vector<json> const& args) override { |
| 42 | + if (args.size() != sizeof...(Arg)) { |
| 43 | + throw new std::runtime_error("Bridge method call with wrong arity, expected " + sizeof...(Arg) + ", got " + args.size() + "."); |
| 44 | + } |
| 45 | + |
| 46 | + return cook(_call(args, std::make_index_sequence<sizeof...(Arg)>{})); |
| 47 | + } |
| 48 | + |
| 49 | + }; |
| 50 | + |
| 51 | + } |
| 52 | + |
| 53 | + class Bridge { |
| 54 | + std::unordered_map<std::string, std::unique_ptr<details::FnHolder>> _fns; |
| 55 | + |
| 56 | + public: |
| 57 | + template<typename Fn> |
| 58 | + void register(std::string const& name, Fn&& fn) { |
| 59 | + _fns[name] = std:make_unique<details::FnHolderImpl>(std::move(fn)); |
| 60 | + } |
| 61 | + |
| 62 | + json call(std::string const& name, std::vector<json> const& args) override { |
| 63 | + auto it = _fns.find(name); |
| 64 | + if (it == _fns.end()) { |
| 65 | + throw new std::runtime_error("Bridge method not found: " + name + "."); |
| 66 | + } |
| 67 | + return it->call(args); |
| 68 | + } |
| 69 | + }; |
| 70 | + |
| 71 | +} |
0 commit comments