Compare commits

..

10 Commits

Author SHA1 Message Date
Сергей Маринкевич 40170d553d remove Impl from proxy 2 weeks ago
Сергей Маринкевич 9f48a74677 move marshaller out of template 2 weeks ago
Сергей Маринкевич 785fca07f1 refactor 2 weeks ago
Сергей Маринкевич 4433568545 rename RpcClient to ProxyMarshaller 2 weeks ago
Сергей Маринкевич 7495f540d2 rename RpcChannel to IpcChannel 2 weeks ago
Сергей Маринкевич c6fed622ee bump readme 2 weeks ago
Сергей Маринкевич 66c4e5190b fup fifos 2 weeks ago
Сергей Маринкевич 0712460db0 improve ipc msg 2 weeks ago
Сергей Маринкевич 99ea8d1646 impove ipc 2 weeks ago
Сергей Маринкевич 1f16ef6b37 hmm 2 weeks ago

@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
# include source includes
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# find python interpreter
find_package(Python3 COMPONENTS Interpreter REQUIRED)
@ -16,38 +17,54 @@ file(MAKE_DIRECTORY ${GENERATED_DIR})
set(RPC_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_rpc.py)
set(RPC_TEMPLATES ${CMAKE_CURRENT_SOURCE_DIR}/tools/templates)
# inputs to parse
set(RPC_INPUTS
${CMAKE_CURRENT_SOURCE_DIR}/src/MyService.h
)
# helper to generate RPC code for an annotated header/source pair
function(autocode_annotated_file OUT_BASENAME HEADER SOURCE)
set(out_proxy_h "${GENERATED_DIR}/${OUT_BASENAME}.proxy.h")
set(out_proxy_cpp "${GENERATED_DIR}/${OUT_BASENAME}.proxy.cpp")
set(out_skeleton_h "${GENERATED_DIR}/${OUT_BASENAME}.skeleton.h")
set(out_skeleton_cpp "${GENERATED_DIR}/${OUT_BASENAME}.skeleton.cpp")
# command to run generator
add_custom_command(
add_custom_command(
OUTPUT
${GENERATED_DIR}/MyService.proxy.h
${GENERATED_DIR}/MyService.proxy.cpp
${GENERATED_DIR}/MyService.skeleton.h
${GENERATED_DIR}/MyService.skeleton.cpp
COMMAND ${CMAKE_COMMAND} -E echo "Generating RPC stubs..."
${out_proxy_h}
${out_proxy_cpp}
${out_skeleton_h}
${out_skeleton_cpp}
COMMAND ${CMAKE_COMMAND} -E echo "Generating RPC stubs for ${OUT_BASENAME}..."
COMMAND ${Python3_EXECUTABLE} ${RPC_GENERATOR} --out-dir ${GENERATED_DIR}
--compile-commands ${CMAKE_BINARY_DIR}/compile_commands.json
--templates ${RPC_TEMPLATES}
${RPC_INPUTS}
DEPENDS ${RPC_GENERATOR} ${RPC_TEMPLATES} ${RPC_INPUTS}
--header ${HEADER}
--source ${SOURCE}
--out-base ${OUT_BASENAME}
DEPENDS ${RPC_GENERATOR} ${RPC_TEMPLATES} ${HEADER} ${SOURCE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Running RPC code generator"
COMMENT "Running RPC code generator for ${OUT_BASENAME}"
VERBATIM
)
)
# accumulate all generated files into a global property so we can
# have one umbrella target that depends on all of them
set_property(GLOBAL APPEND PROPERTY AUTOCODE_GENERATED_FILES
${out_proxy_h}
${out_proxy_cpp}
${out_skeleton_h}
${out_skeleton_cpp}
)
endfunction()
include_directories(${GENERATED_DIR})
add_custom_target(rpc_generated DEPENDS
${GENERATED_DIR}/MyService.proxy.h
${GENERATED_DIR}/MyService.proxy.cpp
${GENERATED_DIR}/MyService.skeleton.h
${GENERATED_DIR}/MyService.skeleton.cpp
# declare all annotated files here
autocode_annotated_file(MyService
${CMAKE_CURRENT_SOURCE_DIR}/src/MyService.h
${CMAKE_CURRENT_SOURCE_DIR}/src/MyService.cpp
)
# umbrella target that depends on all generated RPC files
get_property(_all_generated GLOBAL PROPERTY AUTOCODE_GENERATED_FILES)
add_custom_target(rpc_generated DEPENDS ${_all_generated})
# Server
add_executable(server
src/server.cpp

@ -21,9 +21,19 @@ project/
├── README.md
├── src
│   ├── client.cpp
│   ├── common
│   │   ├── ipc
│   │   │   ├── IpcChannel.h
│   │   │   ├── IpcMessage.h
│   │   │   └── IpcPipeChannel.h
│   │   ├── proxy
│   │   │   └── ProxyMarshaller.h
│   │   └── rpc
│   │   ├── rpc_export.h
│   │   ├── RpcInvoker.h
│   │   └── RpcSerializer.h
│   ├── MyService.cpp
│   ├── MyService.h
│   ├── rpc_export.h
│   └── server.cpp
├── tools
│ ├── generate_rpc.py
@ -37,6 +47,24 @@ project/
---
## Архитектура (кратко)
* **Уровень IPC-сообщений**: `IpcMessage`
* Построение: `msg.add<std::string>("MyService.add"); msg.add<int>(7); msg.add<int>(8);`
* Разбор: `auto name = msg.get<std::string>(); auto a = msg.get<int>(); auto b = msg.get<int>();`
* Внутри хранится строка с простыми типовыми тегами (`i` для `int`, `s` для `std::string`), что позволяет в будущем перейти на бинарный формат без изменения API.
* **Уровень канала**: `RpcChannel` + `IpcPipeChannel`
* `IpcChannel` — абстракция транспорта: `send(const IpcMessage&)`, `receive() -> IpcMessage`.
* `IpcPipeChannel` — реализация поверх двух FIFO (`/tmp/fifo_to_server`, `/tmp/fifo_to_client`), которая внутри работает со строками, но наружу — только с `IpcMessage`.
* **Уровень RPC-ядра**:
* `ProxyMarshaller` — собирает `IpcMessage` из имени метода и аргументов, отправляет через `RpcChannel` и читает ответ.
* `RpcInvoker` — по имени метода (первое поле сообщения) находит зарегистрированную функцию-член и вызывает её, читая аргументы через `get<T>()`.
* **Сгенерированные обёртки**:
* `*.proxy.*` — используют `ProxyMarshaller` и `RpcChannel`, не зависят от конкретного транспорта.
* `*.skeleton.*` — используют `RpcInvoker` и принимают/возвращают `IpcMessage` для диспетчеризации вызовов.
---
## Зависимости
* CMake ≥ 3.10
@ -130,17 +158,21 @@ build/generated/
1. В одном терминале запускаем сервер:
```bash
./server
./build/server
```
Сервер создаёт (при необходимости) именованные пайпы `/tmp/fifo_to_server` и `/tmp/fifo_to_client`, читает запросы из `fifo_to_server` и пишет ответы в `fifo_to_client` через `IpcPipeChannel`.
2. В другом терминале — клиент:
```bash
./client
./build/client
```
Клиент также открывает эти FIFO, но логически пишет в `fifo_to_server` и читает из `fifo_to_client` (через тот же `IpcPipeChannel`), а пользовательский код видит только вызов `MyServiceProxy`.
**Ожидаемый вывод:**
```
```text
RESULT: 15
```

@ -0,0 +1,15 @@
#pragma once
#include "IpcMessage.h"
// Абстракция IPCканала, работающего с IpcMessage.
// Живёт отдельно от RPCкода, чтобы транспорт не зависел от конкретной RPCреализации.
class IpcChannel {
public:
virtual ~IpcChannel() = default;
virtual void send(const IpcMessage& msg) = 0;
virtual IpcMessage receive() = 0;
};

@ -0,0 +1,106 @@
#pragma once
#include <sstream>
#include <string>
// Примитивное IPCсообщение с API add<T>() / get<T>().
// Под капотом пока текстовый формат с типовыми тегами, но снаружи интерфейс не завязан на std::string.
class IpcMessage {
public:
IpcMessage() = default;
explicit IpcMessage(const std::string& raw)
: raw_(raw) {}
IpcMessage(const IpcMessage& other)
: raw_(other.raw_) {}
IpcMessage& operator=(const IpcMessage& other) {
if (this != &other) {
raw_ = other.raw_;
in_.clear();
in_initialized_ = false;
out_.str(std::string{});
}
return *this;
}
// Конструирование исходящего сообщения.
template<typename T>
void add(const T& v);
// Чтение входящего сообщения по частям.
template<typename T>
T get();
const std::string& raw() const {
return raw_;
}
bool empty() const {
return raw_.empty();
}
private:
std::string raw_;
std::ostringstream out_;
std::istringstream in_;
bool in_initialized_{false};
void ensureInput() {
if (!in_initialized_) {
in_.str(raw_);
in_initialized_ = true;
}
}
};
// ===== Специализации по типам =====
// int
template<>
inline void IpcMessage::add<int>(const int& v) {
out_ << 'i' << ' ' << v << ' ';
raw_ = out_.str();
}
template<>
inline int IpcMessage::get<int>() {
ensureInput();
char tag{};
in_ >> tag;
// в PoC просто доверяем, что тип совпадает
int v{};
in_ >> v;
return v;
}
// std::string
template<>
inline void IpcMessage::add<std::string>(const std::string& v) {
// формат: 's' <len> <bytes...>
out_ << 's' << ' ' << v.size() << ' ';
out_.write(v.data(), static_cast<std::streamsize>(v.size()));
out_ << ' ';
raw_ = out_.str();
}
template<>
inline std::string IpcMessage::get<std::string>() {
ensureInput();
char tag{};
in_ >> tag;
// ожидаем 's'
std::size_t len{};
in_ >> len;
// съесть одиночный пробел перед данными
in_.get();
std::string res(len, '\0');
in_.read(&res[0], static_cast<std::streamsize>(len));
// съесть завершающий пробел
in_.get();
return res;
}

@ -0,0 +1,57 @@
#pragma once
#include "IpcChannel.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
// IPCканал поверх именованных pipe.
// Инкапсулирует работу с файловыми дескрипторами и обмен сообщениями IpcMessage.
// readPipe — тот FIFO, который этот endpoint читает; writePipe — тот, в который пишет.
class IpcPipeChannel : public IpcChannel {
public:
IpcPipeChannel(const char* readPipe, const char* writePipe) {
// Канал не создаёт FIFO, только открывает.
// Открываем оба конца как O_RDWR, чтобы избежать блокировок на open(O_RDONLY/O_WRONLY).
// При этом логически читаем только из readPipe, а пишем только в writePipe.
fdIn_ = ::open(readPipe, O_RDWR);
fdOut_ = ::open(writePipe, O_RDWR);
}
~IpcPipeChannel() override {
if (fdIn_ >= 0) {
::close(fdIn_);
}
if (fdOut_ >= 0) {
::close(fdOut_);
}
}
void send(const IpcMessage& msg) override {
if (fdOut_ < 0) {
return;
}
const std::string& data = msg.raw();
::write(fdOut_, data.c_str(), data.size());
::write(fdOut_, "\n", 1);
}
IpcMessage receive() override {
if (fdIn_ < 0) {
return IpcMessage{};
}
char buf[4096];
const int n = ::read(fdIn_, buf, sizeof(buf) - 1);
if (n <= 0) {
return IpcMessage{};
}
buf[n] = 0;
return IpcMessage(std::string(buf));
}
private:
int fdIn_{-1};
int fdOut_{-1};
};

@ -0,0 +1,31 @@
#pragma once
#include <ipc/IpcChannel.h>
class ProxyMarshaller {
public:
explicit ProxyMarshaller(IpcChannel& ch) : channel(ch) {}
template<typename Ret, typename... Args>
Ret call(const std::string& method, const Args&... args) {
IpcMessage msg;
// имя метода
msg.add(method);
// аргументы
(msg.add(args), ...);
// отправить
channel.send(msg);
// получить ответ
IpcMessage resp = channel.receive();
return resp.template get<Ret>();
}
private:
IpcChannel& channel;
};

@ -1,9 +1,8 @@
#pragma once
#include "RpcSerializer.h"
#include <ipc/IpcMessage.h>
#include <functional>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
@ -15,26 +14,24 @@ public:
const std::string& name,
Ret (Obj::*method)(Args...)) {
handlers[name] =
[instance, method](const std::string& req) -> std::string {
std::istringstream in(req);
[instance, method](const IpcMessage& req) -> IpcMessage {
IpcMessage msg = req;
// пропустить имя метода
std::string skip;
in >> skip;
(void)msg.template get<std::string>();
// читать аргументы и вызвать метод
return callMethod<Ret, Obj, Args...>(instance, method, in);
return callMethod<Ret, Obj, Args...>(instance, method, msg);
};
}
std::string dispatch(const std::string& request) {
std::istringstream in(request);
std::string method;
in >> method;
IpcMessage dispatch(const IpcMessage& request) {
IpcMessage tmp = request;
const std::string method = tmp.get<std::string>();
auto it = handlers.find(method);
if (it == handlers.end()) {
return "ERR";
return IpcMessage{};
}
return it->second(request);
@ -42,25 +39,25 @@ public:
private:
template<typename Ret, typename Obj, typename... Args>
static std::string callMethod(Obj* obj,
static IpcMessage callMethod(Obj* obj,
Ret (Obj::*method)(Args...),
std::istringstream& in) {
auto tuple = readArgs<Args...>(in);
IpcMessage& msg) {
auto tuple = readArgs<Args...>(msg);
Ret result =
std::apply(method, std::tuple_cat(std::make_tuple(obj), tuple));
std::ostringstream out;
RpcSerializer::write(out, result);
return out.str();
IpcMessage out;
out.add(result);
return out;
}
template<typename... Args>
static std::tuple<Args...> readArgs(std::istringstream& in) {
return std::tuple<Args...>{RpcSerializer::read<Args>(in)...};
static std::tuple<Args...> readArgs(IpcMessage& msg) {
return std::tuple<Args...>{msg.template get<Args>()...};
}
std::unordered_map<std::string,
std::function<std::string(const std::string&)>>
std::function<IpcMessage(const IpcMessage&)>>
handlers;
};

@ -1,6 +1,6 @@
#pragma once
#include <rpc_export.h>
#include <rpc/rpc_export.h>
// annotate with clang attribute or via comment annotation recognized by libclang
// Use ANNOTATE attribute supported by clang: __attribute__((annotate("export")))

@ -1,48 +1,25 @@
#include "MyService.proxy.h"
#include "rpc/RpcChannel.h"
#include "ipc/IpcPipeChannel.h"
#include "proxy/ProxyMarshaller.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#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() {
mkfifo("/tmp/rpc_in", 0666);
mkfifo("/tmp/rpc_out", 0666);
// Создание FIFO — часть пользовательского IPCкода.
mkfifo("/tmp/fifo_to_server", 0666);
mkfifo("/tmp/fifo_to_client", 0666);
// IPCуровень: канал поверх pipe.
// Клиент пишет в fifo, который читает сервер (fifo_to_server),
// и читает из fifo, в который пишет сервер (fifo_to_client).
IpcPipeChannel ch("/tmp/fifo_to_client", "/tmp/fifo_to_server");
RpcChannelFifoClient ch("/tmp/rpc_in", "/tmp/rpc_out");
MyServiceProxy proxy(ch);
// RPCуровень: создаём marshaller поверх канала и передаём его в прокси.
ProxyMarshaller marshaller(ch);
MyServiceProxy proxy(marshaller);
int r = proxy.add(7, 8);
int r = proxy.add(7, 9);
std::cout << "RESULT: " << r << std::endl;
}

@ -1,11 +0,0 @@
#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;
};

@ -1,35 +0,0 @@
#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{};
};

@ -1,37 +0,0 @@
#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;
};

@ -1,57 +1,29 @@
#include "MyService.h"
#include "MyService.skeleton.h"
#include "rpc/RpcChannel.h"
#include "ipc/IpcPipeChannel.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#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() {
mkfifo("/tmp/rpc_in", 0666);
mkfifo("/tmp/rpc_out", 0666);
// Создание FIFO — часть пользовательского IPCкода.
mkfifo("/tmp/fifo_to_server", 0666);
mkfifo("/tmp/fifo_to_client", 0666);
RpcChannelFifoServer ch("/tmp/rpc_in", "/tmp/rpc_out");
// IPCуровень: канал поверх pipe.
// Сервер читает из fifo_to_server и пишет в fifo_to_client.
IpcPipeChannel ch("/tmp/fifo_to_server", "/tmp/fifo_to_client");
// RPCуровень: скелет поверх того же канала.
MyService realObj;
MyServiceSkeleton skeleton(realObj);
while (true) {
std::string req = ch.receive();
IpcMessage req = ch.receive();
if (req.empty()) {
break;
}
std::string resp = skeleton.dispatch(req);
IpcMessage resp = skeleton.dispatch(req);
ch.send(resp);
}
}

@ -73,11 +73,22 @@ def load_compile_flags(compile_commands_path, src_path):
if skip_next:
skip_next = False
continue
if tok.endswith('g++') or tok.endswith('clang++') or tok.endswith('clang') or tok.endswith('g++') or tok.endswith('cc'):
# skip the compiler binary itself
if (
tok.endswith("g++")
or tok.endswith("clang++")
or tok.endswith("clang")
or tok.endswith("cc")
or tok.endswith("c++")
):
continue
if tok == "-c":
skip_next = True
continue
# drop output file flag: -o <file>
if tok == "-o":
skip_next = True
continue
filtered.append(tok)
return filtered
return []
@ -183,7 +194,13 @@ def main():
p = argparse.ArgumentParser()
p.add_argument("--out-dir", "-o", required=True)
p.add_argument("--compile-commands", "-c", default=None)
p.add_argument("inputs", nargs="+")
p.add_argument("--header", required=True, help="C++ header file to scan for exported RPC classes")
p.add_argument("--source", required=True, help="C++ source file to use for resolving compile flags (from compile_commands.json)")
p.add_argument(
"--out-base",
required=True,
help="Base name for generated files: <out-base>.proxy.[h|cpp], <out-base>.skeleton.[h|cpp]",
)
p.add_argument("--templates", "-t", default=os.path.join(os.path.dirname(__file__), "templates"))
args = p.parse_args()
@ -199,28 +216,68 @@ def main():
except Exception as e:
print("WARNING: cannot set libclang path:", e)
index = Index.create()
all_classes = []
# determine compile flags from the source file (which is what appears in compile_commands.json)
if not os.path.exists(args.header):
print("ERROR: header file not found:", args.header)
return 1
if not os.path.exists(args.source):
print("ERROR: source file not found:", args.source)
return 1
for inp in args.inputs:
if not os.path.exists(inp):
print("WARN: input file not found:", inp)
continue
compile_args = load_compile_flags(args.compile_commands, inp)
# ensure -x c++ if missing
compile_args = load_compile_flags(args.compile_commands, args.source)
if not any(a.startswith("-x") for a in compile_args):
compile_args = ["-x", "c++", "-std=c++17"] + compile_args
print("Parsing", inp, "with args:", compile_args)
classes = parse_file(index, inp, compile_args)
all_classes.extend(classes)
if not all_classes:
print("No exported classes/methods found. Nothing to generate.")
print("Parsing", args.header, "with args:", compile_args)
index = Index.create()
classes = parse_file(index, args.header, compile_args)
if not classes:
print("No exported classes/methods found in header. Nothing to generate.")
return 0
# render templates
render_templates(all_classes, out_dir, args.templates)
print("Generated files for classes:", ", ".join(c.name for c in all_classes))
# For this PoC we expect a single exported service per header.
# If there are multiple, refuse to guess which one should define the filenames.
if len(classes) > 1:
print(
"ERROR: multiple exported classes found in header; "
"current generator expects exactly one when using --out-base."
)
for c in classes:
print(" -", c.name)
return 1
# render templates using the single discovered class but the userprovided base name
cls = classes[0]
env = Environment(
loader=FileSystemLoader(args.templates),
autoescape=False,
trim_blocks=True,
lstrip_blocks=True,
)
proxy_h = env.get_template("proxy.h.j2")
proxy_cpp = env.get_template("proxy.cpp.j2")
skeleton_h = env.get_template("skeleton.h.j2")
skeleton_cpp = env.get_template("skeleton.cpp.j2")
base = args.out_base
with open(f"{out_dir}/{base}.proxy.h", "w") as f:
f.write(proxy_h.render(cls=cls))
with open(f"{out_dir}/{base}.proxy.cpp", "w") as f:
f.write(proxy_cpp.render(cls=cls))
with open(f"{out_dir}/{base}.skeleton.h", "w") as f:
f.write(skeleton_h.render(cls=cls))
with open(f"{out_dir}/{base}.skeleton.cpp", "w") as f:
f.write(skeleton_cpp.render(cls=cls))
print("Generated files for class", cls.name, "into base", base)
return 0
if __name__ == "__main__":

@ -1,20 +1,11 @@
#include "{{ cls.name }}.proxy.h"
#include "rpc/RpcClient.h"
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)) {}
{{ cls.name }}Proxy::{{ cls.name }}Proxy(ProxyMarshaller& marshaller)
: impl(marshaller) {}
{% 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 %}) {
return impl->client.call<{{ m.return_type }}>("{{ cls.name }}.{{ m.name }}"{% for a in m.args %}, {{ a.name }}{% endfor %});
return impl.call<{{ m.return_type }}>("{{ cls.name }}.{{ m.name }}"{% for a in m.args %}, {{ a.name }}{% endfor %});
}
{% endfor %}

@ -1,15 +1,14 @@
#pragma once
#include "rpc/RpcChannel.h"
#include "proxy/ProxyMarshaller.h"
class {{ cls.name }}Proxy {
public:
explicit {{ cls.name }}Proxy(RpcChannel& ch);
explicit {{ cls.name }}Proxy(ProxyMarshaller& marshaller);
{% 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:
class Impl;
Impl* impl;
ProxyMarshaller& impl;
};

@ -9,7 +9,7 @@
{% endfor %}
}
std::string {{ cls.name }}Skeleton::dispatch(const std::string& req) {
IpcMessage {{ cls.name }}Skeleton::dispatch(const IpcMessage& req) {
return invoker.dispatch(req);
}

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

Loading…
Cancel
Save