c ++ lambda,无法访问由副本捕获的变量

时间:2014-02-26 21:45:13

标签: c++ lambda

我有这个简单的客户端/服务器套接字代码,用于发送和接收lambda函数。问题出现在recvlambda()函数内的客户端上,当我在收到它后尝试调用lambda时,我得到了seg fault @

  

printf(“Hello World!%i,%i \ n”,x,y);

调试显示无法访问x和y,它们的内存地址不好。

我在Ubuntu 13.10上使用gcc 4.8.1。

我将x,y传递给sendlambda()函数中的lambda。这不应该是错误的。知道为什么吗?

#include <iostream>
#include <time.h>
#include <gmpxx.h>

using namespace std;

typedef int (*func)();

/* Server code in C */

  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
#include <functional>

void sendlambda(int ConnectFD)
{
    int x = 2342342;
    int y = 23454234;
    function<int (void)> f = [x, y]() mutable -> int
    {
       printf("Hello World! %i, %i\n", x, y);
    };
    printf("sending lambda of %i bytes\n", sizeof(f));
    write(ConnectFD, (void*)&f, sizeof(f));
}


void recvlambda(int SocketFD)
{
    char buffer[1024];
    read(SocketFD, (void*)buffer, sizeof(buffer));
    function<int (void)> &f = *(function<int (void)>  *)buffer;
    f();
}

int server()
{
    printf("server\n");
    struct sockaddr_in stSockAddr;
    int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(-1 == SocketFD)
    {
        printf("can not create socket\n");
        exit(EXIT_FAILURE);
    }

    memset(&stSockAddr, 0, sizeof(stSockAddr));

    stSockAddr.sin_family = AF_INET;
    stSockAddr.sin_port = htons(1100);
    stSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if(-1 == bind(SocketFD,(struct sockaddr *)&stSockAddr, sizeof(stSockAddr)))
    {
        printf("error bind failed\n");
        close(SocketFD);
        exit(EXIT_FAILURE);
    }

    if(-1 == listen(SocketFD, 10))
    {
        printf("error listen failed\n");
        close(SocketFD);
        exit(EXIT_FAILURE);
    }

    for(;;)
    {
        int ConnectFD = accept(SocketFD, NULL, NULL);

        if(0 > ConnectFD)
        {
            printf("error accept failed\n");
            close(SocketFD);
            exit(EXIT_FAILURE);
        }

        /* perform read write operations ...*/
        sendlambda(ConnectFD);

        if (-1 == shutdown(ConnectFD, SHUT_RDWR))
        {
            printf("can not shutdown socket\n");
            close(ConnectFD);
            close(SocketFD);
            exit(EXIT_FAILURE);
        }
        close(ConnectFD);
    }

    close(SocketFD);
    return EXIT_SUCCESS;
}


int client()
{
    printf("client\n");
    struct sockaddr_in stSockAddr;
    int Res;
    int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (-1 == SocketFD)
    {
        printf("cannot create socket\n");
        exit(EXIT_FAILURE);
    }

    memset(&stSockAddr, 0, sizeof(stSockAddr));

    stSockAddr.sin_family = AF_INET;
    stSockAddr.sin_port = htons(1100);
    Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr);

    if (0 > Res)
    {
        printf("error: first parameter is not a valid address family\n");
        close(SocketFD);
        exit(EXIT_FAILURE);
    }
    else if (0 == Res)
    {
        printf("char string (second parameter does not contain valid ipaddress\n)");
        close(SocketFD);
        exit(EXIT_FAILURE);
    }

    if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr)))
    {
        printf("connect failed\n");
        close(SocketFD);
        exit(EXIT_FAILURE);
    }

    /* perform read write operations ... */
    recvlambda(SocketFD);

    (void) shutdown(SocketFD, SHUT_RDWR);

    close(SocketFD);
    return EXIT_SUCCESS;
}

int main(int argc, char** argv)
{
    if(argc>1 && strcmp(argv[1], "server")==0)
        server();
    else
        client();
    return 0;
}

3 个答案:

答案 0 :(得分:3)

一般情况下,您不能指望复制类型的按位副本(例如std::function)能够正常工作。

这是可能发生的事情:在服务器进程中创建的std::function对象是一个函数指针,指向用于初始化它的lambda,它位于服务器进程的内存中。当客户端进程收到它时,它会尝试调用此函数指针,该指针指向不同进程的地址空间(服务器进程的指针)。这种段错误并不奇怪。

但是你仍然处于未定义的行为区域,因为你分配了一个字符数组,然后尝试访问它,好像它是一个std::function对象。

答案 1 :(得分:0)

lambda只是另一个分配在sendlambda麻袋上的本地人。 &f函数与获取该函数中声明的任何其他本地的地址没有什么不同。在这种情况下,虽然f不是一种可以简单地按值复制并在另一帧中重新水合的类型(它不是POD类型)。这就是您在recvlambda

中遇到错误的原因

答案 2 :(得分:0)

我尝试了更多的东西并得出结论,我想要做的就是序列化一个lambda。 Serialize C++ functor

这是不可能的。

当客户端代码在某处(在其自己的进程空间中)使用或定义仿函数,然后接收一些字节流,这些字节流被转换为同一个仿函数时,代码可以工作。它以某种方式调用客户端中的该函数。

但是如果客户端进程没有仿函数块,并且您尝试将字节流强制转换为仿函数,则它不起作用,您无法调用该仿函数。

如果它的闭包变得更糟,服务器上的副本捕获的变量无法映射到客户端上。服务器进程的数据段中的变量,它们在服务器的仿函数中被引用。当该仿函数被发送到客户端时,客户端会尝试在给定数据段的内存地址的情况下访问这些变量。但这些地址是针对服务器进程的。所以我得到了读取访问权限。

似乎我正在尝试使用lambdas进行代码注入,但这是不可能的。