【问题标题】:pattern for handling function pointer of functions of inhomogene signature in CC中处理不均匀签名函数的函数指针的模式
【发布时间】:2015-07-25 03:55:11
【问题描述】:

所以, 我有大量函数可以从表单的某些来源读取输入:

ErrCode_t in_func1(t1_t * const data1);
ErrCode_t in_func2(t2_t * const data2);
...

一些触发函数,告诉我是否可以调用上面的函数:

TrCode_t tr_func1();
TrCode_t tr_func2();
...

以及对应的写出数据的函数:

void out_func1(t1_t const * const data1, const uint32_t handled_error);
void out_func2(t2_t const * const data2, const uint32_t handled_error);
...

还有一个相当复杂的算法取决于触发函数,它决定我是否可以调用输入函数。 (这是一张简化图;每个 I/O 都包含多个触发功能和计时器)。

这个算法但基本上说: 如果触发器说是,则使用指向数据变量的指针调用输入函数,检查错误,进行一些验证,然后将更新变量的指针传递给输出。

 void execute_f1(t1_t * const pData1)
 {
    if(evalTr(tr_func1()))
    {
      const ErrCode_t myErr = in_func1(pData1);
      const uint32_t myOutErr = evalError(myErr);

      out_func1(pData1,myOutErr);
    }
 }

(而evalTr和evalError应该是一些正确使用的评估函数)

我想将此算法封装在一个自己的函数中,

 void execute_io(???)

使用一些函数指针调用来执行此操作。但是如果没有大量的包装函数,我想不出一种符合标准的模式。 我包装了输入函数和输出函数以执行正确的强制转换,并调整签名,例如:

ErrCode_t my_in_func1(void * const pData1)
{
    t1_t * const data1 = (t1_t*) pData1;
    return in_func1(data1);
}

和输出功能类似:

 void my out_func2(void const * const data2, const uint32_t handled_error) {...}

这样我就有了同质签名,这样就可以轻松获得函数指针。 但我真的不想包装所有这些功能。有谁知道可以“在”execute_io 和周围代码“内部”工作的模式,所以我不必包装所有这些函数?

更新:根据 barak manos 的要求,这里是一个组合:

system_interface.h

 ErrCode_t in_func1(t1_t * const data1);
 /* some 500 more of them */

 TrCode_t tr_func1();
 /* some 500 more of them */

 void out_func1(t1_t const * const data1, const uint32_t handled_error);
 /* some 500 more of them */

my_code.c

 static ErrCode_t my_in_func1(void * const data1)
 {
    t1_t * const data1 = (t1_t*) pData1;
    return in_func1(data1);
 }
 /* and some 500 more wrappers */

 static void my_out_func1(void const * const pData1, const uint32_t handled_error)
 {
    t1_t const * const data1 = (t1_t) pData1;
    out_func1(pData1, handled_error);
    return;
 }
 /* and some 500 more wrappers */

typedef ErrCode_t (*inFuncPtr)(void * const);
typedef void (*outFuncPtr)(void const * const, const uint32_t);
typedef TrCode_t (*trFuncPtr)();

execute_io(inFuncPtr inFunc, outFuncPtr outFunc, trFuncPtr trFunc, void * pData)
{
   if(evalTr((*trFunc)()))
   {
      const ErrCode_t myErr = (*inFunc)(pData);
      const uint32_t myOutErr = evalError(myErr);

      (*outFunc)(pData,myOutErr);
   }
   return;
}

void do_all_my_work()
{
   {
     t1_t data1;
     execute_io(&my_in_func1, &my_out_func1, &tr_func1, &data1);
   }
   {
     t2_t data2;
     execute_io(&my_in_func2, &my_out_func2, &tr_func2, &data2);
   }
   /* and some 499 other calls */
}

我想找到另一种模式,它不会强迫我包装所有 I/O 函数。 (不,上面的代码肯定不是一个可执行的例子,而只是一个概念)

