需要的方式来改变不同的结构共同领域(Need way to alter common fields

2019-07-18 13:15发布

我在编程在这儿用,适用于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 );
}

很显然,我写的有不起作用由于空指针和某种形式的转换是为了,但不是一个真正的好办法来施放(有没有?)。 我不反对,不知怎的,指定结构的类型,一个额外的参数。

对于不相关的原因,我不能改变这些结构或将它们组合起来或者做任何其他事情他们。 我不得不使用它们,因为它们。

与往常一样,在此先感谢您的帮助。

编辑:正如我在下面留言补充,共同字段都不能保证所有的开头或全部在年底或其他任何东西。 他们只是保证存在。

Answer 1:

考虑到结构不具有至少一个类似的布局,有办法限制为做到这一点,无论是短期或优雅:

  1. 使用哈希表,而不是结构来存储数据 。 否则,将有可能与他们的名字访问字段作为哈希表的键,而不管实际类型。 这需要在C很多麻烦,且仅当您需要访问说,百场,我不会推荐它。

  2. 坚持强类型和明显更新的领域 。 是啊,这是你不想要的东西,但对于田适量的,这可能是好的。

  3. 使用宏为其他建议 。 但是我认为这是最好的避免宏只要有可能。 这是不值得的。

这是不容易,也不好砍一起用C像这样(没有鸭打字)。 这是一个典型的例子,其中,动态语言会比较舒服(也许你可以从你的代码中调用蟒蛇?:))



Answer 2:

得墨忒耳的规则 - 只露出最低结构 - 可以申请。

static void updateStreetAndCity ( char *street, char *city ) {
    strcpy( street, streetFromSomethingElse );
    strcpy( city, cityFromSomethingElse );
}

其中有,你有两个指针,而不是一个称之为轻微的开销,但它符合一个功能的法案,将您所有的结构类型的工作。

如果您的结构是不同的大小,你可以使用宏给静态多态,就像在C99的tgmath.h做,但是这是相当多的工作。



Answer 3:

#define ASSIGN(type,object,member,value) do { \
    ((type *)object)->member = value; \
    } while (0)

现在你可以做的东西,如:

#include <stdio.h>

struct foo {
    int x;
    char y;
};

struct bar {
    char y;
    int x;
};

int main () {
    struct foo foo;
    struct bar bar;
    ASSIGN(struct foo, &foo, x, 100);
    ASSIGN(struct bar, &bar, y, 'x');
    // ...
}

当然,你总是可以延长该纳入一个函数调用做动作,以使支持memcpystrcpy或类似的将是微不足道的。



Answer 4:

这是时候当我插上的C ++,因为这是多态的明显的用途。 或模板。 或函数重载。

但是,如果所有的公共字段是第一,你应该能够只投指针到ADDR_A并把它当作这样的:

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


Answer 5:

你可以使用宏 - 如果填充Adam建议行不通

#define UPDATE_FIELDS( myStruct, aStreet, aCity ) \
  strcpy( myStruct->street, aStreet ); \
  strcpy( myStruct->city, aCity );

但是C ++插头较好;)



Answer 6:

只要你要更新的部分是每个结构的共同的前缀,你可以定义一个新的结构,它列出了这些前缀字段,您的参数投了这一点。 假设您创建前缀结构的布局相同(即没有奇怪的编译器填充的问题),该更新将与该前缀的所有结构的工作。

编辑:作为sztomi正确地指出你的,只要磁场的相对偏移量是相同的,那么你就可以访问非前缀的元素。 不过,我还是更喜欢创造一个共同的结构作为一个“模板”这个功能; 它扼守针对字段名的变化在任何其他结构您可以选择。



Answer 7:

只是一个未经测试的想法:

struct ADDR_X_offset {
  int street;
  int city;
} ADDR_A_offset = { offsetof(ADDR_A,street), offsetof(ADDR_A,city) },
  ADDR_B_offset = { offsetof(ADDR_B,street), offsetof(ADDR_B,city) };

