如何使 C 中的方法链流畅?

2023-12-01

现有的 C API 如下所示:

//data
typedef struct {int properties;} Widget;

//interface
Widget* SetWidth(Widget *const w, int width){
    // ...
    return w;
}
Widget* SetHeight(Widget *const w, int height){
    // ...
    return w;
}
Widget* SetTitle(Widget *const w, char* title){
    // ...
    return w;
}
Widget* SetPosition(Widget *const w, int x, int y){
    // ...
    return w;
}

第一个参数始终是指向实例的指针,并且转换实例的函数始终将其作为指针返回。

我认为这样做是为了支持某种方法链接?

当函数作为方法存在于对象范围内时,方法链接在语言中才有意义。考虑到 API 的当前状态,我只能这样使用它:

int main(void) {
    Widget w;
    SetPosition(SetTitle(SetHeight(SetWidth(&w,400),600),"title"),0,0);
}

我可以在 C 语言中使用任何技术来获得与其他语言相同的流畅性吗?


C中没有任何语法技巧可以实现方法链接正如在其他一些语言中可能使用的那样。在 C 中,您可以编写单独的函数调用,将对象指针传递给每个函数:

Widget *w = getWidget();
widgetSetWidth(w, 640);
widgetSetHeight(w, 480);
widgetSetTitle(w, "Sample widget");
widgetSetPosition(w, 0, 0);

对于 C++ 和其他 OOP 语言中的方法调用也可以完成同样的操作:

Widget *w = getWidget();
w->SetWidth(640);
w->SetHeight(480);
w->SetTitle("Sample widget");
w->SetPosition(0, 0);

使用上述 API,并假设每个方法返回this对象,方法链接语法如下所示:

getWidget()->SetWidth(640)->SetHeight(480)->SetTitle("Sample widget")->SetPosition(0, 0);

这是否比单独的语句更具可读性取决于品味和本地编码约定。我个人觉得它很麻烦,更难读。在代码生成方面有一个小优点:下次调用时不需要从局部变量重新加载对象指针。这种微小的优化很难证明链接语法是合理的。

一些程序员尝试通过这种方式让它变得更容易接受:

getWidget()
 -> SetWidth(640)
 -> SetHeight(480)
 -> SetTitle("Sample widget")
 -> SetPosition(0, 0);

再说一次,这是一个品味和编码约定的问题……但是 C 语言的等价物看起来确实很尴尬:

Widget *w = widgetSetPosition(widgetSetTitle(widgetSetHeight(widgetSetWidth(getWidget(), 640), 480), "Sample widget"), 0, 0);

并且没有简单的方法可以将这个链重新组织成更具可读性。

请注意,一些最古老的 C 库函数可以链接到:

const char *hello = "Hello";
const char *world = "World";
char buf[200];
strcpy(buf, hello);
strcat(buf, " ");
strcat(buf, world);
strcat(buf, "\n");

可以重组为:

strcat(strcat(strcat(strcpy(buf, hello), " "), world), "\n");

但更安全且更受青睐的方法是:

snprintf(buf, sizeof buf, "%s %s\n", hello, world);

欲了解更多信息,您可能想阅读以下内容:

Marco Pivetta (Ocramius):流畅的界面是邪恶的

另请注意,如果 C 对象具有用于这些调用的函数指针成员,则可以使用上述所有语法,但仍必须将对象指针作为参数传递。函数指针通常分组在对象中存储指针的结构中,模仿 C++ 虚拟方法的实现,使语法稍微重一些:

Widget *w = getWidget();
w->m->SetWidth(w, 640);
w->m->SetHeight(w, 480);
w->m->SetTitle(w, "Sample widget");
w->m->SetPosition(w, 0, 0);

将它们链接起来也是可能的,但没有真正的好处。

最后,应该注意的是,方法链接不允许显式的错误传播。在 OOP 语言中,链接是惯用的,可以抛出异常以或多或少令人满意的方式发出错误信号。在 C 中,处理错误的惯用方法是返回错误状态,这与返回指向对象的指针相冲突。

