Typedef抽象类型指针指向具体类型指针,以避免在C编程中进行强制转换

时间:2019-10-17 18:59:09

标签: c interface casting typedef

我有一个头文件,其中包含对抽象数据类型的指针进行操作以将依赖项隐藏在头中的函数。为了容纳多个平台,标头中声明的函数在源代码树的单独文件中具有多个实现/平台,每个都使用其自己的具体基础类型来实现接口。当然,根据构建中选择的平台,这些实现中只有一个在最终程序中链接。

foo.h

#pragma once
typedef struct foo Foo; // abstract: never defined in the program and used as pointer
Foo* foo_create(void);
void foo_transmogrify(Foo* f);

fooImplBar.c

#include "foo.h"
#include "bar.h" // from a library that defines Bar 

Foo* foo_create(void)
{
    Bar* bar = bar_create();
    return (Foo*) bar; // the cast is needed here to avoid the error
}

void foo_transmogrify(Foo* f)
{
     bar_transmogrify((Bar*) f); // another cast here
}

fooImplBaz.c

#include "foo.h"
#include "baz.h" // from a library that defines Baz

Foo* foo_create(void)
{
    Baz* baz = baz_create();
    return (Foo*) baz; // the cast is needed here to avoid the error
}

void foo_transmogrify(Foo* f)
{
     baz_transmogrify((Baz*) f); // another cast here
}

有办法避免这些强制转换吗?

换句话说,有没有办法在各自的文件夹中将Bar*Baz*键入Foo*?我无法在实现文件夹中使用Bar定义更改BazFoo定义,因为它们(Bar和Baz)来自库。因此,此处在头中进行前向声明并在.c文件中进行定义的模式不适用。 避免强制转换的一种方法是完全使用void*而不是Foo*,以丢失类型信息为代价,我发现这种类型信息比强制转换更糟糕。

1 个答案:

答案 0 :(得分:0)

变体1

您可以尝试使用联盟。这将使接口“ foo”依赖于所有实现(此处为“ bar”和“ baz”)。优点是无需更改实现。

foo.h

#include "bar.h"
#include "baz.h"

typedef union {
    Bar *bar;
    Baz *baz;
} Foo;

Foo foo_create(void);
void foo_transmogrify(Foo f);

fooImplBar.c

#include "foo.h"

Foo foo_create(void)
{
    Foo foo;
    foo.bar = bar_create();
    return foo;
}

void foo_transmogrify(Foo f)
{
    bar_transmogrify(f.bar);
}

fooImplBaz.c

#include "foo.h"

Foo foo_create(void)
{
    Foo foo;
    foo.baz = baz_create();
    return foo;
}

void foo_transmogrify(Foo f)
{
    baz_transmogrify(f.baz);
}

您保留所有类型信息。

我在一个简单的项目中尝试过,但是BarBaz在它们各自的标题中定义不完整。它也应该适用于完整类型。

bar.h

typedef struct bar Bar;
Bar* bar_create(void);
void bar_transmogrify(Bar* f);

bar.c

#include <stdio.h>
#include <stdlib.h>

#include "bar.h"

typedef struct bar {
    int i;
} Bar;

Bar* bar_create(void)
{
    Bar* bar = malloc(sizeof (Bar));
    bar->i = 23;
    return bar;
}

void bar_transmogrify(Bar* f)
{
    printf("Bar: %d\n", f->i);
}

baz.h

typedef struct baz Baz;
Baz* baz_create(void);
void baz_transmogrify(Baz* f);

baz.c

#include <stdio.h>
#include <stdlib.h>

#include "baz.h"

typedef struct baz {
    float f;
} Baz;

Baz* baz_create(void)
{
    Baz* baz = malloc(sizeof (Baz));
    baz->f = 3.14159;
    return baz;
}

void baz_transmogrify(Baz* f)
{
    printf("Baz: %f\n", f->f);
}

main.c

#include "foo.h"

int main(void)
{
    Foo f;
    f = foo_create();
    foo_transmogrify(f);
    return 0;
}

是的,我知道,如果在某些严重的情况下使用此代码,则会发生内存泄漏。将需要一些ba[rz]_destroy()。但这只是一个容易解决的小问题。

