Public API 设计out参数结构类型包含char *

Public API design with out parameter of a structure type containing char *

我正在为事件通知设计 public 平台独立 API,目前有以下接口:

enum ns_event_type{
    deleted,
    moved
};

struct ns_event_meta{
    enum ns_event_type type;
    size_t internal_buffer_size;
    void *internal_buffer; /* Memory to store either deleted_path or moved */
    union {
        const char *deleted_path;
        struct {
             const char *moved_from;
             const char *moved_to;
        } moved;
    } event_data;
};

typedef struct ns_event_queue ns_event_queue;

int ns_take_event(ns_event_queue *queue, struct ns_event_meta *meta_out);

我设计 struct ns_event_meta 的方式是包含一个原始内存缓冲区。该缓冲区用作 const char *readable_path 或(const char *moved_fromconst char *moved_to)的容器。

因此,如果 void *interval_buffer 大小不足以保存正在执行的事件的路径,则函数 ns_take_event returns -1 和调用者预计增长 void *internal_buffer;

我看到的关于此设计的问题是库的客户端可以访问原始缓冲区 void *internal_buffer,而客户端实际上并不打算访问该缓冲区。相比之下,事件元旨在通过成员 enum ns_event_type type;event_data.

进行检查

是否有其他方法可以解决此类设计问题?

在我看来,您有两个相互矛盾的要求。

  1. 您希望用户为 internal_buffer 分配内存(我假设还更新 internal_buffer_size)。

  2. 您想对用户隐藏 internal_buffer

你不能两者都做!如果用户要处理内存分配,则不能 "hide" 缓冲区。所以要么你必须接受用户知道缓冲区,要么你必须在你的代码中分配内存。

隐藏内部数据的一种众所周知的方法是使用不透明数据类型。基本思想是将私有数据成员收集在一个结构中,例如struct private。用户只得到一个指向结构的指针,但没有关于结构内容的信息。您必须提供操作 hidden/private 数据所需的所有函数。

它可能看起来像:

ns_event.h(待用户加入)

enum ns_event_type{
    deleted,
    moved
};

struct ns_private;  // Opaque data type. The user knows that this type exists
                    // but have no idea what it contains.

struct ns_event_meta{
    enum ns_event_type type;
    union {
        const char *deleted_path;
        struct {
             const char *moved_from;
             const char *moved_to;
        } moved;
    } event_data;
    struct ns_private *buffer;  // The user only gets a pointer to the
                                // "private" data but can't access the
                                // members as the user doesn't know
                                // what's inside
};

// Public functions
int ns_init_buffer(struct ns_event_meta *buffer, size_t size);    
int ns_resize_buffer(struct ns_event_meta *buffer, size_t size);    
size_t ns_get_buffer_size(struct ns_event_meta *buffer);    
void ns_free_buffer(struct ns_event_meta *buffer);    
...
...

ns_event.c(您的代码)

struct ns_private {
    size_t internal_buffer_size;
    void *internal_buffer;
}

int ns_init_buffer(struct ns_event_meta *buffer, size_t size)
{
    assert(buffer != NULL);
    buffer->internal_buffer = malloc(size);
    if (buffer->internal_buffer == NULL)
    {
        buffer->internal_buffer_size = 0;
        return 1;
    }
    buffer->internal_buffer_size = size;
    return 0;
}

...
...

您还可以使用句柄而不是指向不透明数据类型的指针。喜欢

struct ns_event_meta{
    enum ns_event_type type;
    union {
        const char *deleted_path;
        struct {
             const char *moved_from;
             const char *moved_to;
        } moved;
    } event_data;
    int handle;    // A handle to hide the location of "private" data
                   // Translation will be needed in the c-file
};

handle 只是您的代码在 ns_init_buffer 调用中选择的一个整数。在您的代码中,您将有一个 table 在句柄号和指向私有数据的指针之间进行转换。这样用户将永远不知道私有数据存储在哪里,甚至不知道保存私有数据的结构的名称。

注意:将句柄存储在结构中是个人喜好问题。您可以将其从结构中删除并让 init-function return 处理。然后用户必须将句柄存储在其他变量中并将其作为额外的函数参数传递。