Доброе время суток форумчане. Сегодня я хочу поделиться с вами небольшой RPC библиотекой, которая поможет создавать крохотные клиент-серверные приложения, не использующие хоть и мощную, но увы тяжеловатую crt языка c++ для примера, размер программы вида int main(){malloc(0);} получается порядка 30 киллобайт при статической линковки и полной оптимизации по размеру. Полный же размер crt может достигать в программе нескольких сот киллобайт. Разумеется для совсем небольших(в плане кода) программ это кажеться несколько накладно. Более того, следующие конструкции языка также неявно подгружают crt: исключения(увы и ах), полиморфизм, более того даже обычный operator new(nothrow) использует std::malloc, поэтому new и delete перегружены в этой либе, но и это ещё не всё! даже при включении некоторых стандартных хедеров подгружается crt например при инклуде <cassert>, размер exe'ика - релиза с оптимизацией на размер увеличивается с 1кб до 60. При использовании некоторых алгоритмов- также используется crt, например при std::copy. Данные наблюдения справедливы для MSVC8 и gcc(насчёт cassert'а для gcc не проверял), для других компиляторов не проверял, но сомневаюсь что у них в этом плане сильно лучше. Зато шаблоны и макросы доступны в полном обьёме Итак в типичном клиент-серверном взаимодействии(для простоты за сервер примем машину, на которой выполняются команды, которые сервер получает от клиента, разумеется в больших системах часто эти понятия могут быть переплетены, но тут я это не рассматриваю) есть прослойка отвечающая за отправку команд с клиента на сервер, и возращение результата работы выполнения этих команд назад. За суть команды в этой библиотеки я принял некоторую свободную функцию, которая получает параметры(до 10 штук) по значению, и возвращает некоторый результат(тоже по значению). Так как использование исключений не допустимо в этой библиотеке(иначе это уже не rpc без crt), а каждая удалённая функция может закончится провалом(например сервер повис, или просто провод обрубился) приходится вводить коды ошибок, любая rpc функция регистрируемая в библиотеке должна принимать первым значением - ссылку на код возврата, не нулевое значение которого означает, что выполнение функции завершилось неудачно, список ошибок можно посмотреть в enum'е shadow::Errors. Регистрация функций происходит в файле shandow_instance.h и формат регистрации следующий: enum FunctionType { //тут перечисляются некоторые идентификаторы функций, они используются исключительно внутри библиотеки и вы кроме //этого файла их нигде не увидете simple, fff, fcnt, //последний элемент опять таки используется в либе для определения колличества регистрируемых функий. last_function_id //this element must be in the end of enum }; //дальше в этом же файле происходит регистрация самих функций, макросом SHADOW_FUNCTION SHADOW_FUNCTION(function_name, function_id, function_ret, array_of_arguments) где function_name -имя функции, которая будет вызываться удалённо(она должна быть в глобальном нэймспэйсе), function_id -идентификатор который вы написали ранее в FunctionType(для каждой функции должен быть строго 1 уникальный идентификатор) function_ret -тип возвращаемого значения функции array_of_arguments -список аргументов типов функции, при этом самый первый, обязательный параметр функции(ShadowRet по ссылке) в этот список не попадает, т.е запись SHADOW_FUNCTION(qwerty, fcnt, shadow::stl::string, (2,(shadow::stl::string, long long))) означает: регистрируем функцию с именем qwerty, идинтификатором fcnt, которая имеет прототип shadow::stl::string qwerty(shadow::ShadowRet& ret, shadow::stl::string str, long long value) для фундаментальных типов, а также для шаблонных классов shadow::stl::basic_string и shadow::stl::vector сериализацию прописывать не нужно, для любых других пользовательских типов придётся прописывать функции SerializeI и SerializeO, смысл их легко понять их исходников либы(файл shadow_rpc.hpp), но замечу что сериализация бинарная. //Рабочий пример файл shandow_instance.h: #ifndef SHADOW_FUNCTION_TYPE #define SHADOW_FUNCTION_TYPE enum FunctionType { simple, fff, fcnt, last_function_id //this element must be in the end of enum }; #endif //SHADOW_FUNCTION_TYPE //multiply times header including SHADOW_FUNCTION(qwerty, fcnt, shadow::stl::string, (2,(shadow::stl::string, long long))) SHADOW_FUNCTION(funciyo, simple, int, (1, (int))) SHADOW_FUNCTION(ischo_funciya, fff, void, (0,())) //файл simple_server.cpp #include "stdafx.h" #ifndef _DEBUG #pragma comment(linker, "/SUBSYSTEM:WINDOWS") #pragma comment(linker, "/ENTRY:main") #pragma comment(linker, "/MERGE:.data=.text /MERGE:.rdata=.text") #pragma comment(linker, "/SECTION:.text,EWR /STUB:../inc/dos_stub.dat") #pragma optimize("gsy", on) #endif #define SHADOW_SERVER_HERE //#define SHADOW_CLIENT_HERE #include "shadow_rpc.hpp" #include "shadow_misc.hpp" int funciyo(shadow::ShadowRet& ret, int a){ return a; } shadow::stl::string qwerty(shadow::ShadowRet& ret, shadow::stl::string str, long long){ if(str == "preved krosavcheg") return "preved medved"; else return "chto tebe?"; } void ischo_funciya(shadow::ShadowRet& ret) { } int main(int argc, char* argv[]) { using namespace shadow; for(;{ TRY{ SyncServer ss(555); CHECKO(ss); stl::vector<char> buf1, buf2; for(;{ buf1.clear(); buf2.clear(); ss.Recv(buf1); CHECKO(ss); RunCmd(buf1, buf2); ss.Send(buf2); CHECKO(ss); } } CATCH_BEGIN CATCH_END } return 0; } //файл simple_client.cpp #include "stdafx.h" #ifndef _DEBUG #pragma comment(linker, "/SUBSYSTEM:WINDOWS") #pragma comment(linker, "/ENTRY:main") #pragma comment(linker, "/MERGE:.data=.text /MERGE:.rdata=.text") #pragma comment(linker, "/SECTION:.text,EWR /STUB:../inc/dos_stub.dat") #pragma optimize("gsy", on) #endif //#define SHADOW_SERVER_HERE #define SHADOW_CLIENT_HERE #include "shadow_rpc.hpp" int main(int argc, char* argv[]) { using namespace shadow; TRY{ SyncClient s("localhost", 555); CHECKO(s); RemoteCall<SyncClient> rc(s); CHECKO(rc); stl::string ret = rc.qwerty("preved krosavcheg",11); CHECKO(rc); _ASSERT(ret == "preved medved"); rc.ischo_funciya(); CHECKO(rc); } CATCH_BEGIN _ASSERT(false); CATCH_END return 0; } на данный момент в библиотеке реализованы только синхронные tcp сокеты, но они сами по себе отношение к rpc не имеют, но переделать на асинхронные в принципе не составит труда, благо исходники мне кажеться вполне понятны))) что означают макросы типа TRY, CATCH_BEGIN, CHECKO и пр. - опять же таки лучше смотреть по исходникам, но макросы вместо прямого кода в данном месте используются с потенциальной дальнейшей поддержкой исключений и трансляций их с сервера на клиент. размер приведённого собранного сервера составляет 8, клиента 7.5кб, rpc'ёвый overhead на каждую новую функцию составляет порядка 300 байт как для сервера так и для клиента. библиотека зависит по хедерам от буста (1.33 или выше), качать его можно отсюда http://boost.org/ исходники shadow_rpc и приведённого тут примера http://snike.pochtamt.ru/flashcard/shadow_rpc.7z з.ы библиотека распостраняется как есть, без каких либо гарантий и пр.