编译如下:

gcc -Wall -Wextra -pedantic -c fooImplBar.c -o fooImplBar.o
gcc -Wall -Wextra -pedantic -c fooImplBaz.c -o fooImplBaz.o
gcc -Wall -Wextra -pedantic -c bar.c -o bar.o
gcc -Wall -Wextra -pedantic -c baz.c -o baz.o
gcc -Wall -Wextra -pedantic -c main.c -o main.o
gcc -Wall -Wextra -pedantic main.o fooImplBar.o bar.o -o mainImplBar
gcc -Wall -Wextra -pedantic main.o fooImplBaz.o baz.o -o mainImplBaz

变种2:

每个实现都会完成不完整定义的结构。您可能需要调整实现,因为它需要使用相同的类型名称。

“ foo.h”与您的完全相同。

fooImplBar.c

#include "foo.h"
#include "bar.h"

Foo* foo_create(void)
{
    Foo* bar = bar_create();
    return bar;
}

void foo_transmogrify(Foo* f)
{
    bar_transmogrify(f);
}

fooImplBaz.c

#include "foo.h"
#include "baz.h"

Foo* foo_create(void)
{
    Foo* baz = baz_create();
    return baz;
}

void foo_transmogrify(Foo* f)
{
     baz_transmogrify(f);
}

您保留类型信息,因为它是相同的类型。但是,每个实现都会以自己的方式定义类型。

bar.h

typedef struct foo {
    int i;
} Foo;

Foo* bar_create(void);
void bar_transmogrify(Foo* f);

bar.c

#include <stdio.h>
#include <stdlib.h>

#include "bar.h"

Foo* bar_create(void)
{
    Foo* bar = malloc(sizeof (Foo));
    bar->i = 23;
    return bar;
}

void bar_transmogrify(Foo* f)
{
    printf("Bar: %d\n", f->i);
}

baz.h

typedef struct foo {
    float f;
} Foo;

Foo* baz_create(void);
void baz_transmogrify(Foo* f);

baz.c

#include <stdio.h>
#include <stdlib.h>

#include "baz.h"

Foo* baz_create(void)
{
    Foo* baz = malloc(sizeof (Foo));
    baz->f = 3.14159;
    return baz;
}

void baz_transmogrify(Foo* f)
{
    printf("Baz: %f\n", f->f);
}

main.c

#include "foo.h"

int main(void)
{
    Foo* f;
    f = foo_create();
    foo_transmogrify(f);
    return 0;
}

就像变体1一样完成编译。


如果我正确理解您的介绍,则不同的实现可能位于单独的文件夹中。然后,您可以使用相同的名称命名文件,类型和功能,因为在一个应用程序中不会使用多个文件。

这比较简单,但可能与您的设置不兼容。

这是示例的外观。 “ foo.h”和“ main.c”相同。

bar / foo.c

#include <stdio.h>
#include <stdlib.h>

#include "../foo.h"

typedef struct foo {
    int i;
} Foo;

Foo* foo_create(void)
{
    Foo* foo = malloc(sizeof (Foo));
    foo->i = 23;
    return foo;
}

void foo_transmogrify(Foo* f)
{
    printf("Bar: %d\n", f->i);
}

baz / foo.c

#include <stdio.h>
#include <stdlib.h>

#include "../foo.h"

typedef struct foo {
    float f;
} Foo;

Foo* foo_create(void)
{
    Foo* foo = malloc(sizeof (Foo));
    foo->f = 3.14159;
    return foo;
}

void foo_transmogrify(Foo* f)
{
    printf("Baz: %f\n", f->f);
}

您不再需要包装器“ fooImplBar”和“ fooImplBaz”。

编译:

gcc -Wall -Wextra -pedantic -c bar/foo.c -o bar/foo.o
gcc -Wall -Wextra -pedantic -c baz/foo.c -o baz/foo.o
gcc -Wall -Wextra -pedantic -c main.c -o main.o
gcc -Wall -Wextra -pedantic main.o bar/foo.o -o mainBar
gcc -Wall -Wextra -pedantic main.o baz/foo.o -o mainBaz