匿名函数 C++

2024-04-17

我正在尝试使用该功能signal(int,void(*)(int)) from <csignal>处理浮点异常SIGFPE。除了显示“浮点异常”或类似内容的消息之外,我希望能够打印一些有用的诊断信息。这意味着我作为处理程序传递给的函数signal需要访问我的代码中的一些数据。问题就在于此。

该函数必须返回void并且只接受 1 个类型参数int。我无法使处理程序成为我的数据存储类的成员函数,因为那样类型将是void(Foo::*)(int)由于隐藏的this指针。

我考虑过使用 lambda 来尝试创建一个像这样的匿名函数;

void handler(int nSig, Foo data)
{
    // do something
}
// snip
Foo data;
signal(SIGFPE, [&](int nSig)->void{handler(nSig,data);});

但是因为 lambda 捕获了变量data从外部编译器不会让它被转换为指针void(*)(int)(这是一个遗憾,因为这似乎是 lambda 的理想用途)。

我可以简单地做data一个全局变量,然后可以在handler但出于显而易见的原因,我不愿意这样做。

所以我的问题是这样的;在 C++ 中模仿匿名函数的最佳方式是什么?

注意:我更喜欢原生 C++ 解决方案,而不必使用 boost 或等效解决方案。


这确实是一个好问题。在指责 C++ 之前,让我们先弄清楚到底发生了什么。想想 lambda 是如何实现的。

最简单的 lambda 是在没有捕获数据时。如果是这样的话,它的底层类型就变成了一个简单的普通函数。例如,像这样的 lambda:

[] (int p0) {}

将相当于一个简单的函数:

void foo(int p0)
{
}

如果您希望 lambda 成为函数指针,这实际上非常有效。例如:

#include <string>
#include <csignal>
#include <iostream>

int main()
{
    int ret;
    signal(SIGINT, [](int signal) {
            std::cout << "Got signal " << signal << std::endl;
        });
    std::cin >> ret;
    return ret;
}

