Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/56.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C中进行依赖项注入?_C_Dependency Injection - Fatal编程技术网

如何在C中进行依赖项注入?

如何在C中进行依赖项注入?,c,dependency-injection,C,Dependency Injection,我正在寻找一个很好的在C中进行DI的技术解决方案 我已经在这里看到了一些DI问题,但是我还没有看到任何实际的例子或具体的实现建议 那么,假设我们有以下情况: 我们有一组c语言的模块;我们想要重构这些模块,以便我们可以使用DI来运行单元测试等等 每个模块实际上由一组c函数组成: 模_函数(…) 模块之间相互依赖。例如,通常您可能会接到以下电话: int module1_doit(int x) { int y = module2_dosomethingelse(x); y += 2; r

我正在寻找一个很好的在C中进行DI的技术解决方案

我已经在这里看到了一些DI问题,但是我还没有看到任何实际的例子或具体的实现建议

那么,假设我们有以下情况:

我们有一组c语言的模块;我们想要重构这些模块,以便我们可以使用DI来运行单元测试等等

每个模块实际上由一组c函数组成:

模_函数(…)

模块之间相互依赖。例如,通常您可能会接到以下电话:

int module1_doit(int x) {
  int y = module2_dosomethingelse(x);
  y += 2;
  return(y);
}
对此,DI的正确方法是什么

可能的解决办法似乎是:

  • (1) 对所有模块函数使用函数指针,并在调用函数时执行以下操作(或类似操作):

    int y=模块->模块2->剂量表hingelse(x)

  • (2) 使用相同的符号编译的多个库(mock、std等),并在正确的实现中动态链接

(2) 这似乎是正确的方法,但很难进行配置,而且会迫使您为每个单元测试构建多个二进制文件

(1) 看起来它可能会工作,但在某个时候,您的DI控制器会陷入这样一种情况:您需要在运行时动态调用一个通用工厂函数(void(factory)(…)比方说)和许多其他需要注入的模块

在c语言中有没有其他更好的方法


“正确”的方法是什么?

有两种方法可以使用。正如雷夫所指出的那样,你是否真的愿意,取决于你自己

第一:在静态库中创建“动态”注入方法。链接库,并在测试期间简单地替换它。瞧,这个方法被取代了

第二:只需基于预处理提供编译时替换:

#ifndef YOUR_FLAG

    /* normal method versions */

#else

    /* changed method versions */

#endif

/* methods that have no substitute */

我的结论是,在C语言中没有“正确”的方法来实现这一点。它总是比在其他语言中更加困难和乏味。但是,我认为重要的是,不要为了单元测试而混淆代码。用C语言把所有东西都变成函数指针听起来不错,但我认为这只会让代码在最后调试时变得可怕

我最新的方法是让事情简单化。我不会更改C模块中的任何代码,除了文件顶部的一个小的
#ifdef UNIT_TESTING
,用于外部和内存分配跟踪。然后,我获取模块,并在删除所有依赖项的情况下对其进行编译,以使其链接失败。一旦我检查了未解析的符号以确保它们是我想要的,我就会运行一个脚本来解析这些依赖项并为所有符号生成存根原型。这些都被转储到单元测试文件中。YMMV取决于外部依赖关系的复杂程度


如果我需要在一个实例中模拟依赖项,在另一个实例中使用真实的依赖项,或者在另一个实例中存根依赖项,那么我最终会为测试中的一个模块创建三个单元测试模块。拥有多个二进制文件可能并不理想,但这是C语言唯一的实际选择。不过,它们都是同时运行的,所以这对我来说并不是什么问题。

这是一个完美的使用案例

Ceedling是一个sort伞形项目,它将Unity和CMock(除其他外)结合在一起,可以使您描述的许多工作自动化

一般来说,Ceedling/Unity/CMock是一组ruby脚本,可以扫描代码,并根据模块头文件自动生成mock,以及找到所有测试并生成将运行这些测试的运行程序的测试运行程序

为每个测试套件生成一个单独的测试运行程序二进制文件,并根据您在测试套件实现中的请求链接到适当的模拟和真实实现中


起初,我不太愿意将ruby作为我们构建系统的依赖项引入到测试中,这看起来非常复杂和神奇,但在尝试了ruby并使用自动生成的模拟代码编写了一些测试之后,我被吸引住了。

我认为在C中使用DI没有任何问题。请参阅:


关于这件事,我参加聚会有点晚了,但这是我最近工作的一个话题

我看到的两种主要方法是使用函数指针,或者将所有依赖项移动到特定的C文件

后者的一个很好的例子是FATF。

fatfs的作者提供了大部分库函数,并将某些特定依赖项降级,供库用户编写(例如串行外围接口函数)

函数指针是另一个有用的工具,使用typedef有助于防止代码变得太难看

下面是我的模数转换器(ADC)代码的一些简化片段:

Foo的中断行为现在被注入到ADC的中断服务例程中

您可以更进一步,将函数指针传递到FOO_Initialize中,这样所有依赖关系问题都由应用程序管理

//dependency_injection.h
typedef void (*DI_Callback)(void)
typedef bool (*DI_CallbackSetter)(DI_Callback)

// foo.c
bool FOO_Initialize(DI_CallbackSetter CallbackSet)
{
    bool err = CallbackSet(FOO_AdcCallback);
    // Initialize other FOO stuff
    return err;
}

@Skurmedel没有尝试用C编写Java。@Rafe如果你有解决方案,请马上回答~我忍不住会对你在没有帮助的情况下简单地对我的问题发火感到有点恼火。你的问题真的没有好的答案。你试图做的在C中没有多大意义。“你做错了”或“选择不同的语言”并不是一个很好的答案。我不是想惹你生气,但我想说的是,你要求的是一些C语言的东西,这些东西并没有发挥它的优势。例如,每个驱动程序都是实现特定接口的模块(取决于驱动程序类型)。所有模块都是松散耦合的,根据内核运行的机器的特定配置,模块(/依赖项)在运行时连接在一起。这由每个驱动程序模块填写一个函数指针结构(接口)来执行,然后将该结构提供给依赖模块dyn
//dependency_injection.h
typedef void (*DI_Callback)(void)
typedef bool (*DI_CallbackSetter)(DI_Callback)

// foo.c
bool FOO_Initialize(DI_CallbackSetter CallbackSet)
{
    bool err = CallbackSet(FOO_AdcCallback);
    // Initialize other FOO stuff
    return err;
}