diff --git a/src/client.cpp b/src/client.cpp index 7bb11d6..2c354ac 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,9 +1,48 @@ #include "MyService.proxy.h" +#include "rpc/RpcChannel.h" + +#include +#include +#include + #include +#include + +class RpcChannelFifoClient : public RpcChannel { +public: + RpcChannelFifoClient(const char* inPipe, const char* outPipe) { + fdOut = open(inPipe, O_WRONLY); + fdIn = open(outPipe, O_RDONLY); + } + + void send(const std::string& data) override { + ::write(fdOut, data.c_str(), data.size()); + ::write(fdOut, "\n", 1); + } + + std::string receive() override { + char buf[4096]; + int n = ::read(fdIn, buf, sizeof(buf) - 1); + if (n <= 0) { + return {}; + } + buf[n] = 0; + return std::string(buf); + } + +private: + int fdIn{}; + int fdOut{}; +}; int main() { - MyServiceProxy proxy("/tmp/rpc_in", "/tmp/rpc_out"); + mkfifo("/tmp/rpc_in", 0666); + mkfifo("/tmp/rpc_out", 0666); + + RpcChannelFifoClient ch("/tmp/rpc_in", "/tmp/rpc_out"); + MyServiceProxy proxy(ch); int r = proxy.add(7, 8); std::cout << "RESULT: " << r << std::endl; } + diff --git a/src/rpc/RpcChannel.h b/src/rpc/RpcChannel.h new file mode 100644 index 0000000..011198c --- /dev/null +++ b/src/rpc/RpcChannel.h @@ -0,0 +1,11 @@ +#pragma once +#include +#include + +class RpcChannel { +public: + virtual ~RpcChannel() = default; + + virtual void send(const std::string& data) = 0; + virtual std::string receive() = 0; +}; diff --git a/src/rpc/RpcChannelFifo.cpp b/src/rpc/RpcChannelFifo.cpp new file mode 100644 index 0000000..cec1be7 --- /dev/null +++ b/src/rpc/RpcChannelFifo.cpp @@ -0,0 +1,35 @@ +#include "RpcChannel.h" + +#include +#include + +#include + +class RpcChannelFifo : public RpcChannel { +public: + RpcChannelFifo(const char* inPipe, const char* outPipe) { + fdOut = open(inPipe, O_WRONLY); + fdIn = open(outPipe, O_RDONLY); + } + + void send(const std::string& data) override { + ::write(fdOut, data.c_str(), data.size()); + ::write(fdOut, "\n", 1); + } + + std::string receive() override { + char buf[4096]; + int n = ::read(fdIn, buf, sizeof(buf) - 1); + if (n <= 0) { + return {}; + } + buf[n] = 0; + return std::string(buf); + } + +private: + int fdIn{}; + int fdOut{}; +}; + + diff --git a/src/rpc/RpcClient.h b/src/rpc/RpcClient.h new file mode 100644 index 0000000..d6c1f15 --- /dev/null +++ b/src/rpc/RpcClient.h @@ -0,0 +1,37 @@ +#pragma once + +#include "RpcChannel.h" +#include "RpcSerializer.h" + +#include +#include + +class RpcClient { +public: + explicit RpcClient(RpcChannel& ch) : channel(ch) {} + + template + Ret call(const std::string& method, const Args&... args) { + std::ostringstream out; + + // имя метода + RpcSerializer::write(out, method); + + // аргументы + (RpcSerializer::write(out, args), ...); + + // отправить + channel.send(out.str()); + + // получить ответ + std::string resp = channel.receive(); + std::istringstream in(resp); + + return RpcSerializer::read(in); + } + +private: + RpcChannel& channel; +}; + + diff --git a/src/rpc/RpcInvoker.h b/src/rpc/RpcInvoker.h new file mode 100644 index 0000000..b963db3 --- /dev/null +++ b/src/rpc/RpcInvoker.h @@ -0,0 +1,67 @@ +#pragma once + +#include "RpcSerializer.h" + +#include +#include +#include +#include +#include + +class RpcInvoker { +public: + template + void registerMethod(Obj* instance, + const std::string& name, + Ret (Obj::*method)(Args...)) { + handlers[name] = + [instance, method](const std::string& req) -> std::string { + std::istringstream in(req); + + // пропустить имя метода + std::string skip; + in >> skip; + + // читать аргументы и вызвать метод + return callMethod(instance, method, in); + }; + } + + std::string dispatch(const std::string& request) { + std::istringstream in(request); + std::string method; + in >> method; + + auto it = handlers.find(method); + if (it == handlers.end()) { + return "ERR"; + } + + return it->second(request); + } + +private: + template + static std::string callMethod(Obj* obj, + Ret (Obj::*method)(Args...), + std::istringstream& in) { + auto tuple = readArgs(in); + Ret result = + std::apply(method, std::tuple_cat(std::make_tuple(obj), tuple)); + + std::ostringstream out; + RpcSerializer::write(out, result); + return out.str(); + } + + template + static std::tuple readArgs(std::istringstream& in) { + return std::tuple{RpcSerializer::read(in)...}; + } + + std::unordered_map> + handlers; +}; + + diff --git a/src/rpc/RpcSerializer.h b/src/rpc/RpcSerializer.h new file mode 100644 index 0000000..d1c036a --- /dev/null +++ b/src/rpc/RpcSerializer.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +class RpcSerializer { +public: + template + static void write(std::ostringstream& out, const T& v) { + out << v << ' '; + } + + template + static T read(std::istringstream& in) { + T v; + in >> v; + return v; + } +}; + + diff --git a/src/server.cpp b/src/server.cpp index eb0789f..e8340dc 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1,31 +1,58 @@ #include "MyService.h" #include "MyService.skeleton.h" +#include "rpc/RpcChannel.h" + #include #include #include + #include +#include + +class RpcChannelFifoServer : public RpcChannel { +public: + RpcChannelFifoServer(const char* inPipe, const char* outPipe) { + fdIn = open(inPipe, O_RDONLY); + fdOut = open(outPipe, O_WRONLY); + } + + void send(const std::string& data) override { + ::write(fdOut, data.c_str(), data.size()); + ::write(fdOut, "\n", 1); + } + + std::string receive() override { + char buf[4096]; + int n = ::read(fdIn, buf, sizeof(buf) - 1); + if (n <= 0) { + return {}; + } + buf[n] = 0; + return std::string(buf); + } + +private: + int fdIn{}; + int fdOut{}; +}; int main() { mkfifo("/tmp/rpc_in", 0666); mkfifo("/tmp/rpc_out", 0666); - int fdIn = open("/tmp/rpc_in", O_RDONLY); - int fdOut = open("/tmp/rpc_out", O_WRONLY); + RpcChannelFifoServer ch("/tmp/rpc_in", "/tmp/rpc_out"); MyService realObj; MyServiceSkeleton skeleton(realObj); - char buf[256]; - while (true) { - int n = read(fdIn, buf, sizeof(buf)-1); - if (n <= 0) break; - - buf[n] = 0; - std::string req(buf); - std::string resp = skeleton.handleRequest(req); - - write(fdOut, resp.c_str(), resp.size()); + std::string req = ch.receive(); + if (req.empty()) { + break; + } + std::string resp = skeleton.dispatch(req); + ch.send(resp); } } + diff --git a/tools/templates/proxy.cpp.j2 b/tools/templates/proxy.cpp.j2 index 0fcef15..ace90e9 100644 --- a/tools/templates/proxy.cpp.j2 +++ b/tools/templates/proxy.cpp.j2 @@ -1,38 +1,20 @@ #include "{{ cls.name }}.proxy.h" -#include -#include -#include -#include -#include +#include "rpc/RpcClient.h" -{{ cls.name }}Proxy::{{ cls.name }}Proxy(const char* pipeIn, const char* pipeOut) { - fdIn = open(pipeIn, O_WRONLY); - if (fdIn < 0) { - perror("open pipeIn"); - } - fdOut = open(pipeOut, O_RDONLY); - if (fdOut < 0) { - perror("open pipeOut"); - } -} +class {{ cls.name }}Proxy::Impl { +public: + explicit Impl(RpcChannel& ch) + : client(ch) {} + + RpcClient client; +}; + +{{ cls.name }}Proxy::{{ cls.name }}Proxy(RpcChannel& ch) + : impl(new Impl(ch)) {} {% for m in cls.methods %} {{ m.return_type }} {{ cls.name }}Proxy::{{ m.name }}({% for a in m.args %}{{ a.type }} {{ a.name }}{% if not loop.last %}, {% endif %}{% endfor %}) { - std::ostringstream out; - out << "{{ m.name }}"; - {% for a in m.args %} - out << " " << {{ a.name }}; - {% endfor %} - out << "\n"; - std::string s = out.str(); - write(fdIn, s.c_str(), (ssize_t)s.size()); - - char buf[256]; - ssize_t n = read(fdOut, buf, sizeof(buf)-1); - if (n <= 0) { - return 0; - } - buf[n] = '\0'; - return std::atoi(buf); + return impl->client.call<{{ m.return_type }}>("{{ cls.name }}.{{ m.name }}"{% for a in m.args %}, {{ a.name }}{% endfor %}); } {% endfor %} + diff --git a/tools/templates/proxy.h.j2 b/tools/templates/proxy.h.j2 index 8c41c2a..2e1679a 100644 --- a/tools/templates/proxy.h.j2 +++ b/tools/templates/proxy.h.j2 @@ -1,15 +1,15 @@ #pragma once -#include -#include -#include + +#include "rpc/RpcChannel.h" class {{ cls.name }}Proxy { public: - {{ cls.name }}Proxy(const char* pipeIn, const char* pipeOut); + explicit {{ cls.name }}Proxy(RpcChannel& ch); {% for m in cls.methods %} {{ m.return_type }} {{ m.name }}({% for a in m.args %}{{ a.type }} {{ a.name }}{% if not loop.last %}, {% endif %}{% endfor %}); {% endfor %} + private: - int fdIn; - int fdOut; + class Impl; + Impl* impl; }; diff --git a/tools/templates/skeleton.cpp.j2 b/tools/templates/skeleton.cpp.j2 index 26b7023..d45e928 100644 --- a/tools/templates/skeleton.cpp.j2 +++ b/tools/templates/skeleton.cpp.j2 @@ -1,19 +1,15 @@ #include "{{ cls.name }}.skeleton.h" -#include -{{ cls.name }}Skeleton::{{ cls.name }}Skeleton({{ cls.name }}& o) : obj(o) {} +{{ cls.name }}Skeleton::{{ cls.name }}Skeleton({{ cls.name }}& obj) +{ +{% for m in cls.methods %} + invoker.registerMethod(&obj, + "{{ cls.name }}.{{ m.name }}", + &{{ cls.name }}::{{ m.name }}); +{% endfor %} +} -std::string {{ cls.name }}Skeleton::handleRequest(const std::string& req) { - std::istringstream in(req); - std::string method; - in >> method; - {% for m in cls.methods %} - if (method == "{{ m.name }}") { - {% for a in m.args %}int {{ a.name }}; in >> {{ a.name }}; - {% endfor %} - int res = obj.{{ m.name }}({% for a in m.args %}{{ a.name }}{% if not loop.last %}, {% endif %}{% endfor %}); - return std::to_string(res); - } - {% endfor %} - return std::string("ERR"); +std::string {{ cls.name }}Skeleton::dispatch(const std::string& req) { + return invoker.dispatch(req); } + diff --git a/tools/templates/skeleton.h.j2 b/tools/templates/skeleton.h.j2 index f2d2936..619f350 100644 --- a/tools/templates/skeleton.h.j2 +++ b/tools/templates/skeleton.h.j2 @@ -1,11 +1,16 @@ #pragma once + #include "{{ cls.name }}.h" +#include "rpc/RpcInvoker.h" + #include class {{ cls.name }}Skeleton { public: - {{ cls.name }}Skeleton({{ cls.name }}& obj); - std::string handleRequest(const std::string& req); + explicit {{ cls.name }}Skeleton({{ cls.name }}& obj); + + std::string dispatch(const std::string& req); + private: - {{ cls.name }}& obj; + RpcInvoker invoker; };