[英]Conventions for functions that operate on structs
Let's say I have the following in foo.h
: 假设我在
foo.h
具有以下内容:
typedef struct {
char* my_buf;
int my_int;
} MyFoo;
/* Creates a foo, allocating memory for my_buf and initializing my_int */
void create_foo(MyFoo *foo);
/* Destroys a foo, freeing the memory for my_buf */
void free_foo(MyFoo *foo);
Now in another module, bar.h
let's say I have the following: 现在在另一个模块
bar.h
我有以下内容:
#include "foo.h"
/* Do something with a foo */
void do_something(MyFoo *foo, SomeType_t otherType, ...);
As written, it's unclear whether or not the MyFoo
provided to do_something
should have been initialized with create_foo
. 如所写,尚不清楚提供给
do_something
的MyFoo
是否应使用create_foo
进行初始化。 Since it's not const
, I think it's reasonably clear that a foo (ie space allocated for a foo) should be passed in, it will be modified in some way, and then the user can use the modified foo. 由于它不是
const
,我认为应该很清楚地传入foo(即为foo分配的空间),它将以某种方式进行修改,然后用户可以使用修改后的foo。 But how do I most effectively indicate whether or not that foo should have been 'created'? 但是,我如何最有效地指示是否应该“创建”该foo? A common pattern is to do something like
一种常见的模式是做类似的事情
MyFoo stackFoo;
do_something(&stackFoo, ...);
But then, if do_something
creates the foo, the user has to know that they're responsible for freeing the foo. 但是,如果
do_something
创建了foo,则用户必须知道他们有责任释放foo。 On the other hand, there's no way to enforce that the foo should have been created; 另一方面,没有办法强制应该创建foo。 I'm just asking for a pointer (I admit this is a weak counter argument since it applies to a lot of things in C).
我只是在问一个指针(我承认这是一个微不足道的反论点,因为它适用于C语言中的很多东西)。
Another option would be to have MyFoo
include a "created" flag so that it the function can reject any foos that haven't been created, but this seems like I'm over-engineering things. 另一个选择是让
MyFoo
包含一个“ created”标志,以便该函数可以拒绝尚未创建的任何foo,但这似乎使我过度设计。
I just want to be sure I'm not missing a common convention or standard way of handling this. 我只是想确保我不会错过通用的约定或标准方式来处理此问题。 Maybe I'm forcing OOP too much.
也许我强迫OOP太多了。 Thoughts and comments appreciated!
想法和评论表示赞赏!
Edit: It was suggested, and I agree, that a better name for create_foo
would be init_foo
. 编辑:有人建议,我同意,
create_foo
的更好名称是init_foo
。
Yes you are forcing OOP, this is no wrong but OOP in C is a matter of orderliness. 是的,您要强制执行 OOP,这没错,但是C中的OOP是有序的。 Whatever, you cannot ensure that a
foo
hasn't been created on stack. 无论如何,您不能确保尚未在堆栈上创建
foo
。
For: 对于:
void do_something(MyFoo *foo, SomeType_t otherType, ...);
it is clear that an existing foo
is to be passed, as the argument being passed by value. 显然,要传递现有的
foo
,因为参数是通过值传递的。 So, the semantic is clear : an existing foo
must be passed. 因此,语义很明确:必须传递现有的
foo
。
For: 对于:
void create_foo(MyFoo *foo);
something's wrong as the argument is passed by value either this should not be called create_foo
but init_foo
, or you should pass the argument as an address of a pointer. 由于参数是通过值传递的,这是不对的,这不应该被称为
create_foo
而应该是init_foo
,或者您应该将参数作为指针的地址传递。 In case of OOP-like programming you should distinguish allocation and initialization. 如果是类似OOP的编程,则应区分分配和初始化。
void init_foo(MyFoo *);
MyFoo foo; // simple creation
init_foo(&foo); // initialization
or 要么
void init_foo(MyFoo *);
MyFoo *alloc_foo();
MyFoo *pfoo = alloc_foo(); // allocation factory
init_foo(pfoo); // initialization
// or with an idiom like
MyFoo *pfoo;
init_foo(pfoo=alloc_foo());
or: 要么:
MyFoo *init_foo(MyFoo *);
MyFoo *alloc_foo();
MyFoo *pfoo = init_foo(alloc_foo());
Now you can also make a single call to a convenient function: 现在,您还可以调用一个便捷函数:
MyFoo *create_foo(); // alloc+initialization factory
MyFoo *pfoo = create_foo();
or with a pass-a-pointer idiom: 或具有传递指针的习惯用法:
void create_foo(MyFoo **f);
MyFoo *pfoo;
create_foo(&pfoo);
If you want you can use an opaque type using modularity, to have a better control of your allocations, but at the end it will not ensure that user is not cheating: 如果您希望使用带有模块性的不透明类型,可以更好地控制您的分配,但最终并不能确保用户没有作弊:
// Module foo.c
struct internal_foo {
int m;
};
void *create_foo() {
struct internal_foo *pfoo = malloc(...);
// init structure
return pfoo;
}
void delete_foo(void *pf) {
free(pf);
}
// convenient functions for members
void set_m_foo(void *pf,int v) { ((struct internal_foo *)pf)->m = v; }
int get_m_foo(void *pf) { return ((struct internal_foo *)pf)->m; }
thus from an external point, user can only see a generic pointer. 因此从外部来看,用户只能看到通用指针。 If you want to ensure a much better typing, then you can also encapsulate the generic pointer into a dummy structure:
struct MyFoo { void *opaque_foo; }
如果要确保更好的键入,则还可以将通用指针封装到伪结构中:
struct MyFoo { void *opaque_foo; }
struct MyFoo { void *opaque_foo; }
. struct MyFoo { void *opaque_foo; }
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.