我如何模块化设计用C? [英] How do I Modular Design in C?

查看:134
本文介绍了我如何模块化设计用C?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想让我的项目更加模块化,这样,如果模块的一个被删除没有模块化间的依赖关系。

I want to make my project more modular so that there are no inter-modular dependencies if one of the module is removed.

有关如如果我划分code在我的过程分为多个目录,说,X,Y和Z因此,在X中的数据结构不应该直接由Y数据结构和Z访问,反之亦然那么我需要一些内部沟通机制点¯x之间,Y和Z

For e.g. If I divide the code in my process into multiple directories, say, X, Y and Z so that data structures in X should not be accessed directly by data structures in Y and Z and vice versa then I need some internal communication mechanism between X, Y and Z.

由于我用C编码,任何人都可以提出一个示例项目或设计考虑的一样吗?

Since I am coding in C, can anyone suggest a sample project or design considerations for the same?

推荐答案

这往往归结为API的设计。我发现有用的几件事情:

This often boils down to API design. A couple things that I find helpful:


  • 请记住,你的API住在你的头文件。实施是在C文件。

  • 避免全局变量 - 使用的访问方法,如果有必要

  • 避免结构共享如果可能的话

  • 使用回调函数来降低耦合

libfoo.h

int (*libfoo_callback)(void *arg, const char *name, int id);

/**
 * Iterate over all known foobars in the system.
 */
int libfoo_iterate_foobars(libfoo_callback cb, void *arg);

libfoo.c

#include "libfoo.h"

/* Private to libfoo.c */
struct foobar {
    struct foobar *next;
    const char *name;
    int id;
};

/* Don't make this globally visible */
static struct foobar *m_foobars;

int libfoo_iterate_foobars(libfoo_callback cb, void *arg)
{
    struct foobar *f;

    for (f = m_foobars; f != NULL; f = f->next) {
        int rc = cb(f->name, f->id);
        if (rc <= 0)
            return rc;   /* Stop iterating */
    }
    return 0;
}

some_consumer.c

#include <stdio.h>
#include "libfoo.h"

struct cbinfo {
    int count;
};

static int test_callback(void *arg, const char* name, int id)
{
    struct cbinfo *info = arg;

    printf("    foobar %d: id=%d name=%s\n", info->count++, id, name);
    return 1;   /* keep iterating */
}

void test(void)
{
    struct cbinfo info = { 0 };
    printf("All foobars in the system:\n");

    libfoo_iterate_foobars(test_callback, &info);

    printf("Total: %d\n", info.count);
}

在这里,我表现出 libfoo的谁跟踪一些foobars。而且,我们有一个消费者谁在这个例子中,仅仅要显示所有foobars的列表。优点这种设计:

Here I show a libfoo who tracks some foobars. And we have a consumer who in this example, simply wants to show a list of all foobars. Benefits to this design:


  • 没有全局可见的变量:没有人比 libfoo的其他可以直接修改foobars名单。他们只能使用libfoo由公共API允许的方式。

  • No globally-visible variables: No one other than libfoo can directly modify the list of foobars. They can only use libfoo in the manner allowed by the public API.

用回调迭代器的方式,我不必知道一个foobar的是如何跟踪甚至保持任何消费者。今天,它结构foobar的列表,也许明天它是一个SQLite数据库。任何,通过隐藏结构定义,消费者只需要知道一个foobar的有一个名称 ID

By using a callback-iterator approach, I've kept the consumer from having to know anything about how a foobar is even tracked. Today it's a list of struct foobar, maybe tomorrow it's an SQLite database. Any, by hiding the structure definition, the consumer only needs to know that a foobar has a name and an id.

要成为的真正的模块化,你将需要两件大事:

To be truly modular, you're going to need two big things:


  1. 一组API定义模块如何生产和消费数据

  2. 要实际加载在运行时模块中的方法

这样做的具体细节将大大取决于您的目标平台,模块化的需求,预算等上。

The specifics of this will greatly vary depending on your target platform, modular needs, budget, etc.

有关#1你会通常有一个模块登记制度,一些组件跟踪加载的模块列表,以及元信息对生产什么和消费。

For #1 you would generally have a module registration system, where some component tracks a list of loaded modules, as well as meta information about what the produce and consume.

如果模块可以调用其他模块提供code,你需要一种方法来使这可见。这也将发挥成2.实施以Linux内核为例子 - 它支持加载内核模块添加新的功能,驱动等进内核,而无需将其所有的编译成一个大的二进制文件的目的。模块可以使用 EXPORT_SYMBOL 来表示特定符号(即功能)可用于其他模块调用。该模块被加载内核轨道,在于出口产品的功能,并在该地址。

If modules can call into code provided by other modules, you'll need a way to make this visible. This will also play into the implementation of 2. Take the Linux kernel for example - it supports loadable kernel modules for the purpose of adding new features, drivers etc. into the kernel, without having to compile it all into one large binary. Modules can use EXPORT_SYMBOL to indicate that particular symbol (i.e. function) is available for other modules to call. The kernel tracks which modules are loaded, what functions they export, and at which addresses.

有关#2你可以利用你的操作系统的共享库的支持。在Linux和其它UNIX系统中,这些动态库是ELF(的.so 文件),这是由动态加载程序加载到进程的地址空间。在Windows上,这些DLL文件。通常情况下,这种加载自动为您您的进程启动时处理。但是,应用程序可以利用动态加载程序显式地加载其选择的附加模块。在POSIX,你会打电话 的dlopen() 以及在Windows上,你会使用 调用LoadLibrary() 。无论是函数会返回某种的处理的给你,这将让你做出有关模块进一步查询或请求。

For #2 you might leverage your OS's shared library support. On Linux and other Unices, these dynamic libraries are ELF (.so files), which are loaded by the dynamic loader into a process's address space. On Windows, these are DLLs. Normally, this loading is handled automatically for you when your process starts. However, an application can leverage the dynamic loader to explicitly load additional modules of its choosing. On POSIX you would call dlopen(), and on Windows you would use LoadLibrary(). Either function will return some sort of handle to you, which will allow you to make further inquiries or requests about the module.

您模块则可能需要(通过设计)导出 codingfreak_init 的功能,它是由您的应用程序调用首次加载一个模块时。那么该功能将使得更多的电话到您的框架或返回数据表明,它需要什么设施,并提供。

Your module then might be required (by your design) to export a codingfreak_init function, which is called by your application when a module is first loaded. This function would then make additional calls into your framework, or return data to indicate what facilities it requires and provides.

这是一个应该让你的车轮转动都非常的一般信息。

This is all very general information which should get your wheels turning.

这篇关于我如何模块化设计用C?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