如何在通用标头中 typedef 实现定义的结构?

2024-02-22

我有一个 C 项目,旨在可移植到各种(PC 和嵌入式)平台。

应用程序代码将使用具有特定于平台的实现的各种调用,但共享通用(通用)API 以帮助实现可移植性。我正在尝试确定声明函数原型和结构的最合适方法。

到目前为止,这是我想到的:

main.c:

#include "generic.h"

int main (int argc, char *argv[]) {
    int  ret;
    gen_t  *data;

    ret = foo(data);
    ...
}

通用.h:(与平台无关的包括)

typedef struct impl_t gen_t;

int foo (gen_t *data);

impl.h:(特定于平台的声明)

#include "generic.h"

typedef struct impl_t {
    /* ... */
} gen_t;

impl.c:(特定于平台的实现)

int foo (gen_t *data) {
    ...
}

Build:

gcc -c -fPIC -o platform.o impl.c
gcc -o app  main.c platform.o

现在,这似乎有效......因为它编译正常。但是,我通常不会标记我的结构,因为它们永远不会在外部访问typedef倒是别名。这是一个小问题,但我想知道是否有办法用匿名结构达到相同的效果?

我也要求后代,因为我搜索了一段时间,我找到的最接近的答案是:(Link https://stackoverflow.com/questions/9749943/how-to-make-a-structure-extern-and-define-its-typedef)

就我而言,这不是正确的方法,因为应用程序特别不应该直接包含实现标头——重点是将程序与平台分离。

我看到了其他一些不太理想的方法来解决这个问题,例如:

通用.h:

#ifdef PLATFORM_X
#include "platform_x/impl.h"
#endif

/* or */

int foo (struct impl_t *data);

这些似乎都不是特别吸引人,而且绝对不是我的风格。虽然我不想逆流而上,但当可能有更好的方法来准确实现我的想法时,我也不希望出现冲突的风格。所以我认为 typedef 解决方案走在正确的轨道上,它只是我留下的结构标签包袱。

想法?


您当前的技术是正确的。尝试使用匿名(未标记)struct失败了你想要做的事情 - 你必须暴露定义的细节struct无处不在,这意味着您不再拥有不透明的数据类型。


In a comment https://stackoverflow.com/questions/26813097/how-do-i-typedef-an-implementation-defined-struct-in-a-generic-header/26814210?noredirect=1#comment42202359_26814210, 用户3629249 https://stackoverflow.com/users/3629249/user3629249 said:

头文件包含的顺序意味着存在对结构体的前向引用generic.h文件;也就是说,在定义结构之前,就使用它。这不太可能编译。

对于问题中显示的标题,此观察结果是不正确的;对于样本来说是准确的main()代码(在添加此响应之前我没有注意到)。

关键点是所示的接口函数接受或返回指向类型的指针gen_t,这又映射到struct impl_t指针。只要客户端代码不需要为该结构分配空间,或者取消引用指向结构的指针来访问该结构的成员,客户端代码就不需要知道该结构的详细信息。将结构类型声明为现有就足够了。您可以使用其中任何一个来声明struct impl_t:

struct impl_t;

typedef struct impl_t gen_t;

后者还引入了别名gen_t对于类型struct impl_t。也可以看看C 标准的哪一部分允许编译此代码? https://stackoverflow.com/questions/12200096/which-part-of-the-c-standard-allows-this-code-to-compile and C标准是否认为存在一两个struct uperms此标头中的条目类型? https://stackoverflow.com/questions/11697705/does-the-c-standard-consider-that-there-are-one-or-two-struct-uperms-entry-typ

原本的main()问题中的程序是:

int main (int argc, char *argv[]) {
    int  ret;
    gen_t  data;

    ret = foo(&data);
    …
}

此代码无法编译gen_t作为不透明(非指针)类型。它可以与以下内容一起使用:

typedef struct impl_t *gen_t;

它不会编译:

typedef struct impl_t gen_t;

因为编译器必须知道结构有多大才能为其分配正确的空间data,但是编译器无法根据不透明类型的定义知道该大小。 (看这是一个好主意吗typedef指点? https://stackoverflow.com/questions/750178/typedef-pointers-a-good-idea/750208#750208用于定义指向结构的指针。)

就这样main()代码应该更像是:

#include "generic.h"

int main(int argc, char **argv)
{
    gen_t *data = bar(argc, argv);
    int ret = foo(data);
    ...
}

其中(对于本例)bar()定义为extern gen_t *bar(int argc, char **argv);,所以它返回一个指向不透明类型的指针gen_t.

关于是否始终使用更好的观点存在分歧struct tagname或使用typedef为了名字。 Linux 内核是一个不使用typedef机制;所有结构都明确struct tagname。另一方面,C++ 不再需要显式的typedef;写作:

struct impl_t;

在 C++ 程序中意味着名称impl_t现在是一个类型的名称。由于不透明的结构类型需要标签(或者您最终会使用void *对于所有事情,这出于多种原因都是不好的,但主要原因是您失去了所有类型安全性void *;记住,typedef引入了基础类型的别名,而不是新的不同类型),我用 C 编写的代码模拟了 C++:

typedef struct Generic Generic;

I avoid using the _t suffix on my types because POSIX reserves the _t for the implementation to use* (see also What does a type followed by _t represent? https://stackoverflow.com/questions/231760/what-does-a-type-followed-by-t-underscore-t-represent/231807#231807). You may be lucky and get away with it. I've worked on code bases where types like dec_t and loc_t were defined by the code base (which was not part of the implementation — where 'the implementation' means the C compiler and its supporting code, or the C library and its supporting code), and both those types caused pain for decades because some of the systems where the code was ported defined those types, as is the system's prerogative. One of the names I managed to get rid of; the other I didn't. 'Twas painful! If you must use _t (it is a convenient way to indicate that something is a type), I recommend using a distinctive prefix too: pqr_typename_t for some project pqr, for example.

* See the bottom line of the second table in The Name Space http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_02_02 in the POSIX standard.

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

如何在通用标头中 typedef 实现定义的结构? 的相关文章

  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • 属性对象什么时候创建?

    由于属性实际上只是附加到程序集的元数据 这是否意味着属性对象仅根据请求创建 例如当您调用 GetCustomAttributes 时 或者它们是在创建对象时创建的 或者 前两个的组合 在由于 CLR 的属性扫描而创建对象时创建 从 CLR
  • 如何在 Unity 中从 RenderTexture 访问原始数据

    问题的简短版本 我正在尝试访问 Unity 中 RenderTexture 的内容 我一直在使用 Graphics Blit 使用自己的材质进行绘制 Graphics Blit null renderTexture material 我的材
  • 嵌入式系统中的malloc [重复]

    这个问题在这里已经有答案了 我正在使用嵌入式系统 该应用程序在 AT91SAMxxxx 和 cortex m3 lpc17xxx 上运行 我正在研究动态内存分配 因为它会极大地改变应用程序的外观 并给我更多的力量 我认为我唯一真正的路线是为
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • 为什么禁止在 constexpr 函数中使用 goto?

    C 14 对你能做什么和不能做什么有规则constexpr功能 其中一些 没有asm 没有静态变量 看起来相当合理 但标准也不允许goto in constexpr功能 即使它允许其他控制流机制 这种区别背后的原因是什么 我以为我们已经过去
  • 如何在 WPF RichTextBox 中跟踪 TextPointer?

    我正在尝试了解 WPF RichTextBox 中的 TextPointer 类 我希望能够跟踪它们 以便我可以将信息与文本中的区域相关联 我目前正在使用一个非常简单的示例来尝试弄清楚发生了什么 在 PreviewKeyDown 事件中 我
  • .Net Core / 控制台应用程序 / 配置 / XML

    我第一次尝试使用新的 ConfigurationBuilder 和选项模式进入 Net Core 库 这里有很多很好的例子 https docs asp net en latest fundamentals configuration ht
  • 线程、进程和 Application.Exit()

    我的应用程序由主消息循环 GUI 和线程 Task Factory 组成 在线程中我调用一些第三方应用程序var p new Process 但是当我调用Application Exit 在消息循环中 我可以看到在线程中启动的进程仍在内存中
  • Windows 10 中 Qt 桌面应用程序的缩放不当

    我正在为 Windows 10 编写一个简单的 Qt Widgets Gui 应用程序 我使用的是 Qt 5 6 0 beta 版本 我遇到的问题是它根本无法缩放到我的 Surfacebook 的屏幕上 这有点难以判断 因为 SO 缩放了图
  • 网络参考共享类

    我用 Java 编写了一些 SOAP Web 服务 在 JBoss 5 1 上运行 其中两个共享一个类 AddressTO Web 服务在我的 ApplycationServer 上正确部署 一切都很顺利 直到我尝试在我的 C 客户端中使用
  • C 中的位移位

    如果与有符号整数对应的位模式右移 则 1 vacant bit will be filled by the sign bit 2 vacant bit will be filled by 0 3 The outcome is impleme
  • 什么是 C 语言的高效工作流程? - Makefile + bash脚本

    我正在开发我的第一个项目 该项目将跨越多个 C 文件 对于我的前几个练习程序 我只是在中编写了我的代码main c并使用编译gcc main c o main 当我学习时 这对我有用 现在 我正在独自开展一个更大的项目 我想继续自己进行编译
  • 将应用程序从 Microsoft Access 迁移到 VB 或 C#.NET

    我目前正试图说服管理层需要将我们的应用程序之一移植到 NET 该应用程序已经发展成为 Access 中的一个庞然大物 SQL 后端 拥有 700 个链接表 650 个表单 子表单 130 个模块和 850 个查询 我几乎知道这样做的所有主要
  • 在 URL 中发送之前对特殊字符进行百分比编码

    我需要传递特殊字符 如 等 Facebook Twitter 和此类社交网站的 URL 为此 我将这些字符替换为 URL 转义码 return valToEncode Replace 21 Replace 23 Replace 24 Rep
  • EPPlus Excel 更改单元格颜色

    我正在尝试将给定单元格的颜色设置为另一个单元格的颜色 该单元格已在模板中着色 但worksheet Cells row col Style Fill BackgroundColor似乎没有get财产 是否可以做到这一点 或者我是否必须在互联
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • 方法参数内的变量赋值

    我刚刚发现 通过发现错误 你可以这样做 string s 3 int i int TryParse s hello out i returns false 使用赋值的返回值是否合法 Obviously i is but is this th
  • 如何使用 ReactiveList 以便在添加新项目时更新 UI

    我正在创建一个带有列表的 Xamarin Forms 应用程序 itemSource 是一个reactiveList 但是 向列表添加新项目不会更新 UI 这样做的正确方法是什么 列表定义 listView new ListView var
  • 如何将字符串“07:35”(HH:MM) 转换为 TimeSpan

    我想知道是否有办法将 24 小时时间格式的字符串转换为 TimeSpan 现在我有一种 旧时尚风格 string stringTime 07 35 string values stringTime Split TimeSpan ts new

随机推荐

  • Kendo UI 参考在 Razor 视图中不起作用

    我正在尝试创建 Telerik 网格视图 但是当我去参考剑道时它无法识别它 当我尝试引用剑道时 Visual Studio 给出错误 这是代码 Html Kendo Grid 下面是错误 System Web Mvc HtmlHelper
  • MongoDB 的插入安全模式有多安全?

    我正在开发一个项目 其中包含一些重要数据 这意味着如果灯或服务器出现故障 我们不会丢失任何数据 我们使用 MongoDB 作为数据库 我想确保插入后我的数据位于数据库中 如果未插入一个元素 则回滚整个批次 我知道 Mongo 背后的理念是我
  • 在 JCuda 中加载多个模块不起作用

    在jCuda中 可以将cuda文件加载为PTX或CUBIN格式并调用 启动 global 来自 Java 的函数 内核 考虑到这一点 我想使用 JCuda 开发一个框架来获取用户的 device 函数在一个 cu文件在运行时加载并运行它 我
  • log4j2.properties 文件的更改导致 elasticsearch 失败

    我已经安装了elasticsearch 6 6 0 和CentOS 7 我想添加一些用于旋转日志的属性 例如大小为50MB的旋转和压缩 但是 如果我向 etc elasticsearch log4j2 properties 文件添加更多配置
  • LINQ 分组依据表达式语法

    我有一个与此类似的 T SQL 查询 SELECT r id r name count FROM RoomBindings GROUP BY r id r name 我想使用 LINQ 做同样的事情 到目前为止我到达这里 var rooms
  • 链接错误“未定义引用‘__gxx_personality_v0’”和g++[重复]

    这个问题在这里已经有答案了 可能的重复 链接上未定义符号 gxx personality v0 https stackoverflow com questions 203548 undefined symbol gxx personalit
  • 带有母版页的 ASP.Net Web 表单中的 JQuery

    我有一个名为 CoursesPage aspx 的页面 它有一个母版页 在 CoursesPage aspx 中 我在课程名称上使用自动完成 jquery 以便更好地搜索 这是我的脚本代码
  • 如何从命令行启动的 GUI 应用程序写入 StdOut?

    我正在 Delphi 7 中编写一个标准的 Windows 应用程序 如果我正在编写控制台应用程序 我可以调用以下命令来输出到命令行或输出文件 writeln Some info 如果我从从命令行启动的标准 GUI 应用程序执行此操作 则会
  • Rails send_file 不播放 mp4

    我有一个 Rails 应用程序 可以保护上传的视频 将它们放入私人文件夹中 现在我需要播放这些视频 当我在控制器中执行以下操作时 def show video Video find params id send file video ful
  • 使用 Json 比较 C# 对象

    我想比较两个对象而不实现 Equals 方法 以这种方式比较它们的缺点是什么 1 用Json序列化它们 2 结果对比 thanks 以这种方式比较它们有什么缺点 失去速度 将对象转换为 JSON 字符串然后比较它们比通过属性等于属性要慢得多
  • SUM() 基于与 SELECT 不同的条件

    您好 有没有一种方法可以根据与 SELECT 语句其余部分不同的条件进行 SUM total points 计算 所以我想要为 非常感谢您的帮助 SELECT members member id members teamname SUM t
  • 没有这样的桶:/usr/local/Cellar/git

    我已经为此苦苦挣扎了几个小时 我在 Mac 上使用 Yosemite 我安装了自制软件 并且使用 git 就很好 我尝试做 git add i我得到了这个错误 无法在 INC 中找到 Git pm INC 包含 所以我按照这里的说明进行操作
  • E:软件包“libssl1.1”没有安装候选者

    sudo apt get install libssl1 1 正在阅读包裹清单 完成 构建依赖树 完成 读取状态信息 完成 软件包 libssl1 1 不可用 但被另一个软件包引用 这可能意味着该包丢失 已过时或 只能从其他来源获得 E 软
  • 回收和赋值函数(`split<-`)

    有人可以解释一下这一行 R 代码是如何工作的吗 split dat f lt lapply split dat f max 我以为这只是一个回收规则 但实际上我无法理解 数据示例 dat lt c 1 2 3 100 200 300 f l
  • 将 pyodbc 连接到 Postgres

    尝试使用 pyodbc 连接到 Postgres 我可以使用 isql 连接到数据库 echo select 1 isql v my connector Returns Connected sql statement help tablen
  • 如果 URL 包含单词则隐藏 div

    如果页面的 url 包含某个单词 我需要隐藏一个 div 感谢这个网站 我已经能够成功地找到该网址是否包含该单词 这段代码的工作原理 但由于某种原因 隐藏 div 不起作用 如下所示 div div
  • 如何将 SVG 转换为 png 或 jpg

    我尝试过使用batik 但我得到的是空 png 文件 我还包含了所有必需的罐子 我的代码是 import org apache batik transcoder image PNGTranscoder import org apache b
  • 在 ChartJS 中对标签进行换行时,工具提示中出现不需要的逗号

    我有一些标签很长的图 我使用在此站点中找到的答案将标签换成新行 这是我的标签的示例 labels Utensilios para escrita e artes Faz de conta Jogos Materiais n o estrut
  • Swift 结构到 NSData 并返回

    我有一个包含一个结构体和一个结构体的结构体NSObject我想序列化为NSData object struct Packet var name String var index Int var numberOfPackets Int var
  • 如何在通用标头中 typedef 实现定义的结构?

    我有一个 C 项目 旨在可移植到各种 PC 和嵌入式 平台 应用程序代码将使用具有特定于平台的实现的各种调用 但共享通用 通用 API 以帮助实现可移植性 我正在尝试确定声明函数原型和结构的最合适方法 到目前为止 这是我想到的 main c