【问题讨论】:

  • 能否请您展示一下您目前拥有的完整代码,并指出您不满意的地方?这比猜测所有这些代码 sn-ps 是如何组合在一起的要容易得多。
  • @barakmanos 希望更新有所帮助。

标签: c function-pointers


【解决方案1】:

我写了一些我相信你想要的东西。如果我遗漏了什么,请告诉我。我为您提到的各种函数和类型编写了存根,以便代码能够编译,但无论使用的类型或您定义的函数中使用的实现,它都应该工作。基本上它是有效的,因为所有指针都是给出内存中值地址的整数,只要指针指向的位置足够大以包含所需的值,那么该内存的内容对于传递指针并不重要。有时可能会影响此概念的唯一问题是某些编译器要求特定数据类型沿特定字节边界对齐。为了解决这个问题,我们首先注意到这些字节边界都是 2 的幂,并且对齐永远不会超过 c 当前为 64 位(8 字节)的最大基元的大小。即使对于非原始类型也是如此,例如。结构/联合。然后我们要做的就是在需要保存这个类型数据的缓冲区上分配一个额外的 8 字节内存,然后将一个介于 0 和 7 之间的值添加到指针,使其可被 8 整除,从而消除对齐问题所有支持 64 位或更少指针的机器。

    /*
 * Test.c
 *
 *  Created on: May 14, 2015
 *      Author: tiger
 */
#include <stdio.h>

typedef int ErrCode_t;
typedef int TrCode_t;
typedef unsigned int uint32_t;
typedef unsigned char uint8_t;
typedef unsigned long long int uint64_t;

typedef int t1_t;
typedef short t2_t;
typedef float t3_t;
typedef double t4_t;
typedef long long int t5_t;

#define MAX_DATA_TYPE_SIZE ((uint32_t)64) //or whatever the size is of your largest data type

uint32_t evalTr(TrCode_t code);
uint32_t evalError(ErrCode_t code);

ErrCode_t in_func1(t1_t* const data1);
ErrCode_t in_func2(t2_t* const data2);
ErrCode_t in_func3(t3_t* const data3);
ErrCode_t in_func4(t4_t* const data4);
ErrCode_t in_func5(t5_t* const data5);

TrCode_t tr_func1();
TrCode_t tr_func2();
TrCode_t tr_func3();
TrCode_t tr_func4();
TrCode_t tr_func5();

void out_func1(t1_t const* const data1, const uint32_t handled_error);
void out_func2(t2_t const* const data2, const uint32_t handled_error);
void out_func3(t3_t const* const data3, const uint32_t handled_error);
void out_func4(t4_t const* const data4, const uint32_t handled_error);
void out_func5(t5_t const* const data5, const uint32_t handled_error);

typedef struct
{
    TrCode_t (*tr_func)(void);
    ErrCode_t (*in_func)(void* const data);
    void (*out_func)(const void* const data, const uint32_t handled_error);
}IOSet;

#define FUNCTION_COUNT ((uint32_t)5)
IOSet ioMap[FUNCTION_COUNT] =
{
    {.tr_func = (void*)&tr_func1, .in_func = (void*)&in_func1, .out_func = (void*)&out_func1},
    {.tr_func = (void*)&tr_func2, .in_func = (void*)&in_func2, .out_func = (void*)&out_func2},
    {.tr_func = (void*)&tr_func3, .in_func = (void*)&in_func3, .out_func = (void*)&out_func3},
    {.tr_func = (void*)&tr_func4, .in_func = (void*)&in_func4, .out_func = (void*)&out_func4},
    {.tr_func = (void*)&tr_func5, .in_func = (void*)&in_func5, .out_func = (void*)&out_func5}
};

void execute_io(IOSet io, void * const pData)
{
    if(evalTr(io.tr_func()))
    {
        const ErrCode_t myErr = io.in_func(pData);
        const uint32_t myOutErr = evalError(myErr);

        io.out_func(pData,myOutErr);
    }
}

