Нужен способ изменить общие поля в разных структурах - PullRequest
3 голосов
/ 18 сентября 2009

Я программирую на C здесь, для Windows и различных платформ Unix. У меня есть набор структур, которые имеют общие поля, но также поля, которые отличаются. Например:

typedef struct {
    char street[10];
    char city[10];
    char lat[10];
    char long[10];
} ADDR_A;

typedef struct {
    char street[10];
    char city[10];
    char zip[10];
    char country[10];
} ADDR_B;

Они, очевидно, не так просты, и у меня на самом деле есть 6 различных структур, но это основная идея.

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

static void updateFields( void *myStruct ) {
    strcpy( myStruct->street, streetFromSomethingElse );
    strcpy( myStruct->city, cityFromSomethingElse );
}

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

По причинам, которые не имеют отношения, Я НЕ МОГУ изменить эти структуры или объединить их или сделать что-либо еще с ними. Я должен использовать их такими, какие они есть.

Как всегда, заранее спасибо за помощь.

РЕДАКТИРОВАТЬ: Как я добавил в комментарии ниже, общие поля НЕ гарантированно будут все в начале или все в конце или что-либо еще. Они просто гарантированно существуют.

Ответы [ 11 ]

0 голосов
/ 18 сентября 2009

Если у вас нет типа передаваемой структуры (как подсказывает ваша пустота *), вы обречены. Это всего лишь куча памяти с начальным адресом.

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

При этом вы получите код, указанный ниже:

#include <stdio.h>
#include <string.h>

typedef struct {
    char street[10];
    char city[10];
    char lat[10];
    char lon[10];
} ADDR_A;

typedef struct {
    char street[10];
    char city[10];
    char zip[10];
    char country[10];
} ADDR_B;

#define UPDATEFIELDS(type, value) updateFields_##type(value)

static void updateFields_ADDR_A(ADDR_A *myStruct ) {
        strcpy(myStruct->street, "abc" );
        strcpy(myStruct->city, "def" );
    }

static void updateFields_ADDR_B(ADDR_B *myStruct ) {
        strcpy(myStruct->street, "abc" );
        strcpy(myStruct->city, "def" );
    }

int main(){
    ADDR_A a;
    ADDR_B b;

    updateFields_ADDR_A(&a);
    updateFields_ADDR_B(&b);
    UPDATEFIELDS(ADDR_A, &a);

    printf("%s\n", a.city);
    printf("%s\n", b.city);
}

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

Если вы также хотите иметь локальный код, вы можете снова использовать макрос,

#define COMMONBODY strcpy(myStruct->street, "abc" );\
        strcpy(myStruct->city, "def" );


static void updateFields_ADDR_A(ADDR_A *myStruct ) {
    COMMON_BODY
    }

static void updateFields_ADDR_B(ADDR_B *myStruct ) {
    COMMON_BODY
    }

или даже поместить общий код в отдельный файл и включить его два раза (довольно необычный, но рабочий файл)

static void updateFields_ADDR_A(ADDR_A *myStruct ) {
    #include "commonbody.c"
    }

static void updateFields_ADDR_B(ADDR_B *myStruct ) {
    #include "commonbody.c"
    }

Также почти возможно (но не так долго, как я знаю) сделать это, используя оператор typeof , за исключением части искажения имени.

Если ваша структура имеет другой размер, мы могли бы даже использовать только одну функцию updateFields (с void *), которая принимает размер структуры в качестве второго параметра и использует ее для автоматического приведения к нужному типу. Это довольно грязно, но возможно [пожалуйста, прокомментируйте, если кто-то хочет увидеть это решение разработанным].

Вы также можете использовать союз. Общие поля будут доступны любому члену объединения, если есть общий префикс (но я понимаю, что это не так). Вы должны понимать, что определение такого объединения ничего не меняет в существующей структуре данных. Это в основном определяет тип ADDR_A или ADDR_B, на который можно ссылаться с помощью указателя.

typedef union {
    ADDR_A a;
    ADDR_B b;
} ADDR_A_or_B;

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

static void updateFields(ADDR_A_or_B *myStruct ) {
    strcpy( myStruct->a.street, someStreeValueFromSomewhereElse);
    strcpy( myStruct->a.city, someCityValueFromSomewhereElse);
}

Вам просто нужно разыграть при вызове:

ADDR_A a;
updateFields((ADDR_A_or_B *)&a);

ADDR_B b;
updateFields((ADDR_A_or_B *)&b);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...