8

Is it good/correct way to do like this for a robust C program

//File1 => Module1.h

static int Fun(int);

struct{
int (*pFn)(int)
}Interface;

//File2 => Module1.c

static int Fun(int){
//do something
}

Interface* Init(void)
{
  Interface *pInterface = malloc(sizeof(Interface));
  pInterface->pFn = Fun;
  return pInterface;
}

//File 3 => Module2.c
#include"Module1.h"
main()
{
  Interface *pInterface1 = Init();
  pInterface1->pFn(5);
}

My intention is to make each module expose an interface...

Questions:

  1. Is it good to write a C code like above to expose an interface... ??
  2. What better ways are available for exposing interface??
  3. Are there any references for design principles for C programming (not C++) ??
4

3 回答 3

1

这对于动态加载的模块来说更为惯用。(或类似的东西。)

通常,两个源文件之间的接口是使用extern直接访问的函数和对象来定义的。您需要为做任何更复杂的事情提出理由。

于 2013-10-11T05:34:18.980 回答
0
  1. 如果你用它来解决简单的任务,它可能只会增加代码库和复杂性。但在某些地方它可能很有用。
    例如:如果您想在 C 中实现STRATEGY 模式,这是一种方法。
  2. Interface您可以使用FIRST-CLASS ADT 模式向用户隐藏结构的内容。或者您可以结合上述模式。
于 2013-10-11T05:53:38.957 回答
0

我的简短回答是否定的。

忽略指出不应在 .h 文件中声明静态函数的注释(这是您要隐藏的部分——毕竟它是静态的),这里有一些事情要记住。

似乎您正在尝试使用该接口来将实现与模块的消费者分离;一个崇高的目标。这可以增加灵活性,因为可以在 .c 文件中更改实现而不破坏调用代码。但是,请考虑您定义的接口。

任何使用此模块的代码都应保证能够通过 Init 获取接口实例(尽管这需要包含在您的 .h 文件中)。该接口实例将指向要使用的函数。一个这样的函数具有签名

int Fun(int);

那你隐藏了什么?当然,您可以更改他们正在调用的函数,但您仍然必须提供具有该签名的函数。

或者,您可以将 .h 定义为:

int Fun(int);

然后在 .c 文件中,类似:

static int StaticFun(int i)
{
    // something
}

int Fun(int i)
{
    StaticFun(i);
}

你可以让 Fun 根据它管理的一些内部状态或配置文件调用不同的函数,无论你想要什么。为什么这样更好?除了更简单之外,它还是一个静态调用;例如,编译器可以内联对 StaticFun 的调用,甚至完全内联函数 StaticFun。通过函数指针调用通常会导致明显的性能损失。

现在,可能有一个“接口”对象的情况,例如您定义的。但我倾向于建议在你的情况下没有一个,而不是没有修改。

假设您的Init功能更改为:

Interface* Init(some state info);

现在你传回的接口对象可以根据传入的状态或配置而改变,这允许你的模块动态映射函数。基本上,我的观点是,如果你实际上是在下面调用一个静态函数,那么你的工作比需要的要努力得多,并且会阻止编译器优化。

作为一个注释,如果你打算走接口路线(我现在正在做一个类似的项目)那么我可以建议修改吗?就像是...

.h 文件:

const Interface* Init(some state);

.c 文件:

static int FunTyp1(int);
static int FunTyp2(int);


static const Interface typ1 = { &FunTyp1 };
static const Interface typ2 = { &FunTyp2 };

const Interface* Init(some state)
{
    if (some state == type1)
       return &typ1;
    else if (some state == type2)
       return &typ2;
}

优点:

  1. 您可能不想使用代码来修改界面?至少,我想不出一个好的理由。
  2. 静态定义它可以避免不必要的堆分配和当消费者重复请求接口对象而忘记释放它们(或者不知道应该释放它们——谁拥有这个对象?)时的内存泄漏。
  3. 您可以为流程中的所有使用代码共享接口对象的单个(静态)实例,而不必担心有人更改它。

请注意,通过拥有几个静态初始化的实例,您可以为要支持的每个函数映射定义不同的“接口”对象。

于 2013-10-11T06:02:42.343 回答