因此,除非保证方法成功,否则建议不要使用方法链并执行迭代测试:

Widget *w = getWidget();
if (SetWidth(w, 640)
||  SetHeight(w, 480)
||  SetTitle(w, "Sample widget")
||  SetPosition(w, 0, 0)) {
    /* there was an error, handle it gracefully */
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使 C 中的方法链流畅? 的相关文章

  • 使用 std::packaged_task/std::exception_ptr 时,线程清理程序报告数据争用

    我遇到了线程清理程序 TSan 的一些问题 抱怨某些生产代码中的数据争用 其中 std packaged task 通过将它们包装在 std function 中而移交给调度程序线程 对于这个问题 我简化了它在生产中的作用 同时触发 TSa
  • 如何在 .NET Framework 2.0 中模拟“Func<(Of <(TResult>)>) 委托”?

    我尝试使用这个类代码项目文章 http www codeproject com KB threads AsyncVar aspx在 VB NET 和 NET Framework 2 0 中 除了这一行之外 所有内容似乎都可以编译Privat
  • 如何在c++中读取pcap文件来获取数据包信息?

    我想用 C 编写一个程序来读取 pcap 文件并获取数据包的信息 例如 len sourc ip flags 等 现在我找到了如下代码 我认为它会帮助我获取信息 但是我有一些疑问 首先我想知道应该将哪个库添加到我的程序中 然后什么是 pca
  • C中的malloc内存分配方案

    我在 C 中尝试使用 malloc 发现 malloc 在分配了一些内存后浪费了一些空间 下面是我用来测试 malloc 的一段代码 include
  • 错误:表达式不产生值

    我尝试将以下 C 代码转换为 VB NET 但在编译代码时出现 表达式不产生值 错误 C Code return Fluently Configure Mappings m gt m FluentMappings AddFromAssemb
  • 使用 LINQ2SQL 在 ASP.NET MVC 中的各种模型存储库之间共享数据上下文

    我的应用程序中有 2 个存储库 每个存储库都有自己的数据上下文对象 最终结果是我尝试将从一个存储库检索到的对象附加到从另一个存储库检索到的对象 这会导致异常 Use 构造函数注入将 DataContext 注入每个存储库 public cl
  • 单个对象的 Monogame XNA 变换矩阵?

    我读过一些解释 XNA Monogame 变换矩阵的教程 问题是这些矩阵应用于 SpriteBatch Begin matrix 这意味着所有 Draw 代码都将被转换 如何将变换矩阵应用于单个可绘制对象 就我而言 我想转换滚动背景 使其自
  • 为什么 Google 测试会出现段错误?

    我是 Google Test 的新手 正在尝试提供的示例 我的问题是 当我引入失败并设置GTEST BREAK ON FAILURE 1 或使用命令行选项 GTest 将出现段错误 我正在考虑这个例子 https code google c
  • 在 C 中初始化变量

    我知道有时如果你不初始化int 如果打印整数 您将得到一个随机数 但将所有内容初始化为零似乎有点愚蠢 我问这个问题是因为我正在评论我的 C 项目 而且我对缩进非常直接 并且它可以完全编译 90 90 谢谢 Stackoverflow 但我想
  • 是否有实用的理由使用“if (0 == p)”而不是“if (!p)”?

    我倾向于使用逻辑非运算符来编写 if 语句 if p some code 我周围的一些人倾向于使用显式比较 因此代码如下所示 if FOO p some code 其中 FOO 是其中之一false FALSE 0 0 0 NULL etc
  • 从 Linux 内核模块中调用用户空间函数

    我正在编写一个简单的 Linux 字符设备驱动程序 以通过 I O 端口将数据输出到硬件 我有一个执行浮点运算的函数来计算硬件的正确输出 不幸的是 这意味着我需要将此函数保留在用户空间中 因为 Linux 内核不能很好地处理浮点运算 这是设
  • 标准化 UTF-8 到底是什么?

    The 重症监护室项目 http userguide icu project org transforms normalization 现在也有一个PHP库 http us php net manual en class normalize
  • 具有交替类型的可变参数模板参数包

    我想知道是否可以使用参数包捕获交替参数模式 例如 template
  • 如何在 Xaml 文本中添加电子邮件链接?

    我在 Windows Phone 8 应用程序中有一些大文本 我希望其中有电子邮件链接 例如 mailto 功能 这是代码的一部分
  • Cmake 链接共享库:包含库中的头文件时“没有这样的文件或目录”

    我正在学习使用 CMake 构建库 构建库的代码结构如下 include Test hpp ITest hpp interface src Test cpp ITest cpp 在 CMakeLists txt 中 我用来构建库的句子是 f
  • 如何在非控制台应用程序中查看 cout 输出?

    输出到调试窗口似乎相当繁琐 我在哪里可以找到cout如果我正在编写非控制台信息 则输出 Like double i a b cout lt lt b lt lt endl I want to check out whether b is z
  • 调用堆栈中的“外部代码”是什么意思?

    我在 Visual Studio 中调用一个方法 并尝试通过检查调用堆栈来调试它 其中一些行标记为 外部代码 这到底是什么意思 方法来自 dll已被处决 外部代码 意味着该dll没有可用的调试信息 你能做的就是在Call Stack窗口中单
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • 如何部署“SQL Server Express + EF”应用程序

    这是我第一次部署使用 SQL Server Express 数据库的应用程序 我首先使用实体 框架模型来联系数据库 我使用 Install Shield 创建了一个安装向导来安装应用程序 这些是我在目标计算机中安装应用程序所执行的步骤 安装
  • 如何从 ODBC 连接获取可用表的列表?

    在 Excel 中 我可以转到 数据 gt 导入外部数据 gt 导入数据 然后选择要使用的数据源 然后在提供登录信息后 它会给我一个表格列表 我想知道如何使用 C 以编程方式获取该列表 您正在查询什么类型的数据源 SQL 服务器 使用权 看

随机推荐

  • 在PYQT5 python中设置文本

    我的程序是基于人工智能的 我在 stackoverflow 中看到了所有关于 Pyqt 设置文本的帖子 我想在单击 自动 AI 按钮时在 lineEdit 中设置文本 def AI click self self lineEdit setT
  • 无法加载捆绑包中的 NIB

    由于未捕获的异常 NSInternalInconsistencyException 而终止应用程序 原因 无法在捆绑包中加载 NIB NSBundle 已加载 名称为 ViewLecturer ViewLecturer viewLectur
  • 使用 Spring Security SAML 将请求参数添加到 SAML 请求

    我需要向 SAML 请求添加请求参数 例如 locale en 以便让登录页面显示正确的语言 我怎么做 我尝试将该属性添加到作为参数发送给开始方法 SamlEntryPoint 的 HttpServletRequest 但这似乎不起作用 有
  • iOS 静态库 + CocoaPods 和重复符号错误

    我正在开发一个静态库 iOS 框架 Jeff Verkoeyen 方式 并且添加了 CocoaPods 来管理依赖项 这是与有关重复符号的其他问题的最大区别 当使用 CocoaPods 的客户端使用我的静态库 框架和我也在使用的第三方库时
  • QSound::play("soundpath") 调用有效,但 QSound 对象无效

    我正在尝试播放声音QSound模块 它尝试使用此代码并工作 QSound play sounds sources BeepSound wav 但我想要这个但不起作用 我创建了一个动态实例QSound并演奏 sounds new QSound
  • java 中的 new Date() 转儒略日期格式

    我需要将 new Date 转换为儒略日期格式 java 中是否有为此构建的函数 我的确切要求是 以儒略日期格式 0YYDDD 表示文件的创建日期 0 数字零 YY 年份的最后两位数 DDD 年内的天数 最多可以是传输日期之前的 7 个日历
  • 如何在 tkinter 中创建日期选择器?

    tkinter 应用程序是否有任何标准方式允许用户选择日期 如果有人仍然需要这个 这里有一个简单的代码 用于使用 tkcalendar 包创建日历和 DateEntry 小部件 pip install tkcalendar 用于安装包 tr
  • 使用 Spring Integration 同时读取 CSV 文件

    我想使用 spring 集成同时处理 CSV 文件 每行将被转换为单独的消息 所以假设我在 CSV 文件中有 10K 行 我想启动 10 个线程 每一行都会传递给这个线程 如果有人向我展示任何示例 那就太好了 Thanks 从 开始Spri
  • 如何编写 Delphi Galileo IDE Expert?

    HI 我想为 D2007 D2009 又名 Galileo IDE 编写一个小型的 Delphi IDE Expert 以便显示一个带有 TMemo 实例的窗口 以及上面备忘录中表单设计器的所有组件名称和类 所选组件将标有 专家应出现在菜单
  • 在 AWS Sagemaker 上安装 graphviz

    我在使用 Python3 的 Jupyter 笔记本上尝试使用如下代码绘制一棵树 import xgboost as xgb from xgboost import plot tree plot tree model num trees 4
  • 如何以编程方式确定 ndb 属性是否为多值

    我正在将应用程序从 Datastore 转换为 ndb 并且在 xml 导入例程中遇到问题 问题是我无法以编程方式确定 ndb model 类的属性是否是多值属性 我怀疑这是由于缺乏基本的 Python 技能 因为到目前为止我编写的代码显示
  • 将 Windows 7 手机的联系人列表集成到应用程序中

    如何获取 Windows 7 手机的联系人列表以在 win7 手机应用程序中使用 对于早期版本的 Windows Phone 7 SDK 只能使用选择器检索电话号码或电子邮件地址以及其他一些信息 现在 使用 7 1 Mango SDK 可以
  • 如何自动将版本号插入AssemblyName

    我试图建立在这个问题的基础上 从 MSBuild 中的文件读取单个值 我的目标是有一个位置来放置多个项目中使用的版本号 并且我还希望其中一个项目的 DLL 文件名中包含版本号的一部分 根据上面的问题 我已经得到了第一部分 但我在第二部分上遇
  • Spring MVC,从请求生成表单支持对象?

    我正在使用 Spring MVC 2 5 并且尝试从 GET 请求加载 JSTL 表单对象 我有 Hibernate POJO 作为我的支持对象 请求中有一个页面指向另一个带有类 id 行主键 的页面 该请求类似于 newpage htm
  • 通过 R 在 SQL 查询中粘贴值

    我有以下包含 AxiomaID 的数据框 x lt c 0123 234 2348 345 3454 并尝试在 R 中运行以下 SQL 查询 SQL6 lt data frame sqlQuery myConn SELECT top 10
  • 创建 bean 'entityManagerFactory' 时出错,嵌套 HibernateException:无法获取默认的 Bean 验证工厂

    关于这个主题已经提出了很多问题 但似乎没有一个能解决我的问题 我尝试使用 Maven Spring Hibernate 和 JPA 以及 Mysql 5 5 构建一个示例项目 这是一个测试桌面应用程序 我不知道 我哪里错了 mvn clea
  • 在php中将数组转换为png [关闭]

    很难说出这里问的是什么 这个问题模棱两可 含糊不清 不完整 过于宽泛或言辞激烈 无法以目前的形式合理回答 如需帮助澄清此问题以便重新打开 访问帮助中心 我想知道如何将颜色数组转换为 png 图像文件 该数组称为 pixels 请帮我 im
  • PhoneGap Android Plugin - 关闭插件 Activity

    我编写了一个 PhoneGap Android 插件 并在那里打开了第二个活动 cordova getActivity runOnUiThread new Runnable Override public void run Context
  • PHP 如何知道何时删除会话?

    我认为会话存储在客户端 因为浏览器关闭时会话会被 删除 然而 今天我被告知这不是真的 会话存储在服务器上 那么会话如何知道浏览器何时关闭 以便会话被删除呢 事实并非如此 有两个因素在起作用 the cookie 的生命周期在客户端 该 co
  • 如何使 C 中的方法链流畅?

    现有的 C API 如下所示 data typedef struct int properties Widget interface Widget SetWidth Widget const w int width return w Wid