Если мы сделаем некоторые упрощающие предположения, это не так уж и плохо: давайте представим, что функция, которую вы хотите отправить и выполнить удаленно, не принимает аргументов и не возвращает значения.Затем вы можете сделать следующее (рабочий пример ниже):
Отправить исходную строку на сервер.
На сервере вызовите gccчтобы скомпилировать строку, как вы описали в своем вопросе.
Загрузите общий объект и затем вызовите функцию.
Теперь этоможет быть расширен в случае, если вы хотите немного больше работы: когда клиент отправляет функцию, сервер удерживает ее для дальнейшего использования.Позже клиент отправляет строку, содержащую вызов функции (включая аргументы) в виде строки C ++.Сервер добавляет строку вызова в конец строки функции, а затем компилирует и запускает всю строку, как показано здесь.В этой модели код функции действительно может быть шаблоном, а строка вызова может содержать любые аргументы, для которых будет работать дедукция типа.Недостатком является то, что вы будете компилировать функцию каждый раз.В зависимости от того, насколько тривиальным или длительным является время выполнения функции, это может быть или не быть проблемой для вас.Конечно, есть много способов сделать все это лучше, но я надеюсь, что это послужит вам отправной точкой.
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstdio>
#include <string>
#include <cassert>
#include <dlfcn.h>
// Compile this with: g++ test.cpp -ldl
// Utility: check various calls below and bail out if bad.
int check(int line, int ret, int err = -1)
{
if (ret == err)
{
fprintf(stderr, "on line %d: ", line);
perror("");
abort();
}
return ret;
}
#define CHECK(X, ...) check(__LINE__, X, ##__VA_ARGS__)
// Common ipv4 address used by both client and server.
struct sockaddr_in addr{};
// This runs the client's part.
int client_main()
{
usleep(100000); // delay to give the server a chance to start.
// Connect to the server
auto sock = CHECK(socket(AF_INET, SOCK_STREAM, 0));
CHECK(connect(sock, (sockaddr*) &addr, sizeof(addr)));
// This is the function to be remotely executed.
std::string msg = R"(
#include <cstdio>
template <class T>
void remote_function(T arg1, T arg2)
{
if (arg1 < arg2)
printf("less\n");
else
printf("more\n");
}
// ---- portion below can be sent separately and appended by server later -----
extern "C" void runit()
{
remote_function(100, 1000);
}
)";
// Send the source
write(sock, msg.c_str(), msg.size());
close(sock);
// Client is done
return 0;
}
// This is the server's part.
int server_main()
{
// Listen for incoming connection from client
auto sock = CHECK(socket(AF_INET, SOCK_STREAM, 0));
CHECK(bind(sock, (sockaddr*) &addr, sizeof(addr)));
CHECK(listen(sock, 1));
struct sockaddr_in peer;
socklen_t peer_sz = sizeof(peer);
auto conn = CHECK(accept(sock, (sockaddr*) &peer, &peer_sz));
// Read the source code from the client
constexpr size_t maxSize = 1024 * 1024;
std::string source(maxSize, 0);
auto sz = CHECK(read(conn, &source[0], maxSize));
source.resize(sz);
printf("server got:%s\n", source.c_str());
// Compile it
auto gcc = popen("/usr/bin/g++ -o /tmp/_test.so -fPIC -shared -xc++ -", "w");
assert(gcc);
CHECK(fwrite(&source[0], 1, source.size(), gcc));
auto ret = CHECK(pclose(gcc));
if (ret == 0)
printf("compiled!\n");
else
abort();
// Load the compiled library
auto lib = dlopen("/tmp/_test.so", RTLD_LOCAL | RTLD_NOW);
assert(lib);
// Run the function
void (*fun)();
* (void**) &fun = dlsym(lib, "runit");
assert(fun);
(*fun)();
// Finished
dlclose(lib);
return 0;
}
int main()
{
// Set up address both client and server use to communicate.
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = htons(3333); // some arbitrary port.
if (fork())
return server_main();
else
return client_main();
}