Динамические библиотеки с функциями универсального типа [c ++] - PullRequest
0 голосов
/ 09 марта 2019

В прошлый день я пытался сделать это на C ++:

Допустим, у меня есть функция foo(arg1, arg2), один клиент и один сервер. arg1 может быть любого типа, arg2 может быть любым типом и foo может возвращать данные любого типа. Функция foo всегда имеет два аргумента.

Я хочу, чтобы клиент взял в качестве входных данных эту функцию (например):

foo(arg1,arg2){

    if(arg1<arg2){
       return 1;
    }else{
       return 5.3;
    }
    return 0;
}

, а затем эта функция должна быть передана и выполнена на сервере.

Я пытался создать динамическую библиотеку с этой функцией внутри с помощью шаблонов. Однако шаблоны не поддерживаются extern "C". Библиотека, которую я пытаюсь экспортировать, такова:

#include < iostream >

  extern "C" {
    template < typename N >
      N testFunc(char * arg) {
        N test = 5;
        std::cout << "Executed| arg is:" << arg << std::endl;
        return test;

      }
  }

Затем я попытался скомпилировать следующие команды:
g ++ -Wall -fPIC -c lib.cpp
g ++ -shared -o lib.so lib.o

Так есть идеи, как я могу это реализовать?

Я знаю, что это небезопасно, но только для образовательных целей.

Ответы [ 2 ]

1 голос
/ 11 марта 2019

Если мы сделаем некоторые упрощающие предположения, это не так уж и плохо: давайте представим, что функция, которую вы хотите отправить и выполнить удаленно, не принимает аргументов и не возвращает значения.Затем вы можете сделать следующее (рабочий пример ниже):

  1. Отправить исходную строку на сервер.

  2. На сервере вызовите gccчтобы скомпилировать строку, как вы описали в своем вопросе.

  3. Загрузите общий объект и затем вызовите функцию.

Теперь этоможет быть расширен в случае, если вы хотите немного больше работы: когда клиент отправляет функцию, сервер удерживает ее для дальнейшего использования.Позже клиент отправляет строку, содержащую вызов функции (включая аргументы) в виде строки 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();
}
0 голосов
/ 09 марта 2019

Недостаточно отправить два объекта на сервер. Вы также должны указать серверу, как сравнивать два объекта.

Например, предположим, что у меня есть клиент, а у вас есть сервер. Предположим, что я определил тип Country на моем клиенте. Предположим, что выражение netherlands < germany оценивается как истинное, потому что я определил operator<(const Country&, const Country&) для сравнения площадей. Нидерланды занимают меньше земель, чем Германия, поэтому netherlands < germany == true, верно?

Проблема в том, что ваш сервер не знает, что такое Country, и как сравнивать два объекта этого типа.

Даже если ваш сервер знал о Country, я всегда мог придумать другой тип, который сервер не знал. Поэтому, к сожалению, если я не понял вашего вопроса, я не верю, что существует прямой способ сделать то, что вы хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...