模块化编译时阵列扩展 [英] Modular compile-time array expansion

查看:163
本文介绍了模块化编译时阵列扩展的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比方说,我在这个sitation:


  

main.c中:

的#include<&stdio.h中GT;
 #包括LT&;&stdlib.h中GT; 的#includeheader.h INT iCanProcess(字符* gimmeSmthToProcess); INT processingFunctionsCount = 0;
 INT(*(* processingFunctions))(字符*)= NULL; INT addProcessingFunction(INT(* FCT)(字符*)){
     processingFunctionsCount ++;
     processingFunctions =的realloc(processingFunctions,
               的sizeof(INT(*)(字符*))* ProcessingFunctionsCount);
   processingFunctions [processingFunctionsCount-1] = FCT;
 } INT主(INT ARGC,CHAR *的argv []){
     字符* dataToProcess =我有些veeeery lenghty数据;
     addProcessingFunction(iCanProcess);     [...]     为(unsigned int类型I = 0; I< processingFunctionsCount;我++){
         processingFunctions [I](dataToProcess);
     }     免费(processingFunctions);
   返回0;
 } INT iCanProcess(字符* gimmeSmthToProcess){...}


  
  

somefile.c:

的#includeheader.h INT机能缺失(字符* someDataToProcess){...}


  
  

header.h:

的#ifndef HEADER_DEF
  #定义HEADER_DEF  EXTERN INT processingFunctionsCount;
  EXTERN INT(*(* processingFunctions))(字符*);  INT addProcessingFunction(INT(* FCT)(字符*));  #万一


有没有什么办法,使用宏或任何其他伎俩机能缺失,我可以添加到数组指针到函数 processingFunctions 不改变的main.c header.h 每次我需要添加一次?

这里的问题是不是要改变阵列,因为它可以很容易地重新分配,但不改变的main()功能:必须有一个方法可以让我知道该文件在这里和编译,同时保持之外获取函数原型的main()

我想过使用preprocessor把戏像<一个href=\"http://stackoverflow.com/questions/4904255/how-can-i-generate-a-list-via-the-c-$p$pprocessor-cpp\">this 之一,但似乎并没有找到一个合适的方式来做到这一点...

(边注:这是一个更大的项目,这实际上是基地code:支持解析器使用相同的输出,但不同输入的精简版有些解析器支持某些类型的文件,所以我。有一个函数指针数组(每个分析器,以检查它们是否兼容),我称之为对文件内容,其中的每一个。然后,我要求用户选择它要使用的解析器。我有一个文件每分析器,包含检查功能,看是否解析器可以处理这个文件,和一个解析功能实际上做所有的辛勤工作,我不能改变的标题或文件的main.c每次我添加一个解析器。)

(边注2:这个冠军是可怕的......如果你有一个更好的任何想法,请哦,请随时编辑和删除此要感谢)


解决方案

您可以使每个功能的模块(共享对象或DLL为Windows 的)与已知名字的一个符号,然后在运行时只需扫描为的的.so 的个目录或的.dll 的S加载每个人,并创建一个指向符号,假设你有 N 模块,其中的 I 的个模块源$ C ​​$ c是

module.i.c

  INT功能(字符*参数)
{
     //你想在这里什么
     返回THE_RETURN_VALUE;
}

然后编译每个 .C 的文件到一个共享对象,我将使用Linux说明在Windows中可以做类似的事情,以及Linux解决方案适用于POSIX系统,以便它涵盖了很多

首先生成的 module.i.c 的文件,这个脚本

#!/斌/庆典因为我在{0..100};

    猫&GT; 。模块$一.C&LT;&LT; EOT
#包括LT&;&stdlib.h中GT;INT
功能(字符*参数)
{
    //带参数新政
    $回报我;
}
EOT
DONE

现在创建的的Makefile 的像这样的

CC = GCC
LDFLAGS =
CFLAGS = -Wall -Werror -g3 -O0
功能= $(patsubst%.C,%。所以,$(通配符*。*。C))所有:$(函数)
    $(CC)$(CFLAGS)$(LDFLAGS)的main.c -o主要-ldl%。所以:%.C
    $(CC)$ -shared(CFLAGS)$(LDFLAGS)$&LT; -o $ @清洁:
    @Rm -fv * *。所以主要的.o

这将加载模块的程序(的我们假定他们是在同一目录下的可执行文件的)

的#include&LT;&stdlib.h中GT;
#包括LT&;&dirent.h GT;
#包括LT&;&string.h中GT;
#包括LT&;&stdio.h中GT;
#包括LT&;&dlfcn.h中GT;INT
主要(无效)
{
    DIR * DIR;
    结构的dirent *进入;
    DIR =执行opendir(。);
    如果(DIR == NULL)
        返回-1;
    而((进入= READDIR(DIR))!= NULL)
    {
        void *的手柄;
        焦炭路径[PATH_MAX];
        INT(*功能)(字符*);
        如果(的strstr(入门&GT; d_name。所以)== NULL)
            继续;
        如果(的snprintf(路径的sizeof(路径),./%s入门&GT; d_name)&GT; = sizeof的(路径))
            继续;
        处理=的dlopen(路径,RTLD_LAZY);
        如果(手柄== NULL)
            继续; //更好:用`dlerror获得报告错误()
        函数=(INT(*)(字符*))的dlsym(处理,功能);
        如果(函数!= NULL)
            fprintf中(标准输出功能:%d个\\ N功能(例如));
        其他
            fprintf中(标准错误,符号未找到:%S \\ n,入门&GT; d_name);
        dlclose(手柄);
    }
    closedir(DIR);
    返回0;
}

