Я разработал реализацию RPC, которая не использует прекомпилированные IDL (например, protobuf) и не требует специфичных прототипов функций (например, <code class="cpp">RPC_ERR (*func) (rpc_arg** args, rpc_arg* ret);</code>). Вместо этого в проекте применяется библиотека libffi, позволяющая вызывать функции через указатели и создавать массив аргументов на основе их "описания" (перечень типов в виде enum), а также аргументов, переданных клиентом. Если кого-то интересует, <a href="http://github.com/catmengi/DynamicRPC" rel="nofollow">исходный код доступен здесь</a>. Привожу пример простого сервера: <pre><code class="cpp">int test(char* a, int b){ printf("%s:%d\n", a, b); return -b; } int main(){ struct rpcserver* serv = rpcserver_create(1234); // создаем сервер на порту 1234 enum rpctypes proto[] = {STR, INT32}; // описание функции rpcserver_load_keys(serv, "keys"); // загрузка "ключей" (пара пароль-привилегия "key"0) int minperm = -1; // минимальные привилегии для вызова функции rpcserver_register_fn(serv, test, "test", INT32, proto, sizeof(proto) / sizeof(proto[0]), NULL, minperm); rpcserver_start(serv); getchar(); rpcserver_free(serv); }</code></pre> API вызова функции с клиента поддерживает переменное количество аргументов, а также возможность передачи некоторых типов данных обратно клиенту (таких как (char*, uint64_t (SIZEDBUF), rpcbuff, rpcstruct). В реализации rpcbuff получен многомерный массив, а rpcstruct представляет собой хеш-таблицу с автоматической упаковкой и распаковкой типов, что позволяет использовать ее как ассоциативный массив, структуру или связанный список. <pre><code class="cpp">int main(int argc, char** argv){ if(argc < 5 || argc > 5){ puts("WRONG usage"); // продолжение кода... </code></pre>
Идея и реализация "динамического" RPC, как вы описали, представляют собой интересный подход, дающий ряд преимуществ и недостатков: ### Преимущества: 1. **Гибкость**: Использование `libffi` для динамического вызова функций позволяет избежать необходимости в жестко прописанных интерфейсах, что может значительно упростить расширение системы и добавление новых сервисов без необходимости пересобирать существующий код. 2. **Динамическая типизация**: Возможность передавать переменное количество аргументов и представлять их различные типы может повысить удобство использования API и упростить его адаптацию для различных клиентов. 3. **Минимизация зависимостей**: Избежание использования IDL и пре-компиляции позволяет снизить зависимость от сторонних инструментов и улучшить интеграцию в разные системы. 4. **Поддержка сложных типов данных**: Ваша реализация, поддерживающая сложные структуры данных, такие как хеш-таблицы для `rpcstruct`, может предложить больше возможностей для пользователя и повысить выразительность API. ### Недостатки: 1. **Производительность**: Использование `libffi` часто сопряжено с накладными расходами на производительность, поскольку динамическая компиляция и взаимодействие с аргументами могут требовать больше времени в сравнении с статически типизированными вызовами. 2. **Ошибки времени выполнения**: Поскольку типы данных проверяются только во время выполнения, это может привести к ошибкам, которые сложно отслеживать и исправлять, поскольку они не проверяются компилятором. 3. **Безопасность**: Динамическая реализация может быть подвержена ошибкам безопасности, таким как переполнение буфера или неправильное обращение с памятью, если не обеспечена правильная проверка типов и длины аргументов. 4. **Сложность отладки**: Отладка проблем в динамическом вызове может быть более сложной, чем в статически типизированных подходах, поскольку контекст выполнения не всегда очевиден. 5. **Совместимость**: На разных платформах могут возникнуть различные зависимости и проблемы с совместимостью, связанные с использованием `libffi`. ### Заключение: С точки зрения жизнеспособности, решение может быть вполне удачным, особенно в ситуациях, где гибкость важнее производительности. Однако важно учитывать указанные недостатки и тестировать систему на устойчивость, производительность и безопасность, прежде чем применять её в производственной среде. Если ваш проект предполагает широкий спектр взаимодействий и требует высокой модульности, то динамический RPC может стать отличным выбором. Неплохо также рассмотреть отзывы пользователей и разработчиков в процессе внедрения вашего решения, чтобы выявить возможные узкие места и улучшить его в будущем.