c++ - 具有通用类型函数的动态库 [c++]

标签 c++ compiler-errors shared-libraries

最后一天我试图用 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

那么知道如何实现它吗?

我知道这不安全,但仅用于教育目的。

最佳答案

如果我们做一些简化的假设,这还不算太糟糕:让我们假设您想要远程发送和执行的函数不接受任何参数并且不返回任何值。然后您可以执行以下操作(下面的工作示例):

  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();
}

关于c++ - 具有通用类型函数的动态库 [c++],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55080747/

相关文章:

c++ - header 多重重新定义

JavaScript 获取不包含子项的 textContent

c - 如何在 OSX 上制作 .so 可执行文件?

java - 了解 Websphere 共享库的使用与在 Java 项目中定义库的对比?

c++ - 是否需要将空指针分配给 std::auto_ptr

c++ - float、double 和 long double 是否有保证的最小精度?

maven - 如何将 Spring Boot 构建插件与 JavaFX 应用程序一起使用

ios - 如何解决xcode 5 中的apple LLVM 5.0 错误?

c++ - 如何在 C++ 中的函数内编辑函数参数

c++ - 无法连接到 QML 仿真层(QML Puppet)