void do_all_my_work()
{
    uint32_t i;
    //allocate a buffer sufficiently large to hold any of the data types on the stack
    // + 8 to allow correcting pointer for any alignment issues
    uint8_t dataBuffer[MAX_DATA_TYPE_SIZE + 8];
    uint64_t dataHandle = (uint64_t)&dataBuffer;
    //ensure the dataHandle is divisible by 8 to satisfy all possible alignments
    //all 8 byte alignments are also valid 4 byte alignments
    //all 4 byte alignments are also valid 2 byte alignments
    //all 2 byte alignments are also valid 1 byte alignments

    //you can use a smaller type than uint64_t for this computation if your pointers are smaller,
    //but this should work for pointers of all sizes up to 64 bits
    if((dataHandle % 8) > 0)
    {
        dataHandle += 8ULL-(dataHandle % 8);
    }

    for(i = 0; i < FUNCTION_COUNT; i++)
    {
        execute_io(ioMap[i], (void*)dataHandle);
    }
}

uint32_t evalTr(TrCode_t code)
{
    static uint32_t result = 0;
    result ^= 1;
    return result;
}

uint32_t evalError(ErrCode_t code)
{
    return 0;
}

ErrCode_t in_func1(t1_t* const data1)
{
    *data1 = 1;
    return 0;
}

ErrCode_t in_func2(t2_t* const data2)
{
    *data2 = 2;
    return 0;
}

ErrCode_t in_func3(t3_t* const data3)
{
    *data3 = 3;
    return 0;
}

ErrCode_t in_func4(t4_t* const data4)
{
    *data4 = 4;
    return 0;
}

ErrCode_t in_func5(t5_t* const data5)
{
    *data5 = 5;
    return 0;
}


TrCode_t tr_func1()
{
    return 0;
}

TrCode_t tr_func2()
{
    return 0;
}

TrCode_t tr_func3()
{
    return 0;
}

TrCode_t tr_func4()
{
    return 0;
}

TrCode_t tr_func5()
{
    return 0;
}


void out_func1(t1_t const* const data1, const uint32_t handled_error)
{
    printf("%d\n", *data1);
    return;
}

void out_func2(t2_t const* const data2, const uint32_t handled_error)
{
    printf("%d\n", *data2);
    return;
}

void out_func3(t3_t const* const data3, const uint32_t handled_error)
{
    printf("%f\n", *data3);
    return;
}

void out_func4(t4_t const* const data4, const uint32_t handled_error)
{
    printf("%f\n", *data4);
    return;
}

void out_func5(t5_t const* const data5, const uint32_t handled_error)
{
    printf("%llu\n", *data5);
    return;
}

int main()
{
    for(;;)
    {
        do_all_my_work();
    }
    return 0;
}

【讨论】:

  • 我必须了解一下您的解决方案才能了解您的解决方案。但我的第一个问题是:像在 IOMap 表中那样将函数指针转换为 void * 是否安全? (保证函数指针适合 void *。我认为这个保证只适用于对象指针)
  • 系统中所有指针的大小都相同,唯一改变的大小是它们指向的值的大小。现在 void* 的对齐方式只保证与 char* 相同,但是,我将函数指针转换为 void*,然后将该 void* 分配给预分配的函数指针,这样我作为函数调用的指针是有保证的具有正确的对齐方式和值作为函数指针
  • 第二个问题:取消引用声明为具有不同签名的函数指针是否与指向的函数不同?当您使用 oi.in_func 和 oi.out_func 作为指向的函数的数据指针不是 void * 而是 tx_t 时,就会发生这种情况。
  • 关于函数指针大小和对象指针大小相同(并且与 void * casts 兼容):这是“公正”的现实,还是根据 C 标准的情况?除此之外,该解决方案看起来确实不错。
  • 再次,c 实际上并不关心变量的类型,只关心变量的大小和对齐方式。所以只要我传递的参数具有与原始函数相同的大小和对齐方式,表面类型是否不同并不重要
猜你喜欢
  • 1970-01-01
  • 2018-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-18
  • 2018-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多