возвращение void * для возврата функции / метода - PullRequest
0 голосов
/ 27 января 2019

Возможно ли, чтобы функция в C возвращала "динамический" тип возврата

пример

printResult (NumOrChar());

void* NumOrChar(void) {
   // return int  or char 
}

void printResult (void* input) {
  if (isdigit(input)) {
     printf("It's a number");
}
  else {
     printf("It's not a number");
}

Ответы [ 4 ]

0 голосов
/ 28 января 2019

Вместо блока if можно использовать структуру указателей функций в качестве виртуальной таблицы, включая to_string.Следующее динамически создает Type, который может быть Num или Letter.

#include <stddef.h> /* offsetof */
#include <stdio.h>  /* [|s|sn]printf, fgets, stdin */
#include <stdlib.h> /* malloc, free, strtol */
#include <ctype.h>  /* isdigit */
#include <errno.h>
#include <assert.h>

struct Type;

typedef void (*TypeToString)(const struct Type *const, char (*const)[32]);
typedef void (*TypeAction)(struct Type *const);

struct Type {
    const struct TypeVt *vt;
};



/* Num extends Type. */

struct Num {
    struct Type base;
    int value;
};

static struct Num *num_upcast(struct Type *const type) {
    return (struct Num *)(void *)((char *)type - offsetof(struct Num, base));
}

static const struct Num *const_num_upcast(const struct Type *const type) {
    return (const struct Num *)(const void *)((const char *)type
        - offsetof(struct Num, base));
}

static void num_to_string(const struct Type *const type, char (*const a)[32]) {
    const struct Num *const num = const_num_upcast(type);
    snprintf(*a, sizeof *a, "%d", num->value); /* C99. */
}

static void num_delete(struct Type *const type) {
    struct Num *const num = num_upcast(type);
    free(num);
}



/* Letter extends Type. */

struct Letter {
    struct Type base;
    char letter;
};

static struct Letter *letter_upcast(struct Type *const type) {
    return (struct Letter *)(void *)((char *)type
        - offsetof(struct Letter, base));
}

static const struct Letter *const_letter_upcast(const struct Type *const type) {
    return (const struct Letter *)(const void *)((const char *)type
        - offsetof(struct Letter, base));
}

static void letter_to_string(const struct Type *const t, char (*const a)[32]) {
    const struct Letter *const letter = const_letter_upcast(t);
    sprintf(*a, "%c", letter->letter);
}

static void letter_delete(struct Type *const type) {
    struct Letter *const letter = letter_upcast(type);
    free(letter);
}



static const struct TypeVt {
    const char *name;
    const TypeToString to_string;
    const TypeAction delete;
} num_vt = { "num", &num_to_string, &num_delete },
    letter_vt = { "char", &letter_to_string, &letter_delete };

static void type_to_string(const struct Type *const t, char (*const a)[32]) {
    assert(t);
    t->vt->to_string(t, a);
}

static void type_delete(struct Type *const t) {
    assert(t);
    t->vt->delete(t);
}

static struct Type *num(const int value) {
    struct Num *num = malloc(sizeof *num);
    if(!num) return 0;
    num->base.vt = &num_vt;
    num->value = value;
    return &num->base;
}

static struct Type *letter(const char letter) {
    struct Letter *l = malloc(sizeof *l);
    if(!l) return 0;
    l->base.vt = &letter_vt;
    l->letter = letter;
    return &l->base;
}



static struct Type *read_type(void) {
    struct Type *type;
    char buffer[64];
    if(!fgets(buffer, sizeof buffer, stdin)) return 0;
    if(isdigit(buffer[0])) {
        long n;
        errno = 0;
        n = strtol(buffer, 0, 0);
        if(errno) return 0;
        type = num(n);
    } else {
        type = letter(buffer[0]);
    }
    return type;
}

int main(void) {
    char a[32];
    struct Type *type = 0;
    int is_success = 0;
    do {
        if(!(type = read_type())) break;
        type_to_string(type, &a);
        printf("\"%s\" is of type %s.\n", a, type->vt->name);
        is_success = 1;
    } while(0); {
        if(type) type_delete(type);
    }
    if(!is_success) return perror("Failure"), EXIT_FAILURE;
    return EXIT_SUCCESS;
}

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

$ bin/numorchar 
524645 3456542563456
"524645" is of type num.
$ bin/numorchar 
6245635724564357652654245634576
Failure: Result too large
$ bin/numorchar 
ata gfddsgsdg
"a" is of type char.
$ bin/numorchar 

"
" is of type char.
0 голосов
/ 27 января 2019

Функции, безусловно, могут возвращать void *.Но это особый тип указателя со свойствами, которые делают его пригодным для передачи указателей на объекты любого типа.Это , а не универсальный тип подстановочного знака.Более того, он не несет никакой информации о реальном типе объекта, на который он указывает, если таковой имеется, поэтому нет способа определить этот тип динамически.Программист C ++ мог бы описать эту ситуацию как C, не обеспечивающую никакого RTTI.

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

union num_or_string {
    struct { _Bool is_num; };
    struct { _Bool _x1; int num; };
    struct { _Bool _x2; char *string; };
};

union num_or_string NumOrChar(void) {
    // return a union num_or_string containing an int or a char *
}

void printResult (union num_or_string) {
    if (num_or_string.is_num) {
        printf("It's a number: %d\n", num_or_string.num);
    } else {
        printf("It's a string: %s\n", num_or_string.string);
    }
}
0 голосов
/ 27 января 2019

Полагаю, вы говорите о функции C # (согласно моему поиску Google ).

В C это невозможно, если вы не делаете это самостоятельно (другоеответы показывают примеры).Это может быть легко или сложно в зависимости от ваших потребностей.Вам следует подумать о переключении на другой язык, если вы действительно этого хотите (иногда их называют variants).

0 голосов
/ 27 января 2019

Вы можете использовать _Generic в некоторых случаях

int func_int(int *p)
{
    printf("%s\n", __FUNCTION__);
    return 5;  /* does not make too much sense */
}

float func_float(float *p)
{
    printf("%s\n", __FUNCTION__);
    return 5.0f;  /* does not make too much sense */
}

double func_double(double *p)
{
    printf("%s\n", __FUNCTION__);
    return 5.0;  /* does not make too much sense */
}


#define func(p) _Generic((p), \
              int *: func_int, \
              float *: func_float, \
              double *: func_double)(p) \
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...