как я могу перебрать массив типа generi c (void **) с указателем на функцию в C? - PullRequest
0 голосов
/ 18 июня 2020

Я хочу выполнить простую задачу, работая с указателями функций в C. Задача состоит в том, чтобы получить массив (из любого типа, например: int / char *) и суммировать / объединить каждые 2 элемента в массиве. для типа char * он работает нормально, но для типа int l oop кажется, перепрыгивает через каждые 2 элемента в массиве (и, таким образом, переполняет массив):

#define N1 4
#define N2 4
typedef void*(*Fn_Sum)(void*, void*);
typedef void(Fn_Prt)(void*);

int sum_num(int a, int b){
    return a + b;
}

char* sum_char(char* a, char* b){
    char *result = malloc(strlen(a) + strlen(b) + 1);
    if (!result) {
        printf("ERROR: malloc failed !\n");
        return NULL;
    }
    strcpy(result, a);
    strcat(result, b);

    return result;
}

void print_num(int a){
    printf("%d", a);
}

void print_string(char* a){
    int i = 0;
    while (a[i] != '\0') {
        printf("%c", a[i]);
        i++;
    }
}
void PrintSums(void** P, int n, Fn_Sum fsum, Fn_Prt fprt){

    for(int i = 0; i < n - 1; i++){
        (fprt)(fsum(P[i], P[i+1]));
        printf(", ");
    }
    printf("\n");
}

int main() {

    int V[N1] = {1,2,3,4};
    char* S[N2] = {"a", "d", "c", "d"};
    PrintSums(V, N1, sum_num, print_num);
    PrintSums(S, N2, sum_char, print_string);
    return 0;
}

ожидаемый результат : 3, 5, 7, ab, b c, cd, фактические выходы: 4, 725939, 4925336, ad, d c, cd,

1 Ответ

0 голосов
/ 18 июня 2020

Создайте абстрактный интерфейс для итератора по элементам. черновик такого интерфейса может выглядеть так:

struct iterator {
   ...
};
// ptr - a pointer to beginning of the array
// size - size of one element in the array
void it_init(iterator *t, void *ptr, size_t size);
bool it_eq(iterator *t, iterator *o); // compare iteratores
void it_add(iterator *t, size_t n);
void it_inc(iterator *t);
// return a pointer to the element
void *it_get(iterator *t);

Не забывайте, что всегда передает обратным вызовам пользователя переменные контекста. В противном случае пользователям придется использовать глобальные переменные, что сделает код беспорядочным. Создайте абстрактный интерфейс с деструкторами и конструкторами вашего суммирующего объекта. Правильно обрабатывать ошибки:

// is passed a pointer to user context
// returns 0 on success
typedef int (*Fn_Sum)(void*, void*);
// is passed a pointer to user context
// returns 0 on success
typedef int (Fn_Prt)(void*);
// returns 0 on success
int PrintSums(iterator it, size_t n, Fn_Sum fsum, Fn_Prt fprt, void *sumctx);

После этого реализуйте объекты, которые предоставляют нужный вам интерфейс:

struct num { .. };
void num_sum(struct num *t, int el);
void num_print(struct num *t, int el);
// expose interface to PrintSums
// that just calls internal api
int num_PrintSums_Fn_Sum(void *ctx, void *el0) {
    struct num *t = ctx;
    int *el = el0;
    num_sum(t, *el);
    return 0;
}
int num_PrintSums_Fn_Prt(void *ctx) {
    struct num *t = ctx;
    num_print(t);
    return 0;
}

Пример всей программы выглядит так:

#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

// iterator
typedef struct iterator {
    void *ptr;
    size_t size;
} iterator;
void it_init(iterator *t, void *ptr, size_t size) {
    *t = (iterator){ ptr, size, };
}
// eq is from test(1) shell command. "eq" means "equal"
bool it_eq(iterator *t, iterator *o) {
    return t->ptr == o->ptr;
}
void it_add(iterator *t, size_t n) {
   t->ptr = (char*)t->ptr + t->size * n;
}
// increment the iterator
void it_inc(iterator *t) {
   it_add(t, 1);
}
// return a pointer to the element
void *it_get(iterator *t) {
   return t->ptr;
}

// interface
typedef int (*Fn_Sum)(void*, void*);
typedef int (Fn_Prt)(void*);
int PrintSums(iterator it, size_t n, Fn_Sum fsum, Fn_Prt fprt, void *sumctx){
    iterator end = it;
    it_add(&end, n);
    for(; !it_eq(&it, &end); it_inc(&it)) {
        int err = fsum(sumctx, it_get(&it));
        if (err) return err;
        err = fprt(sumctx);
        if (err) return err;
        printf(", ");
    }
    printf("\n");
    return 0;
}

// num object
struct num {
    int sum;
};
void num_init(struct num *t) {
    t->sum = 0;
}
void num_sum(struct num *t, int el){
    t->sum += el;
}
void num_print(struct num *t){
    printf("%d", t->sum);
}
void num_free(struct num *T) {
    // nothing, just exists for uniform API
}
// accessors for PrintSums
int num_PrintSums_Fn_Sum(void *ctx, void *el0) {
    struct num *t = ctx;
    int *el = el0;
    num_sum(t, *el);
    return 0;
}
int num_PrintSums_Fn_Prt(void *ctx) {
    struct num *t = ctx;
    num_print(t);
    return 0;
}

// string object
struct str {
    char *str;
};
void str_init(struct str *t) {
    t->str = NULL;
}
int str_sum(struct str *t, const char *str) {
    const size_t str_len = t->str == NULL ? 0 : strlen(t->str);
    void *p = realloc(t->str, str_len + strlen(str) + 1);
    if (p == NULL) {
        free(t->str);
        t->str = NULL;
        return -1;
    }
    t->str = p;
    memcpy(t->str + str_len, str, strlen(str) + 1);
    return 0;
}
void str_print(struct str *t) {
    if (t->str == NULL) {
        printf("(nul)");
    } else {
        printf("%s", t->str);
    }
}
void str_free(struct str *t) {
    free(t->str);
}
// interface for PrintSums
int str_PrintSums_Fn_Sum(void *ctx, void *el0) {
    struct str *t = ctx;
    const char **el = el0;
    str_sum(t, *el);
    return 0;
}
int str_PrintSums_Fn_Prt(void *ctx) {
    struct str *t = ctx;
    str_print(t);
    return 0;
}

// and finally main
int main() {
    int err = 0;

    int V[] = {1,2,3,4};
    iterator numit;
    it_init(&numit, V, sizeof(*V));

    struct num numsum; // the object that will hold the sum
    num_init(&numsum);
    err = PrintSums(numit, sizeof(V)/sizeof(*V), num_PrintSums_Fn_Sum, num_PrintSums_Fn_Prt, &numsum);
    if (err) abort();
    num_free(&numsum);

    char *S[] = {"a", "d", "c", "d"};
    iterator strit;
    it_init(&strit, S, sizeof(*S));

    struct str strsum; // the object that will hold the sum of strings
    str_init(&strsum);
    err = PrintSums(strit, sizeof(S)/sizeof(*S), str_PrintSums_Fn_Sum, str_PrintSums_Fn_Prt, &strsum);
    if (err) abort();
    str_free(&strsum); // YES! Remember to pick out the trash

}

и выходы на Godbolt :

1, 3, 6, 10, 
a, ad, adc, adcd, 

Указатели на конструктор и деструктор «объектов суммы» также могут быть переданы в PrintSums. Тем не менее, можно начать думать о создании виртуальной таблицы для всех этих указателей (ie. Одна структура с указателями функций, которые необходимы для PrintSums ...).

...