问题
I am considering realizing a simple interface pattern in the C language.
A key characteristic is that it would provide multiple definitions for an opaque sturcture supplied by the interface's public header, that is, different implementations would provide different underlaying data for that structure (so across different translation units, the same structure would have different implementation).
I couldn't find any reference whether this would be a good or bad design pattern. At least it doesn't seem to violate the strict aliasing rule, only relying on the consistent order and padding for the common first element(s), which is guaranteed by the C standard (as far as I see). Of course I am aware of opject oriented patterns in C, but I couldn't see this particular one used.
Is this acceptable? Was such a pattern even used? (I couldn't find anything)
For easing understanding, following is a working example of three source files:
reader.h (The public interface definition)
#ifndef READER_H
#define READER_H
typedef struct reader_s reader_t;
char reader_read(reader_t* r);
#endif
reader.c (Glue logic for the interface)
#include "reader.h"
typedef char (reader_f)(reader_t*);
struct reader_s{
reader_f* rfunc;
};
char reader_read(reader_t* r)
{
return r->rfunc(r);
}
reader1.h (An implementation of the interface, header)
#ifndef READER1_H
#define READER1_H
#include "reader.h"
reader_t* reader1_get(void);
#endif
reader1.c (An implementation of the interface, code)
#include "reader1.h"
typedef char (reader_f)(reader_t*);
struct reader_s{
reader_f* rfunc;
int pos;
};
static char reader1_read(reader_t* r);
reader_t reader1_obj = {&reader1_read, 0};
reader_t* reader1_get(void)
{
return &reader1_obj;
}
static char reader1_read(reader_t* r)
{
char rval = 'A' + (r->pos);
(r->pos) = (r->pos) + 1;
if ((r->pos) == 24){ (r->pos) = 0; }
return rval;
}
main.c (Example usage)
#include "reader1.h"
#include <stdio.h>
int main(void)
{
reader_t* rd = reader1_get();
int i;
printf("\n");
for (i = 0; i < 60; i++){
printf("%c", reader_read(rd));
}
printf("\n");
return 0;
}
Possibly the interface should provide another header file for implementers, providing the definition of the structure head containing the function pointers. Maybe it could even rather point to a class structure containing those to minimize object sizes for classes with many methods.
回答1:
Nothing in the C Standard would define behavior in such cases, but if an implementation specifies the storage formats of data types and the means by which inter-module calls are handled, behavior could be defined as a consequence of that. Note, however, that some implementations with "link-time optimization" might not always perform inter-module calls in the "normal" documented fashion.
The only time I can see that such an approach might be workable would be if the public API associated with a type wanted to allow code to declare instances of the type directly or use the assignment operator with them, but did not otherwise want to commit to expose the field layout for any other purpose. In that case, having a public structure type containing something like a uint32[]
or uint64[]
might be a reasonable approach even if all code that would process the internals of the type used a "real" structure type. Such a design would avoid the need to recompile client code if the internal structure type changed, provided the size remained constant. On the other hand, I wouldn't consider any such design for code that might need to be processed with gcc-style type-based aliasing "optimizations" enabled, since gcc is willfully blind to places where such optimizations would break code.
来源:https://stackoverflow.com/questions/39036857/opaque-structures-with-multiple-definitions