关于opaque

有这么一行代码:

typedef struct __opaque yaffs_DIR;
   

而该头文件中并没有任何关于struct __opaque的定义,搜寻了一下系统里的其他头文件,也没有找到。google了一下才找到一篇介绍这种用法的文章Programming Tools - Opaque Pointers

opaque直译的意思是不透明的,C语言中允许通过typedef申明一个抽象的结构体类型,如上例所示,你无需定义struct __opaque的具体实现,就能在其他函数的声明中使用该数据类型的指针。注意,只能是指针,如果是void foo(yaffs_DIR dir),系统就会提示error: dir has incomplete type

在库文件中,opaque类型的实现如下面的代码所示:

typedef struct
   
{
   
    __u32 magic;
   
    yaffs_dirent de;            /* directory entry being used by this dsc */
   
    char name[NAME_MAX+1];      /* name of directory being searched */
   
    yaffs_Object *dirObj;       /* ptr to directory being searched */
   
    yaffs_Object *nextReturn;   /* obj to be returned by next readddir*/
   
    int offset;
   
    struct list_head others;    
   
} yaffsfs_DirectorySearchContext;
   

   
     
   
yaffs_DIR *dir = NULL;
   
yaffsfs_DirectorySearchContext *dsc = NULL;
   
...
   
dsc = YMALLOC(sizeof(yaffsfs_DirectorySearchContext));
   
dir = (yaffs_DIR *)dsc;
   

代码中为抽象类型yaffs_DIR的指针分配了一个具体类型yaffsfs_DirectorySearchContext的空间。而这一层对使用者是不可见的,也许这是opaque这个名字的由来,这种做法可以提高库文件升级过程中对外接口的稳定性。

最后提一点,opaque并不是一个关键词,你可以任意使用其他名字,只是用opaque更能明确地表示这是一个希望对用户隐藏内部结构的数据类型。


Programming Tools - Opaque Pointers

One of the most powerful concepts when writing software is abstraction - hiding the details of a system behind a simpler interface. This article shows you how to use a language feature of C (and C++) to provide a very powerful form of abstraction for use in libraries: opaque pointers.

C/C++ has an interesting language feature. If you declare a typedef of a structure pointer, you don't need to provide that structure's definition. For example:

typedef struct _hidden_struct *handle;

This has declared a new type, handle, which is a pointer to a struct _hidden_struct. What's interesting about this? Mainly the fact that you can now use this handle type without ever having a definition of the structure it's a pointer to. Why is this powerful? That's what this article will show you (some people might already see why). Please note that the typedef above (and those used throught this article) aren't strictly needed to hide the structure's internal definition - you can just keep using the struct keyword as part of the name. However, the typedefs are used to make an abstraction away from the fact that there's a structure at all, instead of turning it into a "handle."

Sample problem

To really see the power of opaque pointers, we'll need a problem to solve. Let's suppose we want to make an image library that loads and saves bitmaps. We know that as time goes on this library will need to be able to do more things (new image types, basic transforms) and we want to preserve both compile time and runtime compatibility for this library. This means that if we have an older application, it should work with a new shared library and that if that older application is rebuilt against the new library and headers, it should still build without errors.

What about C++?

Before this goes much further, I need to address the waving hands of all the C++ fans out there, who might be thinking: "C++ already lets me do all of this behind a nice class interface with inheritance and other nice C++ language features". And this is pretty much true in the case of compile-time compatibility. But, because C++ doesn't let you separate the public and private definitions of a class, the way the class is declared changes its runtime behavior (class size, vtable offsets, etc.) - you can't provide runtime compatibility without a lot of tender care (and sometimes special compilers). So, this is still useful to C++ people, even if the slant is more towards those of us using C.

Okay, back to the library. When people design libraries like this, they'll often declare a structure that will get filled in by the library and an API for manipulating this structure. For example:

typedef struct

{

void *ptr;

int size;

int bytes_per_pixel;

} bitmap_t;

int bitmap_load_file( char *filename, bitmap_t *bitmap );

int bitmap_save_file( char *filename, bitmap_t *bitmap );

So, to use this library, you would declare a bitmap_t variable and invoke bitmap_load_file() with a filename and a pointer to the bitmap_t that was declared. For example:

bitmap_t bitmap;

int ret;

ret = bitmap_load_file( "sample.bmp", &bitmap );

Then the user of the library could simply access the pointer inside the structure and, along with the size and bytes_per_pixel members, go to work on the image. However, because the contents of bitmap_t are known publicly, if the library is updated with new entries in that structure, then the size of the structure will have been changed. And applications that use an updated shared library will probably crash or Other Bad Things when they call into the new library with a structure of a smaller (different) size.

