用于处理C中不均匀签名函数的函数指针的模式

时间:2015-05-13 17:50:28

标签: c function-pointers

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

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提供了多个触发功能和定时器。)

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

 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函数。 (不,上面的代码肯定不是一个可执行的例子,而只是一个概念)

1 个答案:

答案 0 :(得分:0)

我写了一些我相信你想要的东西。如果我遗失了某些东西,请告诉我。我为您提到的各种函数和类型编写了存根,因此代码将编译,但无论使用的类型或您定义的函数中使用的实现,它都应该工作。基本上它是有效的,因为所有指针都只是在内存中给出值的地址的整数,并且只要指针指向足够大以包含所需值的位置,该内存的内容对于传递指针是不重要的。有时会引起这种观念的唯一问题是某些编译器需要特定的数据类型沿特定的字节边界对齐。为了解决这个问题,我们首先注意到这些字节边界都是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;
}