disaggregate templates

master
Сергей Маринкевич 2 months ago
parent adbb2c374b
commit 7ce411b643

@ -1,9 +1,48 @@
#include "MyService.proxy.h" #include "MyService.proxy.h"
#include "rpc/RpcChannel.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream> #include <iostream>
#include <string>
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() { 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); int r = proxy.add(7, 8);
std::cout << "RESULT: " << r << std::endl; std::cout << "RESULT: " << r << std::endl;
} }

@ -0,0 +1,11 @@
#pragma once
#include <string>
#include <vector>
class RpcChannel {
public:
virtual ~RpcChannel() = default;
virtual void send(const std::string& data) = 0;
virtual std::string receive() = 0;
};

@ -0,0 +1,35 @@
#include "RpcChannel.h"
#include <fcntl.h>
#include <unistd.h>
#include <string>
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{};
};

@ -0,0 +1,37 @@
#pragma once
#include "RpcChannel.h"
#include "RpcSerializer.h"
#include <sstream>
#include <string>
class RpcClient {
public:
explicit RpcClient(RpcChannel& ch) : channel(ch) {}
template<typename Ret, typename... Args>
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<Ret>(in);
}
private:
RpcChannel& channel;
};

@ -0,0 +1,67 @@
#pragma once
#include "RpcSerializer.h"
#include <functional>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
class RpcInvoker {
public:
template<typename Obj, typename Ret, typename... Args>
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<Ret, Obj, Args...>(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<typename Ret, typename Obj, typename... Args>
static std::string callMethod(Obj* obj,
Ret (Obj::*method)(Args...),
std::istringstream& in) {
auto tuple = readArgs<Args...>(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<typename... Args>
static std::tuple<Args...> readArgs(std::istringstream& in) {
return std::tuple<Args...>{RpcSerializer::read<Args>(in)...};
}
std::unordered_map<std::string,
std::function<std::string(const std::string&)>>
handlers;
};

@ -0,0 +1,21 @@
#pragma once
#include <sstream>
#include <string>
class RpcSerializer {
public:
template<typename T>
static void write(std::ostringstream& out, const T& v) {
out << v << ' ';
}
template<typename T>
static T read(std::istringstream& in) {
T v;
in >> v;
return v;
}
};

@ -1,31 +1,58 @@
#include "MyService.h" #include "MyService.h"
#include "MyService.skeleton.h" #include "MyService.skeleton.h"
#include "rpc/RpcChannel.h"
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <iostream> #include <iostream>
#include <string>
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() { int main() {
mkfifo("/tmp/rpc_in", 0666); mkfifo("/tmp/rpc_in", 0666);
mkfifo("/tmp/rpc_out", 0666); mkfifo("/tmp/rpc_out", 0666);
int fdIn = open("/tmp/rpc_in", O_RDONLY); RpcChannelFifoServer ch("/tmp/rpc_in", "/tmp/rpc_out");
int fdOut = open("/tmp/rpc_out", O_WRONLY);
MyService realObj; MyService realObj;
MyServiceSkeleton skeleton(realObj); MyServiceSkeleton skeleton(realObj);
char buf[256];
while (true) { while (true) {
int n = read(fdIn, buf, sizeof(buf)-1); std::string req = ch.receive();
if (n <= 0) break; if (req.empty()) {
break;
buf[n] = 0; }
std::string req(buf); std::string resp = skeleton.dispatch(req);
std::string resp = skeleton.handleRequest(req); ch.send(resp);
write(fdOut, resp.c_str(), resp.size());
} }
} }

@ -1,38 +1,20 @@
#include "{{ cls.name }}.proxy.h" #include "{{ cls.name }}.proxy.h"
#include <unistd.h> #include "rpc/RpcClient.h"
#include <fcntl.h>
#include <sstream>
#include <cstdlib>
#include <cstring>
{{ cls.name }}Proxy::{{ cls.name }}Proxy(const char* pipeIn, const char* pipeOut) { class {{ cls.name }}Proxy::Impl {
fdIn = open(pipeIn, O_WRONLY); public:
if (fdIn < 0) { explicit Impl(RpcChannel& ch)
perror("open pipeIn"); : client(ch) {}
}
fdOut = open(pipeOut, O_RDONLY); RpcClient client;
if (fdOut < 0) { };
perror("open pipeOut");
} {{ cls.name }}Proxy::{{ cls.name }}Proxy(RpcChannel& ch)
} : impl(new Impl(ch)) {}
{% for m in cls.methods %} {% 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 %}) { {{ 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; return impl->client.call<{{ m.return_type }}>("{{ cls.name }}.{{ m.name }}"{% for a in m.args %}, {{ a.name }}{% endfor %});
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);
} }
{% endfor %} {% endfor %}

@ -1,15 +1,15 @@
#pragma once #pragma once
#include <string>
#include <memory> #include "rpc/RpcChannel.h"
#include <cstdint>
class {{ cls.name }}Proxy { class {{ cls.name }}Proxy {
public: public:
{{ cls.name }}Proxy(const char* pipeIn, const char* pipeOut); explicit {{ cls.name }}Proxy(RpcChannel& ch);
{% for m in cls.methods %} {% 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 %}); {{ m.return_type }} {{ m.name }}({% for a in m.args %}{{ a.type }} {{ a.name }}{% if not loop.last %}, {% endif %}{% endfor %});
{% endfor %} {% endfor %}
private: private:
int fdIn; class Impl;
int fdOut; Impl* impl;
}; };

@ -1,19 +1,15 @@
#include "{{ cls.name }}.skeleton.h" #include "{{ cls.name }}.skeleton.h"
#include <sstream>
{{ 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::string {{ cls.name }}Skeleton::dispatch(const std::string& req) {
std::istringstream in(req); return invoker.dispatch(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");
} }

@ -1,11 +1,16 @@
#pragma once #pragma once
#include "{{ cls.name }}.h" #include "{{ cls.name }}.h"
#include "rpc/RpcInvoker.h"
#include <string> #include <string>
class {{ cls.name }}Skeleton { class {{ cls.name }}Skeleton {
public: public:
{{ cls.name }}Skeleton({{ cls.name }}& obj); explicit {{ cls.name }}Skeleton({{ cls.name }}& obj);
std::string handleRequest(const std::string& req);
std::string dispatch(const std::string& req);
private: private:
{{ cls.name }}& obj; RpcInvoker invoker;
}; };

Loading…
Cancel
Save