Creating alternative C API for existing library

77 Views Asked by At

Is it considered a bad practice to expose different type names in API and not use types from the library itself?

If there is a library like this:

struct Internal {
    int first;
    float second;
};

int foo(struct Internal);

typedef enum {
    VALUE_1,
    VALUE_2
} INTERNAL_ENUM;

int bar(INTERNAL_ENUM);

It can be linked with header looking like this:

struct External {
    int first;
    float second;
};

int foo(struct External);

typedef enum {
    VALUE_1,
    VALUE_2
} EXTERNAL_ENUM;

int bar(EXTERNAL_ENUM);

In a situation where I create library that uses another library and I don't want that library to be exposed, is it considered impractical to use different type names for the API?

As the project is currently setup (I inherited it), there are two sets of data types, one maintained by me which mirrors the one used in some internal (statically linked) libraries. Code is more complex and full of bugs and I'm asking if my solution would be any better?

1

There are 1 best solutions below

2
John Bollinger On

In a situation where I create library that uses another library and I don't want that library to be exposed, is it considered impractical to use different type names for the API?

"Impractical" would be one word for it.

C's rules for type compatibility require structure types not only to have matching member lists, but also to have matching tags or to match in being tagless. Similarly for unions and enums. The rules also require all declarations of the same object or function to specify compatible types for it.*

This means that doing as you describe elicits undefined behavior. In this case, that includes not only undefined runtime behavior but undefined compile time behavior, too.

Given

application ---uses---> library 1 ---uses---> library 2

you have the choice of whether the headers of library 1 expose the data types of library 2 to application at all. If you choose for them to do, then for structure and union types you also have the choice of whether to expose them as opaque types. But if you want well defined behavior then you do not have the choice of re-tagging library 2's structure, union, or enum types for application consumption. Different tags yield incompatible types, as C uses the term.

As the project is currently setup (I inherited it), there are two sets of data types, one maintained by me which mirrors the one used in some internal (statically linked) libraries. Code is more complex and full of bugs and I'm asking if my solution would be any better?

I don't see what you think would be gained by continuing to "mirror" the data types of the internal libraries, but with different names. It sounds like that would make your problems worse in several ways, not better.

But you absolutely should come up with a way to avoid duplicating data type definitions. Arrange one way or another for all translation units involved to draw on the same definitions (or none at all) for all data types used. This is the purpose of headers.

Of course, this assumes that you are going to pass objects from the application level through library 1 to library 2, which certainly seems to be what you're describing. And that may be a good plan, but it is not your only alternative.


*This is about the types themselves, not type aliases declared via typedef.