I am expanding on Jens Gustedt’s answer since the OP still has questions.
First, it is unclear why you have separate header files for the two implementations (“natural C” and “optimized C”). If they implement the same API, one header should serve for either.
Jens Gustedt’s recommendation is that you declare a struct foo
in the header but define it only in the C source file for the implementation and not in the header. A struct
declared in this way is an incomplete type, and source code that can only see the declaration, and not the definition, cannot see what is in the type. It can, however, use pointers to the type.
The declaration of an incomplete struct
may be as simple as struct foo
. You can also define a type, such as typedef struct foo foo;
or typedef struct foo Mytype;
, and you can define a type that is a pointer to the struct
, such as typedef struct foo *FooPointer;
. However, these are merely for convenience. They do not alter the basic notion, that there is a struct foo
that API users cannot see into but that they can have pointers to.
Inside the implementation, you would fully define the struct
. If you want an unsigned int
in the struct
, you would use:
struct foo
{
unsigned int x;
};
In general, you define the struct foo
to contain whatever data you like.
Since the API user cannot define struct foo
, you must provide functions to create and destroy objects of this type as necessary. Thus, you would likely have a function declared as extern struct foo *FooAlloc(some parameters);
. The function creates a struct foo
object (likely by calling malloc
or a related function), initializes it with data from the parameters, and returns a pointer to the object (or NULL if the creation or initialization fails). You would also have a function extern void FooFree(struct foo *p);
that frees a struct foo
object. You might also have functions to reset, set, or alter the state of a foo object, functions to copy foo objects, and functions to report about foo objects.
Your implementations could also define some global struct foo
objects that could be visible (essentially by address only) to API users. As a matter of good design, this should be done only for certain special purposes, such as to provide instances of struct foo
objects with special meanings, such as a constant object with a permanent “initial state” for copying.
Your two implementations, the “natural C” and the “optimized C” implementations may have different definitions for the struct foo
, provided they are not both used in a program together. (That is, each entire program is compiled with one implementation or the other, not both. If necessary, you could mangle both into a program by using a union, but it is preferable to avoid that.)
This is not a singleton approach.