具有泛型类型函数的动态库[c++]
最后一天,我试着用c++来做这件事: 假设我有一个函数fooarg1,arg2,一个客户端和一个服务器。 arg1可以是任何类型,arg2可以是任何类型,foo可以返回任何类型的数据。函数foo总是有两个参数 我希望客户端将此函数作为输入,例如:具有泛型类型函数的动态库[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; } 然后,我尝试使用以下命令进行编译:
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();
}