2015-05-13 2 views
4

Так, У меня есть огромный набор функций для чтения входов от какого-либо источника формы:шаблона для обработки функции указателя функций inhomogene подписи в C

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); 
... 

Также существует довольно сложный алгоритм в зависимости от функций триггера, который решает, могу ли я назвать функцию ввода, или нет. (это упрощенная картина, более чем одна триггерная функция и таймеры, предусмотренные для каждого ввода-вывода).

Этот алгоритм, но в основном говорит: Если триггер говорит «да», вызовите функцию ввода с указателем на переменную данных, проверьте ошибку, выполните некоторую проверку и затем передайте указатель обновленной переменной на выход.

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 должны быть некоторые функции оценки, которые используются правильно)

Я хотел бы, чтобы инкапсулировать этот Algo в собственной функции,

void execute_io(???) 

называться с некоторыми чтобы это сделать. Но я не могу думать о шаблоне, который бы соответствовал стандарту, без огромного количества функций обертки. Я завернуть ввод-функцию и функцию вывода на perfom правильных слепков, и корректировать свои подписи, как:

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 и окружающего кода, поэтому мне не нужно обертывать все эти функции?

Update: А вот в комбинации, как запрошенной от Barak Маноса:

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 */ 
} 

Я хочу найти другой шаблон, который делает не заставляйте меня обертывать все функции ввода-вывода. (и нет, приведенный выше код, конечно, не является исполняемым примером, а просто концепцией)

+0

Не могли бы вы просто показать полный фрагмент кода, который у вас есть сейчас, и указать, что именно вас не устраивает? Нам было бы намного легче, чем пытаться угадать, как все эти фрагменты кода объединяются вместе. –

+0

@barakmanos надеется, обновление помогает. –

ответ

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; 
} 
+0

Мне нужно найти ваше решение, чтобы понять ваше решение. Но мой первый вопрос: безопасно ли использовать указатель на функцию void *, как это было в таблице IOMap? (указатель на функцию гарантированно помещается в void *. Я думал, что эта гарантия действует только для указателя объекта) –

+0

все указатели в системе имеют одинаковый размер, размер которого изменяется только в размере значения, на которое они указывают. Теперь выравнивание void * является только гарантией того, что оно будет таким же, как char *, однако я преобразую указатель функции в void *, а затем присваиваю этот void * предварительно выделенному указателю функции, поэтому указатель, который я вызываю как функция, является guarenteed чтобы иметь правильное выравнивание и значение как указатель функции –

+0

И второй вопрос: сохраняется ли это для разыменования указателя функции, объявленного как отличающегося подписи, чем указана функция? Это происходит, когда вы используете oi.in_func и oi.out_func, поскольку указатель данных указанной функции не является void *, а tx_t. –

Смежные вопросы