具有泛型类型函数的动态库[c++]

具有泛型类型函数的动态库[c++],c++,compiler-errors,shared-libraries,C++,Compiler Errors,Shared Libraries,最后一天,我试着用c++来做这件事: 假设我有一个函数fooarg1,arg2,一个客户端和一个服务器。 arg1可以是任何类型,arg2可以是任何类型,foo可以返回任何类型的数据。函数foo总是有两个参数 我希望客户端将此函数作为输入,例如: foo(arg1,arg2){ if(arg1<arg2){ return 1; }else{ return 5.3; } return 0; } 然后,我尝试使用以下命令进行编译:

最后一天,我试着用c++来做这件事:

假设我有一个函数fooarg1,arg2,一个客户端和一个服务器。 arg1可以是任何类型,arg2可以是任何类型,foo可以返回任何类型的数据。函数foo总是有两个参数

我希望客户端将此函数作为输入,例如:

foo(arg1,arg2){

    if(arg1<arg2){
       return 1;
    }else{
       return 5.3;
    }
    return 0;
}
然后,我尝试使用以下命令进行编译: g++-Wall-fPIC-c lib.cpp g++-shared-o lib.so lib.o

你知道我该如何实现吗


我知道这不安全,但只是出于教育目的。

仅将这两个对象发送到服务器是不够的。您还必须告诉服务器如何比较这两个对象


例如,假设我有一个客户机,而你有一个服务器。假设我在客户机上定义了一个类型Country。假设表达式荷兰<德国>的计算结果为真,因为我定义了运算符,如果我们做一些简化假设,那还不错:让我们设想一下,要远程发送和执行的函数不带参数,也不返回值。然后,您可以执行以下工作示例:

将源字符串发送到服务器

在服务器上,调用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();
}

c没有模板类型。还有,您使用的是什么编译器?g++很好地支持从共享库导出模板函数。传递函数是什么意思?没有任何标准的方法来序列化函数并通过网络发送它们。您正在谈论创建远程过程调用库吗?在大多数RPC实现中,您在服务器上编译函数并将其与RPC包装器链接以创建服务器。在客户机上创建一个存根函数,该函数序列化参数并将调用发送到服务器。@Barman我认为远程过程调用服务器allready时知道该函数。在我的示例中,我希望客户机在函数更改时发送函数。@πάνταῥεῖ 我试过使用g++,但它让我犯了一个错误,我只是想做一些类似于apachespark的事情。其中,主节点向工作节点发送映射函数,工作节点执行映射函数。我知道scala没有类型。但我真的觉得奇怪,这不能在c++中实现,我不熟悉scala和ApacheSpark。他们能以我的国家为例吗?在我看来,对于服务器未知的类型,您的语义是绝对正确的,我没有想到这一点,但是我只想要一个简单的场景。给定简单的数据类型和运算符。有可能吗?我明白了。这不容易,是吗?您的问题是C++允许本地实现决定如何表示类型。如果您的服务器的字符是有符号的,long是64位,而我的客户端的字符是无符号的,long是32位,该怎么办?此外,即使它返回适合在网络上传输的纯文本字符串,也允许实现选择它返回的字符串。我所能做的就是建立一组预定的要识别的类型,并通过传输您定义的枚举来指定类型。或者,由于Boost库被广泛使用,这会有帮助吗?这是一个有趣的答案,+1表示努力。不过,我不确定这有多实际。这可能不是C++是如何使用的,也不是肯定的,如果客户端和服务器在CPU架构和/或ABI格式上不同,它会起作用。@ THB,在我提出的解决方案中,客户端从不编译代码,也不运行它。因此,唯一的架构/ABI是服务器上的架构/ABI,因为最终用户的函数是在服务器上编译的,所以它是一致的。客户机可以在完全不同的硬件或操作系统上运行。我上面列出的示例客户机代码当然会相应地更改。@Always我一直认为我已经测试了您的代码,似乎正在做我想做的事情。获取函数返回值的任何方法。我认为这很难,因为我们不知道用户将发送什么类型的函数,
#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();
}