Alternatively, the API could be defined to take a structure size:

int bitmap_load_file( char *filename, bitmap_t *bitmap, int size );

int bitmap_save_file( char *filename, bitmap_t *bitmap, int size );

and would be used like this:

ret = bitmap_load_file( "sample.bmp", &bitmap, sizeof( bitmap_t ) );

Which effectively tags the version of the library by using the size of the structure. However, this makes for a terrible support nightmare inside the library where the structure size has to be checked and different code paths are taken based on this structure size. This leads to a lot of code bloat.

We could also try to avoid the structure size issue by padding the structure with some "reserved" space:

typedef struct

{

void *ptr;

int size;

int bytes_per_pixel;

char reserved[64];

} bitmap_t;

But this is only a temporary fix. What if we didn't choose a value that's large enough? Then we're back to the case where a new library causes problems. If we're liberal with our reserved space, then we waste memory. (Since you're reading this on a QNX web page, I'm guessing that wasting memory doesn't sit well with you either.)

Another problem common to all of these approaches is what can occur if the layout of the structure changes. Say a new version of the library is built with a structure definition that looks like this:

typedef struct

{

int version;

void *ptr;

int size;

int bytes_per_pixel;

} bitmap_t;

Then the compiled apps will also get "confused", since what was previously the structure member ptr is now version, and so on. The position of a structure member within a structure is important. Seems pretty much impossible to meet our goals, huh? Fear not, loyal readers, the situation isn't that dire!

Hiding the structure's internals

The common thread to all the situations above was that the compiled application was aware of the size of the structure and the location in the structure of the structure members. So we need to hide the internals of the structure and provide access functions to get the important data out of the structure.

Let's try out a new public interface:

typedef struct _internal_bitmap * bitmap_t;

int bitmap_alloc( bitmap_t *bitmap );

int bitmap_free( bitmap_t *bitmap );

int bitmap_load_file( bitmap_t bitmap, char *filename );

int bitmap_save_file( bitmap_t bitmap, char *filename );

int bitmap_get_ptr( bitmap_t bitmap, void **ptr );

int bitmap_get_size( bitmap_t bitmap, int *size );

int bitmap_get_bpp( bitmap_t bitmap, int *bpp );

And now we can maintain a private interface that applications never get to see or use, only the library:

struct _internal_bitmap

{

void *ptr;

int size;

int bytes_per_pixel;

}

Did you notice the opaque pointer? Also, notice we've added "access functions" to get the interesting data from the new bitmap "handle"? It's pretty obvious now that we're passing in a bitmap_t (a structure pointer) as a handle to the library, but the alloc and free functions are a little confusing for people.

When we declare a bitmap_t, we're really just declaring a pointer to a structure, so we need to provide some memory for that pointer to point at. Here are the guts of the bitmap_alloc() function:

int bitmap_alloc( bitmap_t *bitmap )

{

struct _internal_bitmap *handle;

handle = ( struct _internal_bitmap * )malloc( sizeof( *handle ) );

if( handle == NULL )

{

return -1;

}

memset( handle, 0, sizeof( *handle ) );

*bitmap = handle;

return 0;

}

Since a bitmap_t is just a struct pointer, we allocate the proper sized struct (which we can do - this code is part of the library and it knows how big the structure is). Once we've verified that the malloc() didn't fail, we assign the newly allocated structure to the bitmap_t pointer. So when the application calls this function, it will get back the proper sized structure to pass into the rest of the library functions.

Here's an example of an "access function" that uses the allocated bitmap handle:

int bitmap_get_ptr( bitmap_t bitmap, void **ptr )

{

if( ptr == NULL ){  return -1; }

*ptr = bitmap->ptr;

return 0;

}

Since the library knows the definition of the _internal_bitmap structure, it can directly access its members. If you tried to access the internals of the bitmap_t handle in application code, the compiler would return an error, because it has no idea how the structure is organized or what any of its members are named.

For the last bit of code, I'll write a function that loads a bitmap, sets all the pixels to 255, and writes the bitmap back:

int turn_bitmap_white( char *filename )                                              {

int ret, i;

bitmap_t bitmap;

unsigned char *ptr;

int size;

ret = bitmap_alloc( &bitmap );

if( ret )

return ret;

ret = bitmap_load_file( bitmap, filename );

if( ret )

return ret;

ret = bitmap_get_ptr( bitmap, (void **)&ptr );

ret |= bitmap_get_size( bitmap, &size );

if( ret )

return ret;

for( i=0; i<size; i++ )

{

ptr[i] = 255;

}

ret = bitmap_save_file( bitmap, filename );

if( ret )

return ret;

bitmap_free( &bitmap );

return 0;

}

Problem solved

So, we've solved our problem. If we change the structure layout, we'll be okay, since the application code can't access the internals of the structure directly and must use "access functions" to get at the internal data.

If we change the size of the structure, we'll be okay, since the library itself allocates the memory for the structure and knows the proper size of the structure to allocate for the given version of the library. You can now replace the library (in shared library form) without having to rebuild any applications. And you can rebuild applications against the library without change. Now, this does assume that you haven't changed the API to your library - opaque pointers are a powerful tool, but they can't perform magic.

Lastly, the use of opaque pointers enforces good programming practice by providing a defined interface (abstraction) between the application and the library. This is usually a good method even when doing your own projects, since it lets you easily separate functionality for future projects!

 

这是一个基于AI视觉识别与3D引擎技术打造的沉浸式交互圣诞装置。 简单来说,它是一棵通过网页浏览器运行的数字智慧圣诞树,你可以用真实的肢体动作来操控它的形态,并将自己的回忆照片融入其中。 1. 核心技术组成 这个作品是由三个尖端技术模块组成的: Three.js 3D引擎:负责渲染整棵圣诞树、动态落雪、五彩挂灯和树顶星。它创建了一个具备光影和深度感的虚拟3D空间。 MediaPipe AI手势识别:调用电脑摄像头,实时识别手部的21个关键点。它能读懂你的手势,如握拳、张开或捏合。 GSAP动画系统:负责处理粒子散开与聚合时的平滑过渡,让成百上千个物体在运动时保持顺滑。 2. 它的主要作用与功能 交互式情感表达: 回忆挂载:你可以上传本地照片,这些照片会像装饰品一样挂在树上,或者像星云一样环绕在树周围。 魔法操控:握拳时粒子迅速聚拢,构成一棵挺拔的圣诞树;张开手掌时,树会瞬间炸裂成星光和雪花,照片随之起舞;捏合手指时视线会拉近,让你特写观察某一张选中的照片。 节日氛围装饰: 在白色背景下,这棵树呈现出一种现代艺术感。600片雪花在3D空间里缓缓飘落,提供视觉深度。树上的彩色粒子和白色星灯会周期性地呼吸闪烁,模拟真实灯串的效果。 3. 如何使用 启动:运行代码后,允许浏览器开启摄像头。 装扮:点击上传照片按钮,选择温馨合照。 互动:对着摄像头挥动手掌可以旋转圣诞树;五指张开让照片和树化作满天星辰;攥紧拳头让它们重新变回挺拔的树。 4. 适用场景 个人纪念:作为一个独特的数字相册,在节日陪伴自己。 浪漫惊喜:录制一段操作手势让照片绽放的视频发给朋友。 技术展示:作为WebGL与AI结合的案例,展示前端开发的潜力。
【顶级EI复现】计及连锁故障传播路径的电力系统 N-k 多阶段双层优化及故障场景筛选模型(Matlab代码实现)内容概要:本文提出了一种计及连锁故障传播路径的电力系统N-k多阶段双层优化及故障场景筛选模型,并提供了基于Matlab的代码实现。该模型旨在应对复杂电力系统中可能发生的N-k故障(即多个元件相继失效),通过构建双层优化框架,上层优化系统运行策略,下层模拟故障传播过程,从而实现对关键故障场景的有效识别与筛选。研究结合多阶段动态特性,充分考虑故障的时序演化与连锁反应机制,提升了电力系统安全性评估的准确性与实用性。此外,模型具备良好的通用性与可扩展性,适用于大规模电网的风险评估与预防控制。; 适合人群:电力系统、能源互联网及相关领域的高校研究生、科研人员以及从事电网安全分析、风险评估的工程技术人员。; 使用场景及目标:①用于电力系统连锁故障建模与风险评估;②支撑N-k故障场景的自动化筛选与关键脆弱环节识别;③为电网规划、调度运行及应急预案制定提供理论依据和技术工具;④服务于高水平学术论文复现与科研项目开发。; 阅读建议:建议读者结合Matlab代码深入理解模型构建细节,重点关注双层优化结构的设计逻辑、故障传播路径的建模方法以及场景削减技术的应用,建议在实际电网数据上进行测试与验证,以提升对模型性能与适用边界的认知。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值