static void updateFields( void *myStruct, struct ADDR_X_offset *offset ) {

    strcpy( myStruct+offset->street, streetFromSomethingElse );

    strcpy( myStruct+offset->city,   cityFromSomethingElse );

}


Answer 8:

尚未提及的一个选择是创建的字段的描述符,使用offsetof()宏从<stddef.h> 然后,您可以通过适当的描述符的修改功能。

typedef struct string_descriptor
{
    size_t offset;
    size_t length;
} string_descriptor;

在C99,然后你可以做这样的事情:

int update_string(void *structure, const string_descriptor d, const char *new_val)
{
    char *buffer = (char *)structure + d.offset;
    size_t len = strlen(new_val);
    size_t nbytes = MIN(len, d.length);
    // Optionally validate lengths, failing if the new value won't fit, etc
    memcpy(buffer, new_val, nbytes);
    buffer[nbytes] = '\0';
    return(0);  // Success
}

void some_function(void)
{
    ADDR_A a;
    ADDR_B b;
    static const string_descriptor a_des =
        { .offset = offsetof(ADDR_A, street), .length = sizeof(a.street) };
    static const string_descriptor b_des =
        { .offset = offsetof(ADDR_B, street), .length = sizeof(b.street) };

    update_string(&a, a_des, "Regent St."); // Check return status for error!
    update_string(&b, b_des, "Oxford St."); // Check return status for error!
}

显然,单一类型的,这看起来笨拙,但仔细概括,您可以使用多个描述符和描述符可以是更大(更复杂),但按引用传递,而且只需要定义一次,等有了合适的描述中,相同update_string()函数可用于更新任何无论是在两个结构的字段。 棘手位是长度初始化sizeof(b.street) ; 我认为,需要正确类型的变量各地客户提供正确的信息,但我会很高兴采取(标准C99)修订,删除限制。 非最小通用描述符可以包含可能对相关类型指示符和成员的姓名和其他信息 - 我坚持一个非常简单的结构,尽管诱惑阐述。 您可以使用函数指针,为不同的字符串提供不同的验证 - 纬度和经度的验证是从街道名称验证很大的不同。

您也可以编写采取的描述符阵列和做多的更新功能。 例如,你可以有描述符中ADDR_A领域和ADDR_B一个数组另一个,然后通过在新的字符串,等等的阵列等领域的新值

该责任是你得到正确的描述符,并使用正确的描述符。


另请参见: 目的是什么,并返回__builtin_offsetof运营商的类型?



Answer 9:

如果你不能以任何方式改变结构,我想传递的类型的想法是好的。 让为数众多的做一件事到一个地址的功能,并将其带地址的类型。

int updateStreetAndCity(void *addr, int type);
double getLatitude(void *addr, int type);

等你可以有一个枚举,或几个宏定义,使类型。 然后在每个功能的基础上,类型,你传递,调用特定于该类型的另一个函数做的工作适合你(避免怪物switch语句这确实一千个不同的东西)。



Answer 10:

如果你没有传递的结构类型(如您的无效*建议),基本上就注定。 它们只是一个开始地址的大量内存。

可以做的是有几个功能,如你所说的update_fields并以某种方式调用正确的一个取决于结构的类型( 是容易的,可以很隐蔽在宏避免syntaxic噪声)。

这样做你的代码如下:

#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函数(无效*)在结构,第二个参数的大小需要用它来自动地转换为正确的类型。 这是很肮脏的,但有可能[请评论,如果有人希望看到的解决方案developped。

你也可以使用一个联盟。 通用字段将使用任何工会成员的访问,如果有一个共同的前缀(但我的理解是不是这样)。 你必须明白,定义这样的工会没有任何改变现有的数据结构。 这基本上限定比使用一个指针引用一个类型或者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);


Answer 11:

typedef struct {
    char street[10];
    char city[10];
} ADDR_BASE;

typedef struct {
    struct ADDR_BASE addr;
    char lat[10];
    char long[10];
} ADDR_A;

typedef struct {
    struct ADDR_BASE addr;
    char zip[10];
    char country[10];
} ADDR_B;

这种方式,您可以通过ADDR_BASE *给你的函数。



文章来源: Need way to alter common fields in different structs