到目前为止,一切都很好。但现在您想要将一些数据与信号处理程序相关联(顺便说一句,上面的代码是未定义的行为,如您只能在信号处理程序中执行信号安全代码 http://lazarenko.me/2013/01/15/how-not-to-write-a-signal-handler/)。所以你想要一个像这样的 lambda:

#include <string>
#include <csignal>
#include <iostream>

struct handler_context {
    std::string code;
    std::string desc;
};

int main()
{
    int ret;
    handler_context ctx({ "SIGINT", "Interrupt" });
    signal(SIGINT, [&](int signal) {
            std::cout << "Got signal " << signal
                      << " (" << ctx.code << ": " << ctx.desc
                      << ")\n" << std::flush;
        });
    std::cin >> ret;
    return ret;
}

让我们暂时忘记 C++ lambda 的语法糖。即使在 C 或汇编程序中,您也可以“模仿”lambda,这已不是什么秘密。那么实际上看起来怎么样? C 风格的“Lambda”可能看起来像这样(这仍然是 C++):

#include <string>
#include <cstdlib>
#include <iostream>

/*
 * This is a context associated with our lambda function.
 * Some dummy variables, for the sake of example.
 */
struct lambda_captures {
    int v0;
    int v1;
};

static int lambda_func(int p0, void *ctx) // <-- This is our lambda "function".
{
    lambda_captures *captures = (lambda_captures *)ctx;
    std::cout << "Got " << p0 << " (ctx: "
              << captures->v0 << ", " << captures->v1
              << ")\n" << std::flush;
    return 0;
}

// Below is an example of API function provided to the user that can
// invoke a callback supplied by the user.
static void some_api_function(int (*callback)(int p, void *data), void *data)
{
    callback(12345, data);
    callback(98765, data);
}

int main()
{
    lambda_captures captures;
    captures.v0 = 1986;
    captures.v1 = 2012;

    some_api_function(lambda_func, (void *)&captures);

    return EXIT_SUCCESS;
}

上面是 C 风格,C++ 倾向于将“context”作为“this”传递,这始终是隐式的第一个参数。如果我们的 API 支持传递“数据”作为第一个参数,我们可以应用指针成员转换 (PMF) 并编写如下内容:

#include <string>
#include <cstdlib>
#include <iostream>

struct some_class {
    int v0;
    int v1;

    int func(int p0)
    {
        std::cout << "Got " << p0 << " (ctx: "
                  << v0 << ", " << v1
                  << ")\n" << std::flush;
        return p0;
    }
};

static void some_api_function(int (*callback)(void *data, int p), void *data)
{
    callback(data, 12345);
    callback(data, 98765);
}

int main()
{
    typedef int (*mpf_type)(void *, int);

    some_class clazz({ 1986, 2012 }); // <- Note a bit of a Java style :-)
    some_api_function((mpf_type)&some_class::func, (void *)&clazz);

    return EXIT_SUCCESS;
}

在上面的两个示例中,请注意“数据”始终被传递。这个非常重要。如果应该调用回调的 API 不接受以某种方式传递回回调的“void *”指针,则您无法将任何上下文与回调关联起来。唯一的例外是全球数据。例如,这个 API 就很糟糕:

#include <string>
#include <cstdlib>
#include <iostream>

struct lambda_captures {
    int v0;
    int v1;
};

static int lambda_func(int p0)
{
/*
    // WHERE DO WE GET OUR "lambda_captures" OBJECT FROM????
    lambda_captures *captures = (lambda_captures *)ctx;
    std::cout << "Got " << p0 << " (ctx: "
              << captures->v0 << ", " << captures->v1
              << ")\n" << std::flush;
*/
    return 0;
}

// Below is an example of API function provided to the user that can
// invoke a callback supplied by the user.
static void some_api_function(int (*callback)(int p))
{
    callback(12345);
    callback(98765);
}

int main()
{
    lambda_captures captures;
    captures.v0 = 1986;
    captures.v1 = 2012;

    some_api_function(lambda_func /* How do we pass a context??? */);

    return EXIT_SUCCESS;
}

话虽如此,旧的信号 API 正是如此。解决该问题的唯一方法是将您的“上下文”实际放入全局范围内。然后信号处理函数可以访问它,因为地址是众所周知的,例如:

#include <string>
#include <cstdlib>
#include <iostream>

struct lambda_captures {
    int v0;
    int v1;
};

lambda_captures captures({ 1986, 2012 }); // Whoa-la!!!

static int lambda_func(int p0)
{
    std::cout << "Got " << p0 << " (ctx: "
              << captures.v0 << ", " << captures.v1
              << ")\n" << std::flush;
    return 0;
}

// Below is an example of API function provided to the user that can
// invoke a callback supplied by the user.
static void some_api_function(int (*callback)(int p))
{
    callback(12345);
    callback(98765);
}

int main()
{
    some_api_function(lambda_func);

    return EXIT_SUCCESS;
}

这是人们必须面对的。不仅仅是信号 API 的情况。这也适用于其他事情。例如中断处理程序处理。但在低级编程中,你必须处理硬件。当然,在用户空间提供这种 API 并不是最好的主意。我会再次提到 - 在信号处理程序中您只能做一小部分事情。你只能打电话异步信号安全函数 https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers.

当然,旧的 API 不会很快消失,因为它实际上是 POSIX 标准。然而,开发人员认识到了这个问题,并且有更好的方法来处理信号。例如,在 Linux 中,您可以使用eventfd http://www.kernel.org/doc/man-pages/online/pages/man2/eventfd.2.html要安装信号处理程序,请将其与任意上下文关联并在回调函数中执行您想要的操作。

无论如何,让我们回到您正在使用的 lambda。问题不在于 C++,而在于信号 API,除了使用全局变量之外,您无法传递上下文。话虽这么说,它也适用于 lambda:

#include <string>
#include <cstdlib>
#include <csignal>
#include <iostream>

struct some_data {
    std::string code;
    std::string desc;
};

static some_data data({ "SIGING", "Interrupt" });

int main()
{
    signal(SIGINT, [](int signal) {
            std::cout << "Got " << signal << " (" << data.code << ", "
                      << data.desc << ")\n" << std::flush;
        });
    return EXIT_SUCCESS;
}

因此,C++ 在这里所做的事情并不可耻,因为它做了正确的事情。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

匿名函数 C++ 的相关文章

  • 在 C/C++ 中获得正模数的最快方法

    通常在我的内部循环中 我需要以 环绕 方式索引数组 因此 例如 如果数组大小为 100 并且我的代码要求元素 2 则应该给它元素 98 高级语言 例如 Python 可以简单地使用my array index array size 但由于某
  • 我如何理解这个 C 类型声明?

    double bar int double double double double 在查看讲座幻灯片时 我发现了留给学生的练习 用简单的英语来说 什么是类型bar在这个 C 声明中 Please帮助我解决这个问题 我什至不知道从哪里开始
  • 平滑滚动.net 表单

    您好 我正在 net 中使用表单 并且在运行时动态添加大量链接标签 我将这些链接标签添加到面板并将该面板添加到 winform 当链接标签的数量增加时 表单会显示一个自动滚动条 垂直 现在 当我使用自动滚动向下滚动时 表单在滚动时不会更新其
  • EF Core 通过完全替换断开集合导航属性的更新

    使用 EF Core 5 0 我有一个 SPA 页面 可以加载Group实体及其集合Employee来自 API 的实体 var groupToUpdate await context Groups Include g gt g Emplo
  • VS 程序在调试模式下崩溃,但在发布模式下不崩溃?

    我正在 VS 2012 中运行以下程序来尝试 Thrust 函数查找 include cuda runtime h include device launch parameters h include
  • ASP.NET Web API 客户端 ProgressMessageHandler Post 任务卡在 WinForm 应用程序中

    我在用着HttpClient and ProgressMessageHandler来自MS ASP NET Web API 客户端库 http nuget org packages Microsoft AspNet WebApi Clien
  • 与 Qt 项目的静态链接

    我有一个在 Visual Studio 2010 Professional 中构建的 Qt 项目 但是 当我运行它 在调试或发布模式下 时 它会要求一些 Qt dll 如果我提供 dll 并将它们放入 System32 中 它就可以工作 但
  • 如何在 SqlDataReader.Read() 期间从死锁异常中恢复

    我的 NET 应用程序的事件日志显示 它在从 Sql Server 读取数据时偶尔会出现死锁 这种情况通常非常罕见 因为我们已经优化了查询以避免死锁 但有时仍然会发生 过去 我们在调用ExecuteReader函数在我们的SqlComman
  • 如何在 C# 控制台应用程序中将修饰符(ctrl、alt、shift)按键捕获为单个按键?

    Console ReadKey 仅在按下 正常 键时捕获输入 然后将修饰符 如果有 附加为键信息的一部分 如何将单个修饰键注册为输入 提供了一种解决方案这个链接 https blogs msdn microsoft com toub 200
  • 时间:2019-03-17 标签:c#ThreadSafeDeepCopy

    我一直在阅读很多其他问题以及大量谷歌搜索 但我一直无法找到明确的解决方案 根据我读过的一些最佳实践 类的静态方法应该创建线程安全的 并且实例成员应该将线程安全留给消费者 我想为该类实现深度复制方法 该类本身还有其他引用类型成员 有没有什么方
  • 如何在 QTabWidget Qt 中展开选项卡

    我有一个QTabWidget像这个 但我想展开选项卡以 填充 整个小部件宽度 如下所示 我怎样才能做到这一点 我在用Qt 5 3 2 and Qt 创建者 3 2 1 Update 我尝试使用setExpanding功能 ui gt myT
  • 如何在服务器端按钮点击时关闭当前标签页?

    我尝试在确认后关闭当前选项卡 因此我将以下代码放在确认按钮的末尾 但选项卡没有关闭 string jScript ClientScript RegisterClientScriptBlock this GetType keyClientBl
  • C++ php 和静态库

    我创建了一个library a 其中包含 cpp 和 h 文件 其中包含很多类 嵌套类和方法 我想在 php 示例中包含这个静态库并尝试使用它 我想提一下 我是 php 新手 我已经在 test cpp 文件中测试了我的 libray a
  • 给出 5 个参数,但在终端中只得到 3 个参数

    我想将一个文件传递给一个c 程序 如果我在 IDE 中执行此操作 test string string lt test txt return argc 5 但在终端上我刚刚得到argc 3 看来 这是因为 什么是 lt 意思是 我正在使用
  • AES 输出是否小于输入?

    我想加密一个字符串并将其嵌入到 URL 中 因此我想确保加密的输出不大于输入 AES 是可行的方法吗 不可能创建任何始终会创建比输入更小的输出的算法 但可以将任何输出反转回输入 如果您允许 不大于输入 那么基本上您只是在谈论同构算法alwa
  • 无法在内存位置找到异常源:cudaError_enum

    我正在尝试确定 Microsoft C 异常的来源 test fft exe 中 0x770ab9bc 处的第一次机会异常 Microsoft C 异常 内存位置 0x016cf234 处的 cudaError enum 我的构建环境是 I
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • 在 EnvDTE 中调试时捕获 VS 局部变量

    是否可以使用 EnvDTE 进行 vsix Visual Studio 扩展来捕获本地和调试窗口使用的调试数据 或者可以通过其他方法吗 我想创建一个自定义的本地窗口 我们可以修改它以根据需要显示一些较重的内容 而无需为高级用户牺牲原始的本地
  • .NET Core 中的跨平台文件名处理

    如何处理文件名System IO以跨平台方式运行类以使其在 Windows 和 Linux 上运行 例如 我编写的代码在 Windows 上完美运行 但它不会在 Ubuntu Linux 上创建文件 var tempFilename Dat
  • 列表过滤器内的 Java 8 lambda 列表

    示例 JSON id 1 products id 333 status Active id 222 status Inactive id 111 status Active id 2 products id 6 status Active

随机推荐

  • 尝试 glUseProgram 时出现 Opengl 错误 1281

    我有什么想法可以调试这个opengl进一步错误 1281 我正在从文件加载源代码 编译 链接 然后尝试检查错误glUseProgram 在我的对象的绘制方法中 log info gl2 glIsProgram shaderProgram t
  • imagesLoaded 方法不适用于 JQuery 砌体和无限滚动

    我一直在使用 JQuery masonry 现在我正在添加无限滚动 几乎每个砖石 砖块 中都有图像 在我使用无限滚动之前 图像加载得很好 一切都很棒 我为无限滚动添加了 javascript 的下一部分 并在内部添加了 imagesLoad
  • 使用 Visual Studio 2017 以 .NET Framework 2.0(不是 .NET Core 2.0)为目标

    我想为打开硬件监视器 https github com openhardwaremonitor openhardwaremonitor 它使用 NET Framework 2 0 同样 它不是 NET Core 2 0 当我克隆存储库并打开
  • 如何读取与 Rust 中的自定义类型关联的枚举值?

    我在 Rust 中有一个实现 如下所示 在主函数中 我正在读取一个值SalaryRange枚举 这将显示High So High This can be a complex type just using string for the qu
  • 如何将 tkinter 按钮垂直浮动在左侧

    我希望我的 tkinter 按钮 条目和文本垂直浮动在左侧的中间 我尝试过使用 pack 和 side left 但它只是水平堆叠它们 如果我可以垂直堆叠它们那就完美了 我尝试过使用网格 但无法将它们放在中间 我尝试过anchor w 但也
  • 如何设置特定Facebook页面的manage_pages权限?

    我如何将我的应用程序的管理页面权限仅设置为特定页面 现在我的应用程序获得了管理 Facebook 用户的所有页面的权限 我如何限制这一点并获得仅访问特定页面的权限 我正在使用一种简单的身份验证方法 app id xxxxxxxxxxxxx
  • Android Oreo (API26) 和 android.app.DownloadManager

    各位 此代码不适用于 Android Oreo 但在旧版本上可以 我可以看到通知和DownloadManager ACTION DOWNLOAD COMPLETE广播消息 Kotlin testButton setOnClickListen
  • OleDB INSERT 命令错误

    我有一个数据库 我使用 MS ACCESS 我有这个插入代码 我可以读取数据 但写入时出错 我按照说明操作 但它不起作用 这是我的代码 OleDbConnection con new OleDbConnection provider Mic
  • 设置 JOptionPane 对话框的助记符和热键

    是否可以为 JOptionPane 对话框中的按钮分配热键和助记符 我希望能够在 JOptionPane 生成的带有 是 否 和 取消 选项的消息对话框中 按 Y 键单击 是 按钮 按 N 键单击 否 按钮 然后按 escape 键激活转义
  • Kubernetes - 以编程方式找出服务 IP 范围 CIDR

    我需要一种方法来获取适用于所有 Kubernetes 集群的服务集群 IP 范围 如 CIDR 我尝试了以下方法 这对于使用 kubeadm 创建的集群效果很好 因为它会 grep apiserver pod 的参数 kubectl clu
  • 对数与平方根的 Big-O

    一般来说 以下内容总是正确的吗 log n O na a 1 s t a is any constant positive integer perhaps very large 如果不是的话 最大的值是多少a这个陈述对于哪些人来说是正确的
  • MySQL 加入不存在的地方

    我有一个连接两个表的 MySQL 查询 Voters 家庭 他们加入voters household id and household id 现在我需要做的是修改它 将选民表连接到第三个表 称为消除 voter id and elimina
  • 如何进行应用内购买去除广告

    我有 Android 游戏项目 我正在使用 ad mob 来投放广告 但是 如果有人想去掉广告就必须先付费怎么办 你能给我教程吗 因为我是 Android 和 Java 的新手 您只需在应用程序中创建一个按钮即可进行应用内购买 并且 一旦用
  • 使用 Gradle 运行命令行并保存输出结果

    我想使用 Gradle 运行命令行 该命令有一个输出 我在 Windows powershell 中运行此命令 mybat bat myArgs当我按回车键时 它会打印一些数字 如下所示 123456我想用 gradle 运行这个命令并保存
  • 在 SwiftUI 中创建自定义 Spacer() 时如何读取容器轴

    在 SwiftUI 中Spacer 文档 https developer apple com documentation swiftui spacer https developer apple com documentation swif
  • Sitecore - 如何导入内容?

    因此 我收到了一项新任务 将内容从网站导入到使用 Sitecore CMS 构建的新网站 我的客户基本上正在进行改造 以前的网站是使用非常旧的 CMS 构建的 所有内容都是 HTML 格式 我实际上正在考虑抓取旧网站并将所有内容转储为 cs
  • 在 Android Studio 上导入 panoramaGL

    我在项目中导入 PanoramaGL 库时遇到问题 这是图书馆https github com zarelaky panoramagl android tree master PanoramaGL https github com zare
  • 何时使用 HashMap 而不是 LinkedList 或 ArrayList,反之亦然

    为什么我们不能总是使用 HashMap 尽管它在添加 删除操作上比 ArrayList 或 LinkedList 高效得多 而且与元素的数量无关 我用 google 搜索了一下 发现了一些原因 但使用 HashMap 总有一种解决方法 而且
  • 获得令牌后如何从 Google Plus API 获取电子邮件地址

    我使用 oauth2 0 获得了 accesstoken 我能够获取人名 性别等 但无法获取用户的电子邮件地址 任何人都可以粘贴一些示例代码或任何有关如何从 google plus API 获取电子邮件地址的建议吗 如果用户明确授权您的应用
  • 匿名函数 C++

    我正在尝试使用该功能signal int void int from