Есть ли способ передать тип структуры функции c - PullRequest
3 голосов
/ 16 сентября 2008

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

Я также заметил, что в этих функциях также происходит блокировка мьютекса, поэтому я думаю, что могу оставить их в покое ...

Ответы [ 8 ]

6 голосов
/ 16 сентября 2008

Если вы убедитесь, что поле размещено в одном и том же месте в каждой такой структуре, вы можете просто привести указатель, чтобы получить поле. Этот метод используется во многих системных библиотеках низкого уровня, например BSD розетки.

struct person {
  int index;
};

struct clown {
  int index;
  char *hat;
};

/* we're not going to define a firetruck here */
struct firetruck;


struct fireman {
  int index;
  struct firetruck *truck;
};

int getindexof(struct person *who)
{
  return who->index;
}

int main(int argc, char *argv[])
{
  struct fireman sam;
  /* somehow sam gets initialised */
  sam.index = 5;

  int index = getindexof((struct person *) &sam);
  printf("Sam's index is %d\n", index);

  return 0;
}

При этом вы теряете безопасность типов, но это ценная техника.

[Сейчас я фактически протестировал приведенный выше код и исправил различные мелкие ошибки. Это намного проще, когда у вас есть компилятор. ]

3 голосов
/ 16 сентября 2008

Поскольку структуры - это не что иное, как предопределенные блоки памяти, вы можете сделать это. Вы можете передать void * в структуру и целое число или что-то еще, чтобы определить тип.

С этого момента самый безопасный способ сделать это - преобразовать пустоту * в указатель соответствующего типа перед доступом к данным.

Вам нужно быть очень, очень осторожным, поскольку вы теряете безопасность типов при приведении к пустоте *, и вы, вероятно, можете столкнуться с трудной отладкой ошибки времени выполнения при выполнении чего-то подобного.

1 голос
/ 19 сентября 2008

Если проверяемое поле идентификатора является частью общей начальной последовательности полей, общих для всех структур, то использование объединения гарантирует, что доступ будет работать:

#include <stdio.h>

typedef struct
{
    int id;
    int junk1;
} Foo;

typedef struct
{
    int id;
    long junk2;
} Bar;

typedef union
{
    struct
    {
        int id;
    } common;

    Foo foo;
    Bar bar;
} U;

int matches(const U *candidate, int wanted)
{
    return candidate->common.id == wanted;
}

int main(void)
{
    Foo f = { 23, 0 };
    Bar b = { 42, 0 };

    U fu;
    U bu;

    fu.foo = f;
    bu.bar = b;

    puts(matches(&fu, 23) ? "true" : "false");
    puts(matches(&bu, 42) ? "true" : "false");

    return 0;
}

Если вам не повезло, и поле появляется с различными смещениями в различных структурах, вы можете добавить параметр смещения в вашу функцию. Затем, offsetof и макрос-оболочка имитируют то, что запрашивал OP - передавая тип структуры на сайт вызова:

#include <stddef.h>
#include <stdio.h>

typedef struct
{
    int id;
    int junk1;
} Foo;

typedef struct
{
    int junk2;
    int id;
} Bar;

int matches(const void* candidate, size_t idOffset, int wanted)
{
    return *(int*)((const unsigned char*)candidate + idOffset) == wanted;
}

#define MATCHES(type, candidate, wanted) matches(candidate, offsetof(type, id), wanted)

int main(void)
{
    Foo f = { 23, 0 };
    Bar b = { 0, 42 };
    puts(MATCHES(Foo, &f, 23) ? "true" : "false");
    puts(MATCHES(Bar, &b, 42) ? "true" : "false");

    return 0;
}
1 голос
/ 17 сентября 2008

Я думаю, вы должны взглянуть на стандартные функции C qsort () и bsearch () для вдохновения. Это универсальный код для сортировки массивов и поиска данных в предварительно отсортированном массиве. Они работают с любым типом структуры данных, но вы передаете им указатель на вспомогательную функцию, которая выполняет сравнения. Вспомогательная функция знает подробности структуры и поэтому правильно выполняет сравнение.

Фактически, поскольку вы хотите выполнять поиск, может оказаться, что все, что вам нужно, это bsearch (), хотя, если вы строите структуры данных на лету, вы можете решить, что вам нужна другая структура, чем отсортированный список. , (Вы можете использовать отсортированные списки - это просто приводит к замедлению работы по сравнению, скажем, с кучей. Однако для правильной работы вам понадобится общая функция heap_search () и функция heap_insert (), и такие функции не стандартизированы в C. Поиск в Интернете показывает, что такие функции существуют - не по этому имени; просто не пытайтесь выполнить «поиск в куче», поскольку предполагается, что вы имели в виду «дешевый поиск» и вы получаете тонны мусора!)

0 голосов
/ 16 сентября 2008

Краткий ответ: нет. Однако вы можете создать свой собственный метод для этого, то есть предоставить спецификацию для создания такой структуры. Однако, как правило, это не нужно и не стоит усилий; просто перейдите по ссылке. (callFuncWithInputThenOutput(input, &struct.output);)

0 голосов
/ 16 сентября 2008

Вы можете сделать это с параметризованным макросом, но большинство политик кодирования будут недовольны.


#include 
#define getfield(s, name) ((s).name)

typedef struct{
  int x;
}Bob;

typedef struct{
  int y;
}Fred;

int main(int argc, char**argv){
    Bob b;
    b.x=6;

    Fred f;
    f.y=7;

    printf("%d, %d\n", getfield(b, x), getfield(f, y));
}
0 голосов
/ 16 сентября 2008

Я немного заржавел на c, но попробуйте использовать указатель void * в качестве типа переменной в параметре функции. Затем передайте адрес структуры функции, а затем используйте его так, как вы бы.

void foo(void* obj);

void main()
{
  struct bla obj;
  ...
  foo(&obj);
  ...
}

void foo(void* obj)
{
  printf(obj -> x, "%s")
}
0 голосов
/ 16 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...