Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.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_Refactoring_Global Variables_Shared Libraries - Fatal编程技术网

将遗留C代码(紧密结合广泛使用全局代码)重构到共享库中

将遗留C代码(紧密结合广泛使用全局代码)重构到共享库中,c,refactoring,global-variables,shared-libraries,C,Refactoring,Global Variables,Shared Libraries,我正在开发一个用C编写的遗留系统。我正在将代码从几个大型模块重构为几个更小、逻辑独立的共享库 问题是现有的代码使得这样的分区很困难,因为大型模块试图做太多的事情。其他其他挑战包括: 现有代码是紧密耦合的: 例如,本应实现收集结构(实际上是动态数组)的模块还具有从文件、数据库、读取缓存数据等检索数据(填充结构)的例程 代码广泛使用了全局变量,当我将代码划分到单独的共享库中时,我不知道(如果?)如何工作 现有标头的外观如下所示: /* DYN_ARRAY header */ DYN_ARRAY*

我正在开发一个用C编写的遗留系统。我正在将代码从几个大型模块重构为几个更小、逻辑独立的共享库

问题是现有的代码使得这样的分区很困难,因为大型模块试图做太多的事情。其他其他挑战包括:

  • 现有代码是紧密耦合的:

    例如,本应实现收集结构(实际上是动态数组)的模块还具有从文件、数据库、读取缓存数据等检索数据(填充结构)的例程

  • 代码广泛使用了全局变量,当我将代码划分到单独的共享库中时,我不知道(如果?)如何工作

现有标头的外观如下所示:

/* DYN_ARRAY header */
DYN_ARRAY* DYN_ARRAY_Alloc(); 
void       DYN_ARRAY_Free(DYN_ARRAY *ptr); 
int        DYN_ARRAY_LoadFile(DYN_ARRAY *ptr, cont char* filename, FILE_STRUCT_INFO *info);

/* obvious dependency on database functionality */
int        DYN_ARRAY_LoadQueryResults(DYN_ARRAY *ptr, const char* sql);

/* This innocuous looking function calls a function which introduces
   a dependencies on another logically separate module
*/ int DYN_数组_GetIdKeyValue(常量DYN_数组*ptr,常量int key_id)

我正在考虑将现有的DYN_阵列模块分为三个共享库,如下所示:

  • dyn_阵列_核心(取决于:无)
  • 动态数组数据库(取决于:动态数组内核、数据库实用程序…)
  • 动态数组杂项(取决于:动态数组核心、杂项使用…)
  • 我的问题是:

  • 这是一种明智的方法吗(或者有更好的方法来划分代码?)

  • 分区代码是否会像以前一样工作(假设代码使用全局变量-即每个dll是否都有自己的全局变量副本?[如果是,那么显然这不是我想要的]-在这种情况下,我如何重构代码以像以前一样工作?)


  • 如果您想进行更改以完成一些有用的事情,那么将代码移动到单独的共享对象中不会有多大作用。在我看来,更好的方法是首先重写代码以尽可能多地消除这些全局变量——构建一个或多个结构来保存上下文,并修改函数签名以包含上下文指针。一旦到了那里,将代码分离成更清晰的模块(可能还有多个共享库)将为您提供更有用的输出

    首先,您可能需要创建一个新的头文件,其目的是定义以前的全局文件重新定位到的结构。首先请注意,我们希望在不将代码移动到共享库的情况下执行所有这些重构——仅在验证初始重构成功后执行。(即,一次引入的潜在故障点不得超过必要的数量。)

    下一步是将要消除的所有全局变量移动到此结构中。如果您知道所有全局变量的定义位置,那么任务就很简单了。。。只需将它们从源位置删除,注意任何非零初始值,并将变量定义移动到结构中(无需初始化)。如果全局变量的当前位置没有很好地定义(它们散布在各处),那么这一步就比较困难,但是编译器工具可以帮助您在当前代码中找到全局变量

    下一步考虑<代码> CrrigLogBalCutExter()/<代码>函数。这个函数将分配一个上下文结构并初始化它;如果没有非零初始化,则可以完全消除该函数

    GlobalContext *CreateGlobalContext()
    {
        GlobalContext *context = malloc(sizeof(*context));
        memset(context, 0, sizeof(*context)); // initialize it to all zeros
    
        // if needed, initialize individual non-zero elements
        // context->some_non_zero = 1;
    
        return context;
    }
    
    现在您没有了更多的全局变量(在您计划移动的代码模块中),并且有了一种通过适当的初始化为它们创建存储的方法,您现在已经有了一堆无法编译的代码—您应该拥有所有以前全局变量的未解析引用或未知标识符

    任何访问已移动全局变量的函数都应该添加一个新参数。例如,考虑这种变化:

    extern int global_a;
    void FunctionToBeMoved(int a)
    {
        global_a = a;
    }
    
    // the above old function becomes:
    void FunctionToBeMoved(GlobalContext *context, int a)
    {
        context->global_a = a;
    }
    
    完成这些转换后,您需要修改调用每个已更改函数的方式——它们只需传递它们现在接收的上下文(或者如果由于缺少直接全局使用而忽略了上下文,则现在需要接收)


    这不是一项很小的工作,但是将写得不好的代码转换成可读/可维护的代码通常是很大的。

    您有任何测试吗?如果您想进行更改以完成一些有用的事情,将代码移动到单独的共享对象中不会有多大作用。在我看来,更好的方法是首先重写代码以尽可能多地消除这些全局变量——构建一个或多个结构来保存上下文,并修改函数签名以包含上下文指针。一旦你到了那里,将代码分离成更清晰的模块(可能还有多个共享库)将为你提供更有用的输出。@mah:我确实想过使用你建议的方法(见我的上一个问题),但这种方法涉及的工作量太大了。不过,请你再详细说明一下(也许在回答中),我如何像你所建议的那样,将globals总结成上下文结构。Thanks@HomunculusReticulli当然可以,请看下面。谢谢你的回答。这在一定程度上有助于回答关于全球人的问题。我有两个问题:首先:,如果
    createGlobalContext
    是在moduleA中定义的,并且它在moduleC中使用并被模块化(例如,每个模块都是一个单独的编译单元(甚至是共享库),那么globals是否会有多个副本?。第二个问题是调用模块解耦。我可以只为模块提供函数原型(这样它就可以编译),并在运行时解析引用(完整的应用程序将链接到所有共享库);您只想调用
    createGlobalContext()
    一次。。。调用方有责任将上下文传播到任何其他需要它的代码。A2:是的,这就是它对共享对象的静态链接的工作方式。做一个d是可能的
    extern int global_a;
    void FunctionToBeMoved(int a)
    {
        global_a = a;
    }
    
    // the above old function becomes:
    void FunctionToBeMoved(GlobalContext *context, int a)
    {
        context->global_a = a;
    }