在Windows上的想法是一样的,但你不能遍历像code上面的目录,你需要使用的 调用LoadLibrary() 而不是的dlopen(),并更换则dlsym()通过相应的功能。

但同样的想法会工作了。

如何保护你加载模块及其文件夹可以的这个问题

Let's say I'm in this sitation:

main.c :

 #include <stdio.h>
 #include <stdlib.h>

 #include "header.h"

 int iCanProcess (char* gimmeSmthToProcess);

 int processingFunctionsCount = 0;
 int (*(*processingFunctions)) (char*) = NULL;

 int addProcessingFunction(int (*fct)(char*)) {
     processingFunctionsCount++;
     processingFunctions = realloc(processingFunctions, 
               sizeof(int (*)(char*))*ProcessingFunctionsCount);
   processingFunctions[processingFunctionsCount-1] = fct;
 }

 int main(int argc, char *argv[]) {
     char* dataToProcess = "I am some veeeery lenghty data";
     addProcessingFunction(iCanProcess);

     [ ... ] 

     for(unsigned int i = 0; i < processingFunctionsCount; i++) {
         processingFunctions[i](dataToProcess);
     }

     free(processingFunctions);
   return 0;
 }

 int iCanProcess (char* gimmeSmthToProcess) { ... }

somefile.c :

 #include "header.h"

 int aFunction(char* someDataToProcess) { ... }  

header.h :

  #ifndef HEADER_DEF
  #define HEADER_DEF

  extern int processingFunctionsCount;
  extern int (*(*processingFunctions)) (char*);

  int addProcessingFunction(int (*fct)(char*));

  #endif

Is there ANY way, using macros or any other trick, I can add aFunction to the array of pointer-to-functions processingFunctions without changing main.c or header.h every time I need to add one ?

The problem here is not to change the array as it can be reallocated easily, but to NOT change main() function: there must be a way I can be aware of the file being here and compiled, and fetch the function prototype while staying outside of main()

I thought about using a preprocessor trick like this one but don't seem to find a proper way to do it...

(Side-note : This is a trimmed-down version of a bigger project, which in fact is base code to support parsers with the same output but different input. Some parsers support some type of files, so i have an array of function pointers (one for each parser, to check if they are compatible) and I call each one of them against the file contents. Then, I ask the user to chose which parser it wants to use. I have one file per parser, containing a "check" function, to see if the parser can handle this file, and a "parse" function to actually do all the hard work. I can't change the header or the main.c files every time I add a parser. )

(Side-note 2 : this title is terrible... if you have any idea for a better one, please oh PLEASE feel free to edit it and remove this note. Thanks)

解决方案

You could make each function a module (shared object or dll for windows) with a single symbol of a known name, and then at runtime simply scan a directory for the .sos or .dlls load each one and create a pointer to the symbol, suppose you had N modules, where the ith module source code is

module.i.c

int function(char *parameter)
{
     // Do whatever you want here
     return THE_RETURN_VALUE;
}

Then you compile each .c file into a shared object, I will use Linux for illustration on windows you can do a similar thing, and the linux solution works on POSIX systems so it covers a lot

First generate the module.i.c files with this script

#!/bin/bash

for i in {0..100};
do
    cat > module.$i.c <<EOT
#include <stdlib.h>

int
function(char *parameter)
{
    // Deal with parameter
    return $i;
}
EOT
done

Now create a Makefile like this one

CC = gcc
LDFLAGS =
CFLAGS = -Wall -Werror -g3 -O0
FUNCTIONS = $(patsubst %.c,%.so, $(wildcard *.*.c))

all: $(FUNCTIONS)
    $(CC) $(CFLAGS) $(LDFLAGS) main.c -o main -ldl

%.so: %.c
    $(CC) -shared $(CFLAGS) $(LDFLAGS) $< -o $@

clean:
    @rm -fv *.so *.o main

And the program that would load the modules (we assume that they are in the same directory as the executable)

#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include <dlfcn.h>

int
main(void)
{
    DIR *dir;
    struct dirent *entry;
    dir = opendir(".");
    if (dir == NULL)
        return -1;
    while ((entry = readdir(dir)) != NULL)
    {
        void *handle;
        char path[PATH_MAX];
        int (*function)(char *);
        if (strstr(entry->d_name, ".so") == NULL)
            continue;
        if (snprintf(path, sizeof(path), "./%s", entry->d_name) >= sizeof(path))
            continue;
        handle = dlopen(path, RTLD_LAZY);
        if (handle == NULL)
            continue; // Better: report the error with `dlerror()'
        function = (int (*)(char *)) dlsym(handle, "function");
        if (function != NULL)
            fprintf(stdout, "function: %d\n", function("example"));
        else
            fprintf(stderr, "symbol-not-found: %s\n", entry->d_name);
        dlclose(handle);
    }
    closedir(dir);
    return 0;
}

On Windows the idea would be the same, although you can't traverse the directory like the code above and you need to use LoadLibrary() instead of dlopen(), and replace the dlsym() with the appropriate function.

But the same idea would work too.

More information on how to secure the modules you load and their folder can be found in this question

这篇关于模块化编译时阵列扩展的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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