Modern C++ pattern for ugly C struct allocation - c

I'm making an ioctl call from C++ into a driver I don't own/maintain, and I'm trying to sort out if there's a clean, "safe-ish" mechanism to deal with some of the ugly struct allocation required.
Slimmed down version of some structures involved
// IOCTL expects an instance of this structure "first"
typedef struct {
int param1;
int param2;
} s_ioctl_request;
//... followed by an instance of this. If attr_length
// is > sizeof(s_attr_header), more data is allowed to follow.
typedef struct {
uint32_t attr_length;
uint32_t attr_type;
} s_attr_header;
// Example that uses more data than just the header.
typedef struct {
s_attr_header hdr;
uint32_t attr_param;
} s_attr_type1;
// Another example.
typedef struct {
s_attr_header hdr;
uint32_t attr_param1;
uint32_t attr_param2;
} s_attr_type2;
The ioctl requires that s_ioctl_request be immediately followed by an s_attr_header, or other struct containing it, where attr_length is set to the size of the outer struct in bytes.
In C, to write a wrapper for the ioctl it would be done via something along these lines:
int do_ugly_ioctl(int fd, int p1, int p2, s_attr_header * attr)
{
int res;
// Allocate enough memory for both structures.
s_ioctl_request *req = malloc( sizeof(*req) + attr->hdr.attr_length );
// Copy the 2nd, (variable length) structure after the first.
memcpy( ((char*)req) + sizeof(*req), attr, attr->hdr.attr_length);
// Modify params as necessary
req->param1 = p1;
req->param2 = p2;
// Make the driver call, free mem, and return result.
res = ioctl(fd, SOME_IOCTL_ID, req);
free(req);
return res;
}
// Example invocation.
s_attr_type1 a1;
a1.hdr.attr_length = sizeof(a1);
a1.hdr.attr_type = 1;
do_ugly_ioctl(fd, 10, 20, &a1);
A couple options I'm thinking of, are:
Throw modern C++-isms out the window, and do exactly what I've shown above.
Allocate the storage with a std::vector, then do ugly casts with the resulting std::vector::data() pointer so at least I'm not doing new[] / delete[] or malloc / free.
Create a unique wrapper method for each s_attr_type* that uses its own "special" struct. This seems "safest", i.e. least likely for the user of the wrapper method to screw it up. And bonus points, allows pass-by-ref.
Method #3 example:
int do_ugly_ioctl(fd, int param1, int param2, s_attr_type2& attr){
struct RequestData {
s_ioctl_request ioreq;
s_attr_type2 attr;
};
RequestData r;
r.ioreq.param1 = param1;
r.ioreq.param2 = param2;
r.attr = attr;
r.attr.hdr.attr_length = sizeof(attr); // Might as well enforce this here.
ioctl(fd, SOME_IOCTL_ID, (void*) &r);
}
So I guess some questions here are:
Is it "worth it" to C++-ize a solution to this problem? (as opposed to relying on the more error-prone C impl).
If I go with method #3 or similar, is there anything that I can do with <type_traits> to make a template of this function and only accept structs with an s_attr_header as the first member?
Any other brilliant ideas?

Totally worth it, and your solution is quite nice.
You might want to declare your structures as packed (there are compiler extensions to achieve this) to avoid having extra padding when combining multiple structures.
You can also set the size of the structure within the constructor.
struct RequestData
{
RequestData() : ioreq{}, attr{}
{
attr.hdr.attr_length = sizeof(attr);
}
s_ioctl_request ioreq;
s_attr_type2 attr;
};
concerning your second question, you could split the assignment in two, it's not too nice but it's easy and if you pass something without a correct header, it will lead to a compiler error:
template<typename Attr>
int do_ugly_ioctl(fd, int param1, int param2, Attr& attr){
struct RequestData {
s_ioctl_request ioreq;
Attr attr;
};
RequestData r;
r.ioreq.param1 = param1;
r.ioreq.param2 = param2;
s_attr_header hdr = Attr.hdr; //this will lead to compilation error if the type is not what we expect
(void) hdr;
r.attr = attr;
r.attr.hdr.attr_length = sizeof(attr); // Might as well enforce this here.
ioctl(fd, SOME_IOCTL_ID, (void*) &r);
}

Related

What is the intended usage of DEVICE_INT_ATTR?

I would like to expose some settings of my device via sysfs. If I understand it right, a driver can have multiple devices, so there should be one instance of the settings variable per device. This should be easy enough using DEVICE_ATTR macro.
Checking the sources I noticed there is also DEVICE_INT_ATTR and other with different type. I wonder what is the intended usage, as they use device_show_int functions that get pointer to device, but don't actually use it:
ssize_t device_store_int(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct dev_ext_attribute *ea = to_ext_attr(attr);
int ret;
long new;
ret = kstrtol(buf, 0, &new);
if (ret)
return ret;
if (new > INT_MAX || new < INT_MIN)
return -EINVAL;
*(int *)(ea->var) = new;
/* Always return full write size even if we didn't consume all */
return size;
}
EXPORT_SYMBOL_GPL(device_store_int);
I searched kernel sources for those macros, and it seems that they work with a global variable. For example DEVICE_INT_ATTR is used in drivers/base/core.c for mca_cfg.tolerant:
static DEVICE_INT_ATTR(tolerant, 0644, mca_cfg.tolerant);
but the mca_cfg varaible is actually global, not tied to a device:
struct mca_config mca_cfg __read_mostly = {
.bootlog = -1,
/* ... */
.tolerant = 1,
.monarch_timeout = -1
};
which makes it look like a driver (not device) attribute.
I also checked commit that adds these macros but it did not help me much.
You correctly take that DEVICE_INT_ATTR and other macros from that family are for "global" attributes, which store and show methods doesn't use dev parameter.
If you want to define attribute, which can be bound to several devices, then you could write your own store and show methods which gets information about the value from dev.
E.g. by having device
struct my_device
{
struct device base;
int repetition;
};
you could expose its repetition field in the attribute using following show method:
// Shows value of 'repetition' field of my_device.
static ssize_t repetition_show(struct device *dev, struct device_attribute *attr, char *buf)
{
// Obtain pointer to the real device structure.
struct my_device* my_dev = container_of(dev, struct my_device, base);
return sprintf(buf, "%d\n", my_dev->repetition);
}
Structure of such attribute could be initialized using __ATTR macro:
static struct device_attribute repetition_attr =
__ATTR(repetition, S_IRUGO, repetition_show, NULL);
Making "generic" attributes
Assume your device struct contains many int fields, which you want to expose via attributes:
struct my_device
{
struct device base;
int repetition;
int counter;
int value;
};
In that case you could generalize attribute definition, so you don't need to create many show (and store) functions.
E.g. you could store offset of the exposed field in your attribute structure:
struct device_bounded_attr
{
struct device_attribute base_attr;
size_t field_offset;
};
// Initializer for struct device_bounded_attr
//
// - real_device_type - type of the actual device structure
// - device_member - member of type 'struct device' in the actual device structure
// - field_member - member in actual device structure which you want to expose as attribute.
#define BOUNDED_ATTR(name, mode, show, store, real_device_type, device_member, field_member) { \
.base_attr = __ATTR(name, mode, show, store), \
.field_offset = offsetof(real_device_type, field_member) - offsetof(real_device_type, device_member)
}
Using this field, you could rewrite show method as follows:
// Shows value of integer field, stored in device.
static ssize_t bounded_attr_show(struct device *dev, struct device_attribute *attr, char *buf)
{
// Obtain pointer to the real attribute structure.
struct device_bounded_attr* bounded_attr = container_of(attr, struct device_bounded_attr, base_attr);
// Having offset of the field, calculate pointer to it
int field_ptr* = (int*)(((char*)dev) + bounded_attr->field_offset);
return sprintf(buf, "%d\n", *field_ptr);
}
So attributes can be declared as follows:
static struct device_bounded_attr repetition_attr =
BOUNDED_ATTR(repetition, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, repetition);
static struct device_bounded_attr counter_attr =
BOUNDED_ATTR(counter, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, counter);
static struct device_bounded_attr value_attr =
BOUNDED_ATTR(counter, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, value);

Inline functions, macros and other solutions to allocate on caller's stack

I have a lot of code that looks like this:
int bufferSize = fooBufferSize(); // hate having to do this; this logic should be in `foo`
char buffer[bufferSize];
foo(buffer);
bar(buffer);
It happens all the time for me. In the wild, I see something similar a lot:
int bufferSize = snprintf(NULL, 0, format, ...); // exact same issue as above
char buffer[bufferSize+1];
sprintf(buffer, format, ...);
Besides the fact that the above are tedious for the user to write, they also probably redo a lot of computations, which is not only inefficient, but it isn't DRY. I know that I could just malloc the buffer within foo, but there's a lot of issues with that: memory fragmentation, remembering to call free, overhead of malloc/free.
char *foo() {
char *buffer = malloc(...);
// process the data in the buffer
return buffer;
}
main() {
char *buffer = foo();
bar(buffer);
}
There are a lot of cases where I probably would use malloc (or a buffer pool) for things that are constantly removed and deleted (e.g. projectile objects in a game). However, the case that comes up a lot for me is that I want to allocate an object on the stack and then dispose of it when my function returns. The issue is that the object is generally allocated in a stack frame further down from the stack frame I want the object to live in. I'd prefer if I could just build on-top of the stack that foo uses. Like, what if I did this instead:
void foo(char **bufferPtr) {
char buffer[...];
// process buffer
*bufferPtr = buffer;
jmp __builtin_return_address(0); // pseudocode to jump to return address
}
main() {
char *buffer = NULL;
foo(&buffer);
bar(buffer);
}
I don't even know the exact syntax to make this approach work, but it is both GCC-specific and extremely hacky. In addition, if the jump isn't understood by the compiler, local variable states might not be restored properly. I guess, what I really want is for foo to behave like a macro, e.g. like this:
#define foo(buffer) \
char buffer[4]; \
strcpy(buffer, "hey");
int main() {
foo(buffer)
bar(buffer);
}
However, I really don't like using macros (terrible error messages, bad IDE support, slippery slope, etc.)
The macro above looks nice, but in my current usecase, I'm building a computation graph (similar to TensorFlow), and some of the node constructors would look really awkward using macros.
typedef struct {
float *data; // buffer to store output data of computation
int order; // number of dimensions
int *dimensions; // e.g. [3,4] for a 3x4 matrix
} Node;
typedef struct {
Node super; // it's still a node, so just pass a ref to this whenever you need a Node*
Node *A;
Node *B;
} MatMulNode;
void printMatrix(const char *name, Node *node) {
assert(node->order == 2);
printf("%s: [%d x %d]\n", name, node->dimensions[0], node->dimensions[1]);
}
// look at all these backslashes
// also, `return -1` might not make sense in the context this macro is used.
#define matmul(node, left, right) \
if (left.order != 2 || right.order != 2) {\
return -1;\
}\
if (left.dimensions[1] != right.dimensions[0]) {\
return -1;\
}\
int dimensions[2];\
dimensions[0] = left.dimensions[0];\
dimensions[1] = right.dimensions[1];\
float data[dimensions[0] * dimensions[1]];\
MatMulNode node = {\
.super = {\
.data = data,\
.order = 2,\
.dimensions = dimensions,\
},\
.A = &left,\
.B = &right,\
};
int main() {
Node A = {
NULL,
2,
(int[]) {1, 2}
};
Node B = {
NULL,
2,
(int[]) {2, 3}
};
matmul(C, A, B);
printMatrix("A", &A);
printMatrix("B", &B);
printMatrix("C", &C.super);
}
I'm honestly surprised by how well this works, but I also hate the fact that I have to use macros for it and refuse to believe that this is the best API I can make which avoids malloc.
I tried using inline functions, but inline is just a suggestion unless I'm using the always_inline attribute (but that's GCC only), and AFAICT, it doesn't seem to work with alloca. I'm not even sure if the below code has defined behavior, or if I'm just getting lucky:
static inline __attribute__((always_inline)) Node *matmul(Node *left, Node *right) {
if (left->order != 2 || right->order != 2) {
return NULL;
}
if (left->dimensions[1] != right->dimensions[0]) {
return NULL;
}
int dimensions[2];
dimensions[0] = left->dimensions[0];
dimensions[1] = right->dimensions[1];
float data[dimensions[0] * dimensions[1]];
MatMulNode node = {
.super = {
.data = data,
.order = 2,
.dimensions = dimensions,
},
.A = left,
.B = right,
};
return &node.super;
}
The final approach I know of is to use continuation-passing style, e.g.
void matmul(Node *A, Node *B, void callback(void *, Node *C), void *context) {
MatMulNode C = ...;
callback(context, &C.super);
}
The obvious tail-call optimization of this approach is nice, and the fact that the stack is obviously preserved would make it much less hacky than things like jmp, but the API that it presents to the user is really ugly. For example, what if I want to do matmul(A, matmul(B, C))? The code I'd have to write is extremely counter-intuitive, especially because I have to pass in a context variable to the callbacks, when they should Ideally just have access to the entire stack and choose whatever variables they need from there.
void callbackABC(void *context, Node *ABC) {
assert(ABC->order == 2);
printf("A(BC) is [%d x %d]\n", ABC->dimensions[0], ABC->dimensions[1]);
}
void callbackBC(void *context, Node *BC) {
Node *A = context;
matmul(A, BC, callbackABC, NULL);
}
int main() {
Node A = {
NULL,
2,
(int[]) {1, 2}
};
Node B = {
NULL,
2,
(int[]) {2, 3}
};
Node C = {
NULL,
2,
(int[]) {3, 4}
};
matmul(&B, &C, callbackBC, &A);
}
Overall, I think that inline functions are the closest thing to what I want, but rather than ask "How do I force a function to always inline?" I figured I'd ask with the full context of what I want to achieve and why none of my solutions work.
I'm not recommending it, but also you may want to take a look at the __attribute__ cleanup(freefunc) feature in gcc. Basically handles cleanup when the associated variable leaves scope.

How to pass a local struct as parameter to pthread_create?

The following function doesn't work. pin_thread_function sometimes receive garbage instead of the struct data. What is wrong? I know that is some basic scope related problem, but I can't explain.
typedef void (*state_callback_t)(int state);
struct pin_thread_params
{
char pin[3 + 1];
state_callback_t callback_onchange;
};
extern int pin_monitor_create(const char * pin,
state_callback_t callback_onchange)
{
int ret;
unsigned long int thread;
struct pin_thread_params params;
//Setup struct
strcpy(params.pin, "123");
params.callback_onchange = callback_onchange;
//Create thread with struc as parameter
ret = pthread_create(&thread, NULL, pin_thread_function, &params);
if (!ret)
{
ret = pthread_detach(thread);
}
return ret;
}
static void * pin_thread_function(void * context)
{
struct pin_thread_params params;
memcpy(&params, context, sizeof(params));
//Sometimes the correct string, most time garbage
printf("Started monitoring %s", params.pin);
}
When params is malloc'ed before pthread_create, everything works fine, like this:
...
struct pin_thread_params * params;
//Setup struct with malloc
params = malloc(sizeof(struct pin_thread_params));
strcpy(params->pin, "123");
params->callback_onchange = callback_onchange;
...
struct pin_thread_params params is statically allocated and the address of it is not safe to use once the scope of it is over (i.e. after pin_monitor_create has returned). It may happen that sometimes the thread execution starts before the pin_monitor_create has exited and you see the valid data in params. However, the dynamically allocated memory is from heap and will be usable until you free it, so you always get valid data in params within pin_thread_function .
I'm not particularly knowledgeable about pthreads (can't just comment quite yet), but you are passing a pointer to stack allocated memory to the thread which will eventually be clobbered by proceeding function calls.

1 struct with 2 different definitions (or dynamic structs)

I'm implementing a little program in C which uses a shared library called "libhelper.so".
The "libhelper.so" defines a struct in it's h-file, but sadly depending on the target-system those definitions are different (libhelper.so is always provided by the system and not by myself):
System A:
struct theStruct {
int fd;
unsigned int flags;
struct config config; // only in System A
int foo; // in both systems
int bar; // only in System A
};
System B:
struct theStruct {
int fd;
unsigned int flags;
int foo; // in both systems
int foobar; // only in system B
};
In my program, I thought I just define that struct by myself like this:
struct theStruct {
int fd;
unsigned int flags;
struct config config; // only in System A
int foo; // in both systems
int foobar; // only in system B
int bar; // only in System A
};
As the result of a call to a function in "libhelper.so" I got an instance of "theStruct" and now I though I can just check if "theStructInstance->bar" or "theStructInstance->foobar" is filled with a valid value to detect which implementation has been used by the library.
But it seems that I get just values like 1...6 which looks like the position of the field in the struct.
Has anyone an idea how I can do this?
No, this won't work.
First of all, all the definitions of the structure must be the same, otherwise you get the infamous Undefined Behaviour.
Second, look at the memory layout. What should be the offset to bar from the beginning of the structure? The first and the third definitions don't agree on this (the fields are most likely to be located consecutively).
Maybe you can try a union?
struct theStruct {
int fd;
unsigned int flags;
struct config config;
int foo; // in both systems
union {
int bar; // only in System A
int foobar; // only in system B
};
};
If you choose this, you should use only bar on System A and only foobar on System B.
If the two systems are incompatible, and the actual type needed for bar is not available on System B (and vice versa), you can do with the following code:
struct theStruct {
int fd;
unsigned int flags;
struct config config;
int foo; // in both systems
#ifdef SYSTEM_A
int bar; // only in System A
#else
#ifdef SYSTEM_B
int foobar; // only in system B
#else
#pragma error(either SYSTEM_A or SYSTEM_B must be enabled)
#endif
#endif
};
This way you will be always working with either with code compiled for System A or for System B, so you'll need to have different executables (which seems to be unavoidable anyway if you are compiling for the systems so much different).
You'll need to wrap parts of your code accessing the fields into #ifdefs:
#ifdef SYSTEM_A
s.bar = 5;
#endif
-- otherwise you'll get compile errors on System B.
Another possible solution would be to write platform dependent code to handle each of the individual structs, and then load their data into a common struct. This would then allow you to deal with the same members of the struct, no matter the code path, rather than always referencing one of the two union members based on the system:
struct mystruct;
mystruct.member1 = theStruct.member1; //the common part of the struct
mystruct.member2 = theStruct.member2;
#ifdef platform1
mystruct.member3 = theStruct.p1member; //specific to platform1
mystruct.member4 = -1;
#else
mystruct.member3 = -1;
mystruct.member4 = theStruct.p2member; //specific to platform2
#endif
Here is an approach to think about.
A bit of background about the assumptions I am making first.
It sounds like you have some function libraryFunction () that returns a pointer to a struct theStruct. However the actual layout of the struct theStruct depends on the particular system on which your application is running. In this struct is some information that you need to access. You do not specify the calling arguments or signature of the library function and if the pointer to the struct theStruct is returned as a function value or a pointer to a pointer is part of the argument list. I will assume it is a function return value.
Create a struct you define for the information that you want. Create two files each of which has a function that takes a void pointer and a pointer to your new struct and then fills in your struct with the data you want from the library supplied struct. Each of these two files will be compiled with a particular system target specified (SystemA or SystemB) so that your conversion function will interpret the struct provided by the library function depending on the target system and fill in your struct with the data you want.
file 1 for System A
// copy of the struct used in System A which is in the library header file
// put here for reference only as should be in the header file
struct theStruct {
int fd;
unsigned int flags;
struct config config; // only in System A
int foo; // in both systems
int bar; // only in System A
};
// my struct that contains the data from struct theStruct that I want
// would be in a header file included into each of these files but here for reference
struct myConvertStruct {
int foo;
};
void convert2SystemA (void *structPtr, struct *myStruct)
{
myStruct->foo = ((struct theStruct *)structPtr)->foo;
}
file 2 for System B
// copy of the struct used in System B which is in the library header file
// put here for reference only as should be in the header file
struct theStruct {
int fd;
unsigned int flags;
int foo; // in both systems
int foobar; // only in system B
};
// my struct that contains the data from struct theStruct that I want
// would be in a header file included into each of these files but here for reference
struct myConvertStruct {
int foo;
};
void convert2SystemB (void *structPtr, struct *myStruct)
{
myStruct->foo = ((struct theStruct *)structPtr)->foo;
}
file 3 using the conversion functions
// my struct that contains the data from struct theStruct that I want
// would be in a header file included into each of these files but here for reference
struct myConvertStruct {
int foo;
};
{
struct myConvertStruct myStruct;
// some function body and now we come to the library call
if (mySystem == SystemA) {
void *pStruct = libraryFunction (......);
convert2SystemA (pStruct, &myStruct);
} else if (mySystem == SystemB) {
void *pStruct = libraryFunction (......);
convert2SystemB (pStruct, &myStruct);
} else {
// some error conditions
}
// now use the data that you have pulled as you want to use it
}
The reason why your proposal won't work is that the offset to the foo member is different for System A and System B. You say that you can only figure out what System you are using at runtime. So, when System B sets foo, it will likely end up setting something inside of config instead.
enum system { SystemUnknown, SystemA, SystemB };
struct theStructSystemA {
int fd;
unsigned int flags;
struct config config; // only in System A
int foo; // in both systems
int bar; // only in System A
};
struct theStructSystemB {
int fd;
unsigned int flags;
int foo;
int foobar;
};
struct myStruct {
union {
struct theStructSystemA a;
struct theStructSystemB b;
} u;
enum system sys;
};
struct myStruct s = { 0 };
Now, you can set bar to some invalid value: s.u.a.bar = -1, for example. Now, when you call into your library, you can check:
s.u.a.bar = -1;
some_libhelper_call((void *)&s);
if (s.u.a.bar != -1) s.sys = SystemA;
else s.sys = SystemB;
So now, after s.sys is known, you can switch to a different code path that deals entirely with a known system version.

Object-orientation in C

What would be a set of nifty preprocessor hacks (ANSI C89/ISO C90 compatible) which enable some kind of ugly (but usable) object-orientation in C?
I am familiar with a few different object-oriented languages, so please don't respond with answers like "Learn C++!". I have read "Object-Oriented Programming With ANSI C" (beware: PDF format) and several other interesting solutions, but I'm mostly interested in yours :-)!
See also Can you write object oriented code in C?
I would advise against preprocessor (ab)use to try and make C syntax more like that of another more object-oriented language. At the most basic level, you just use plain structs as objects and pass them around by pointers:
struct monkey
{
float age;
bool is_male;
int happiness;
};
void monkey_dance(struct monkey *monkey)
{
/* do a little dance */
}
To get things like inheritance and polymorphism, you have to work a little harder. You can do manual inheritance by having the first member of a structure be an instance of the superclass, and then you can cast around pointers to base and derived classes freely:
struct base
{
/* base class members */
};
struct derived
{
struct base super;
/* derived class members */
};
struct derived d;
struct base *base_ptr = (struct base *)&d; // upcast
struct derived *derived_ptr = (struct derived *)base_ptr; // downcast
To get polymorphism (i.e. virtual functions), you use function pointers, and optionally function pointer tables, also known as virtual tables or vtables:
struct base;
struct base_vtable
{
void (*dance)(struct base *);
void (*jump)(struct base *, int how_high);
};
struct base
{
struct base_vtable *vtable;
/* base members */
};
void base_dance(struct base *b)
{
b->vtable->dance(b);
}
void base_jump(struct base *b, int how_high)
{
b->vtable->jump(b, how_high);
}
struct derived1
{
struct base super;
/* derived1 members */
};
void derived1_dance(struct derived1 *d)
{
/* implementation of derived1's dance function */
}
void derived1_jump(struct derived1 *d, int how_high)
{
/* implementation of derived 1's jump function */
}
/* global vtable for derived1 */
struct base_vtable derived1_vtable =
{
&derived1_dance, /* you might get a warning here about incompatible pointer types */
&derived1_jump /* you can ignore it, or perform a cast to get rid of it */
};
void derived1_init(struct derived1 *d)
{
d->super.vtable = &derived1_vtable;
/* init base members d->super.foo */
/* init derived1 members d->foo */
}
struct derived2
{
struct base super;
/* derived2 members */
};
void derived2_dance(struct derived2 *d)
{
/* implementation of derived2's dance function */
}
void derived2_jump(struct derived2 *d, int how_high)
{
/* implementation of derived2's jump function */
}
struct base_vtable derived2_vtable =
{
&derived2_dance,
&derived2_jump
};
void derived2_init(struct derived2 *d)
{
d->super.vtable = &derived2_vtable;
/* init base members d->super.foo */
/* init derived1 members d->foo */
}
int main(void)
{
/* OK! We're done with our declarations, now we can finally do some
polymorphism in C */
struct derived1 d1;
derived1_init(&d1);
struct derived2 d2;
derived2_init(&d2);
struct base *b1_ptr = (struct base *)&d1;
struct base *b2_ptr = (struct base *)&d2;
base_dance(b1_ptr); /* calls derived1_dance */
base_dance(b2_ptr); /* calls derived2_dance */
base_jump(b1_ptr, 42); /* calls derived1_jump */
base_jump(b2_ptr, 42); /* calls derived2_jump */
return 0;
}
And that's how you do polymorphism in C. It ain't pretty, but it does the job. There are some sticky issues involving pointer casts between base and derived classes, which are safe as long as the base class is the first member of the derived class. Multiple inheritance is much harder - in that case, in order to case between base classes other than the first, you need to manually adjust your pointers based on the proper offsets, which is really tricky and error-prone.
Another (tricky) thing you can do is change the dynamic type of an object at runtime! You just reassign it a new vtable pointer. You can even selectively change some of the virtual functions while keeping others, creating new hybrid types. Just be careful to create a new vtable instead of modifying the global vtable, otherwise you'll accidentally affect all objects of a given type.
I once worked with a C library that was implemented in a way that struck me as quite elegant. They had written, in C, a way to define objects, then inherit from them so that they were as extensible as a C++ object. The basic idea was this:
Each object had its own file
Public functions and variables are defined in the .h file for an object
Private variables and functions were only located in the .c file
To "inherit" a new struct is created with the first member of the struct being the object to inherit from
Inheriting is difficult to describe, but basically it was this:
struct vehicle {
int power;
int weight;
}
Then in another file:
struct van {
struct vehicle base;
int cubic_size;
}
Then you could have a van created in memory, and being used by code that only knew about vehicles:
struct van my_van;
struct vehicle *something = &my_van;
vehicle_function( something );
It worked beautifully, and the .h files defined exactly what you should be able to do with each object.
C Object System (COS) sounds promising (it's still in alpha version). It tries to keep minimal the available concepts for the sake of simplicity and flexibility: uniform object oriented programming including open classes, metaclasses, property metaclasses, generics, multimethods, delegation, ownership, exceptions, contracts and closures. There is a draft paper (PDF) that describes it.
Exception in C is a C89 implementation of the TRY-CATCH-FINALLY found in other OO languages. It comes with a testsuite and some examples.
Both by Laurent Deniau, which is working a lot on OOP in C.
The GNOME desktop for Linux is written in object-oriented C, and it has an object model called "GObject" which supports properties, inheritance, polymorphism, as well as some other goodies like references, event handling (called "signals"), runtime typing, private data, etc.
It includes preprocessor hacks to do things like typecasting around in the class hierarchy, etc. Here's an example class I wrote for GNOME (things like gchar are typedefs):
Class Source
Class Header
Inside the GObject structure there's a GType integer which is used as a magic number for GLib's dynamic typing system (you can cast the entire struct to a "GType" to find it's type).
Slightly off-topic, but the original C++ compiler, Cfront, compiled C++ to C and then to assembler.
Preserved here.
If you think of methods called on objects as static methods that pass an implicit 'this' into the function it can make thinking OO in C easier.
For example:
String s = "hi";
System.out.println(s.length());
becomes:
string s = "hi";
printf(length(s)); // pass in s, as an implicit this
Or something like that.
I used to do this kind of thing in C, before I knew what OOP was.
Following is an example, which implements a data-buffer which grows on demand, given a minimum size, increment and maximum size. This particular implementation was "element" based, which is to say it was designed to allow a list-like collection of any C type, not just a variable length byte-buffer.
The idea is that the object is instantiated using the xxx_crt() and deleted using xxx_dlt(). Each of the "member" methods takes a specifically typed pointer to operate on.
I implemented a linked list, cyclic buffer, and a number of other things in this manner.
I must confess, I have never given any thought on how to implement inheritance with this approach. I imagine that some blend of that offered by Kieveli might be a good path.
dtb.c:
#include <limits.h>
#include <string.h>
#include <stdlib.h>
static void dtb_xlt(void *dst, const void *src, vint len, const byte *tbl);
DTABUF *dtb_crt(vint minsiz,vint incsiz,vint maxsiz) {
DTABUF *dbp;
if(!minsiz) { return NULL; }
if(!incsiz) { incsiz=minsiz; }
if(!maxsiz || maxsiz<minsiz) { maxsiz=minsiz; }
if(minsiz+incsiz>maxsiz) { incsiz=maxsiz-minsiz; }
if((dbp=(DTABUF*)malloc(sizeof(*dbp))) == NULL) { return NULL; }
memset(dbp,0,sizeof(*dbp));
dbp->min=minsiz;
dbp->inc=incsiz;
dbp->max=maxsiz;
dbp->siz=minsiz;
dbp->cur=0;
if((dbp->dta=(byte*)malloc((vuns)minsiz)) == NULL) { free(dbp); return NULL; }
return dbp;
}
DTABUF *dtb_dlt(DTABUF *dbp) {
if(dbp) {
free(dbp->dta);
free(dbp);
}
return NULL;
}
vint dtb_adddta(DTABUF *dbp,const byte *xlt256,const void *dtaptr,vint dtalen) {
if(!dbp) { errno=EINVAL; return -1; }
if(dtalen==-1) { dtalen=(vint)strlen((byte*)dtaptr); }
if((dbp->cur + dtalen) > dbp->siz) {
void *newdta;
vint newsiz;
if((dbp->siz+dbp->inc)>=(dbp->cur+dtalen)) { newsiz=dbp->siz+dbp->inc; }
else { newsiz=dbp->cur+dtalen; }
if(newsiz>dbp->max) { errno=ETRUNC; return -1; }
if((newdta=realloc(dbp->dta,(vuns)newsiz))==NULL) { return -1; }
dbp->dta=newdta; dbp->siz=newsiz;
}
if(dtalen) {
if(xlt256) { dtb_xlt(((byte*)dbp->dta+dbp->cur),dtaptr,dtalen,xlt256); }
else { memcpy(((byte*)dbp->dta+dbp->cur),dtaptr,(vuns)dtalen); }
dbp->cur+=dtalen;
}
return 0;
}
static void dtb_xlt(void *dst,const void *src,vint len,const byte *tbl) {
byte *sp,*dp;
for(sp=(byte*)src,dp=(byte*)dst; len; len--,sp++,dp++) { *dp=tbl[*sp]; }
}
vint dtb_addtxt(DTABUF *dbp,const byte *xlt256,const byte *format,...) {
byte textÝ501¨;
va_list ap;
vint len;
va_start(ap,format); len=sprintf_len(format,ap)-1; va_end(ap);
if(len<0 || len>=sizeof(text)) { sprintf_safe(text,sizeof(text),"STRTOOLNG: %s",format); len=(int)strlen(text); }
else { va_start(ap,format); vsprintf(text,format,ap); va_end(ap); }
return dtb_adddta(dbp,xlt256,text,len);
}
vint dtb_rmvdta(DTABUF *dbp,vint len) {
if(!dbp) { errno=EINVAL; return -1; }
if(len > dbp->cur) { len=dbp->cur; }
dbp->cur-=len;
return 0;
}
vint dtb_reset(DTABUF *dbp) {
if(!dbp) { errno=EINVAL; return -1; }
dbp->cur=0;
if(dbp->siz > dbp->min) {
byte *newdta;
if((newdta=(byte*)realloc(dbp->dta,(vuns)dbp->min))==NULL) {
free(dbp->dta); dbp->dta=null; dbp->siz=0;
return -1;
}
dbp->dta=newdta; dbp->siz=dbp->min;
}
return 0;
}
void *dtb_elmptr(DTABUF *dbp,vint elmidx,vint elmlen) {
if(!elmlen || (elmidx*elmlen)>=dbp->cur) { return NULL; }
return ((byte*)dbp->dta+(elmidx*elmlen));
}
dtb.h
typedef _Packed struct {
vint min; /* initial size */
vint inc; /* increment size */
vint max; /* maximum size */
vint siz; /* current size */
vint cur; /* current data length */
void *dta; /* data pointer */
} DTABUF;
#define dtb_dtaptr(mDBP) (mDBP->dta)
#define dtb_dtalen(mDBP) (mDBP->cur)
DTABUF *dtb_crt(vint minsiz,vint incsiz,vint maxsiz);
DTABUF *dtb_dlt(DTABUF *dbp);
vint dtb_adddta(DTABUF *dbp,const byte *xlt256,const void *dtaptr,vint dtalen);
vint dtb_addtxt(DTABUF *dbp,const byte *xlt256,const byte *format,...);
vint dtb_rmvdta(DTABUF *dbp,vint len);
vint dtb_reset(DTABUF *dbp);
void *dtb_elmptr(DTABUF *dbp,vint elmidx,vint elmlen);
PS: vint was simply a typedef of int - I used it to remind me that it's length was variable from platform to platform (for porting).
I think what Adam Rosenfield posted is the correct way of doing OOP in C. I'd like to add that what he shows is the implementation of the object. In other words the actual implementation would be put in the .c file, while the interface would be put in the header .h file. For example, using the monkey example above:
The interface would look like:
//monkey.h
struct _monkey;
typedef struct _monkey monkey;
//memory management
monkey * monkey_new();
int monkey_delete(monkey *thisobj);
//methods
void monkey_dance(monkey *thisobj);
You can see in the interface .h file you are only defining prototypes. You can then compile the implementation part " .c file" into a static or dynamic library. This creates encapsulation and also you can change the implementation at will. The user of your object needs to know almost nothing about the implementation of it. This also places focus on the overall design of the object.
It's my personal belief that oop is a way of conceptualizing your code structure and reusability and has really nothing to do with those other things that are added to c++ like overloading or templates. Yes those are very nice useful features but they are not representative of what object oriented programming really is.
ffmpeg (a toolkit for video processing) is written in straight C (and assembly language), but using an object-oriented style. It's full of structs with function pointers. There are a set of factory functions that initialize the structs with the appropriate "method" pointers.
If you really thinks catefully, even standard C library use OOP - consider FILE * as an example: fopen() initializes an FILE * object, and you use it use member methods fscanf(), fprintf(), fread(), fwrite() and others, and eventually finalize it with fclose().
You can also go with the pseudo-Objective-C way which is not difficult as well:
typedef void *Class;
typedef struct __class_Foo
{
Class isa;
int ivar;
} Foo;
typedef struct __meta_Foo
{
Foo *(*alloc)(void);
Foo *(*init)(Foo *self);
int (*ivar)(Foo *self);
void (*setIvar)(Foo *self);
} meta_Foo;
meta_Foo *class_Foo;
void __meta_Foo_init(void) __attribute__((constructor));
void __meta_Foo_init(void)
{
class_Foo = malloc(sizeof(meta_Foo));
if (class_Foo)
{
class_Foo = {__imp_Foo_alloc, __imp_Foo_init, __imp_Foo_ivar, __imp_Foo_setIvar};
}
}
Foo *__imp_Foo_alloc(void)
{
Foo *foo = malloc(sizeof(Foo));
if (foo)
{
memset(foo, 0, sizeof(Foo));
foo->isa = class_Foo;
}
return foo;
}
Foo *__imp_Foo_init(Foo *self)
{
if (self)
{
self->ivar = 42;
}
return self;
}
// ...
To use:
int main(void)
{
Foo *foo = (class_Foo->init)((class_Foo->alloc)());
printf("%d\n", (foo->isa->ivar)(foo)); // 42
foo->isa->setIvar(foo, 60);
printf("%d\n", (foo->isa->ivar)(foo)); // 60
free(foo);
}
This is what may be resulted from some Objective-C code like this, if a pretty-old Objective-C-to-C translator is used:
#interface Foo : NSObject
{
int ivar;
}
- (int)ivar;
- (void)setIvar:(int)ivar;
#end
#implementation Foo
- (id)init
{
if (self = [super init])
{
ivar = 42;
}
return self;
}
#end
int main(void)
{
Foo *foo = [[Foo alloc] init];
printf("%d\n", [foo ivar]);
[foo setIvar:60];
printf("%d\n", [foo ivar]);
[foo release];
}
My recommendation: keep it simple. One of the biggest issues I have is maintaining older software (sometimes over 10 years old). If the code is not simple, it can be difficult. Yes, one can write very useful OOP with polymorphism in C, but it can be difficult to read.
I prefer simple objects that encapsulate some well-defined functionality. A great example of this is GLIB2, for example a hash table:
GHastTable* my_hash = g_hash_table_new(g_str_hash, g_str_equal);
int size = g_hash_table_size(my_hash);
...
g_hash_table_remove(my_hash, some_key);
The keys are:
Simple architecture and design pattern
Achieves basic OOP encapsulation.
Easy to implement, read, understand, and maintain
I'm a bit late to the party here but I like to avoid both macro extremes - too many or too much obfuscates code, but a couple obvious macros can make the OOP code easier to develop and read:
/*
* OOP in C
*
* gcc -o oop oop.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct obj2d {
float x; // object center x
float y; // object center y
float (* area)(void *);
};
#define X(obj) (obj)->b1.x
#define Y(obj) (obj)->b1.y
#define AREA(obj) (obj)->b1.area(obj)
void *
_new_obj2d(int size, void * areafn)
{
struct obj2d * x = calloc(1, size);
x->area = areafn;
// obj2d constructor code ...
return x;
}
// --------------------------------------------------------
struct rectangle {
struct obj2d b1; // base class
float width;
float height;
float rotation;
};
#define WIDTH(obj) (obj)->width
#define HEIGHT(obj) (obj)->height
float rectangle_area(struct rectangle * self)
{
return self->width * self->height;
}
#define NEW_rectangle() _new_obj2d(sizeof(struct rectangle), rectangle_area)
// --------------------------------------------------------
struct triangle {
struct obj2d b1;
// deliberately unfinished to test error messages
};
#define NEW_triangle() _new_obj2d(sizeof(struct triangle), triangle_area)
// --------------------------------------------------------
struct circle {
struct obj2d b1;
float radius;
};
#define RADIUS(obj) (obj)->radius
float circle_area(struct circle * self)
{
return M_PI * self->radius * self->radius;
}
#define NEW_circle() _new_obj2d(sizeof(struct circle), circle_area)
// --------------------------------------------------------
#define NEW(objname) (struct objname *) NEW_##objname()
int
main(int ac, char * av[])
{
struct rectangle * obj1 = NEW(rectangle);
struct circle * obj2 = NEW(circle);
X(obj1) = 1;
Y(obj1) = 1;
// your decision as to which of these is clearer, but note above that
// macros also hide the fact that a member is in the base class
WIDTH(obj1) = 2;
obj1->height = 3;
printf("obj1 position (%f,%f) area %f\n", X(obj1), Y(obj1), AREA(obj1));
X(obj2) = 10;
Y(obj2) = 10;
RADIUS(obj2) = 1.5;
printf("obj2 position (%f,%f) area %f\n", X(obj2), Y(obj2), AREA(obj2));
// WIDTH(obj2) = 2; // error: struct circle has no member named width
// struct triangle * obj3 = NEW(triangle); // error: triangle_area undefined
}
I think this has a good balance, and the errors it generates (at least with default gcc 6.3 options) for some of the more likely mistakes are helpful instead of confusing. The whole point is to improve programmer productivity no?
#include "triangle.h"
#include "rectangle.h"
#include "polygon.h"
#include <stdio.h>
int main()
{
Triangle tr1= CTriangle->new();
Rectangle rc1= CRectangle->new();
tr1->width= rc1->width= 3.2;
tr1->height= rc1->height= 4.1;
CPolygon->printArea((Polygon)tr1);
printf("\n");
CPolygon->printArea((Polygon)rc1);
}
Output:
6.56
13.12
Here is a show of what is OO programming with C.
This is real, pure C, no preprocessor macros. We have inheritance,
polymorphism and data encapsulation (including data private to classes or objects).
There is no chance for protected qualifier equivalent, that is,
private data is private down the innheritance chain too.
But this is not an inconvenience because I don't think it is necessary.
CPolygon is not instantiated because we only use it to manipulate objects
of down the innheritance chain that have common aspects but different
implementation of them (Polymorphism).
If I were going to write OOP in C I would probably go with a pseudo-Pimpl design. Instead of passing pointers to structs, you end up passing pointers to pointers to structs. This makes the content opaque and facilitates polymorphism and inheritance.
The real problem with OOP in C is what happens when variables exit scope. There are no compiler-generated destructors and that can cause issues. Macros can possibly help, but it is always going to be ugly to look at.
I'm also working on this based on a macro solution. So it is for the bravest only, I guess ;-) But it is quite nice already, and I'm already working on a few projects on top of it.
It works so that you first define a separate header file for each class. Like this:
#define CLASS Point
#define BUILD_JSON
#define Point__define \
METHOD(Point,public,int,move_up,(int steps)) \
METHOD(Point,public,void,draw) \
\
VAR(read,int,x,JSON(json_int)) \
VAR(read,int,y,JSON(json_int)) \
To implement the class, you create a header file for it and a C file where you implement the methods:
METHOD(Point,public,void,draw)
{
printf("point at %d,%d\n", self->x, self->y);
}
In the header you created for the class, you include other headers you need and define types etc. related to the class. In both the class header and in the C file you include the class specification file (see the first code example) and an X-macro. These X-macros (1,2,3 etc.) will expand the code to the actual class structs and other declarations.
To inherit a class, #define SUPER supername and add supername__define \ as the first line in the class definition. Both must be there. There is also JSON support, signals, abstract classes, etc.
To create an object, just use W_NEW(classname, .x=1, .y=2,...). The initialization is based on struct initialization introduced in C11. It works nicely and everything not listed is set to zero.
To call a method, use W_CALL(o,method)(1,2,3). It looks like a higher order function call but it is just a macro. It expands to ((o)->klass->method(o,1,2,3)) which is a really nice hack.
See Documentation and the code itself.
Since the framework needs some boilerplate code, I wrote a Perl script (wobject) that does the job. If you use that, you can just write
class Point
public int move_up(int steps)
public void draw()
read int x
read int y
and it will create the class specification file, class header, and a C file, which includes Point_impl.c where you implement the class. It saves quite a lot of work, if you have many simple classes but still everything is in C. wobject is a very simple regular expression based scanner which is easy to adapt to specific needs, or to be rewritten from scratch.
Another way to program in an object oriented style with C is to use a code generator which transforms a domain specific language to C. As it's done with TypeScript and JavaScript to bring OOP to js.
I'd suggest you to try out COOP
It features Classes, Inheritance, Exceptions, Memory management, its own Unit Testing Framework for C, and more.
All of this while maintaining type safety and (many parts of the) intellisence!
And, yes, it uses Macro magics to do it.
#Adam Rosenfield has a very good explanation of how to achieve OOP with C
Besides, I would recommend you to read
1) pjsip
A very good C library for VoIP. You can learn how it achieves OOP though structs and function pointer tables
2) iOS Runtime
Learn how iOS Runtime powers Objective C. It achieves OOP through isa pointer, meta class
For me object orientation in C should have these features:
Encapsulation and data hiding (can be achieved using structs/opaque pointers)
Inheritance and support for polymorphism (single inheritance can be achieved using structs - make sure the abstract base is not instantiable)
Constructor and destructor functionality (not easy to achieve)
Type checking (at least for user-defined types as C doesn't enforce any)
Reference counting (or something to implement RAII)
Limited support for exception handling (setjmp and longjmp)
On top of the above it should rely on ANSI/ISO specifications and should not rely on compiler-specific functionality.
Look at http://ldeniau.web.cern.ch/ldeniau/html/oopc/oopc.html. If nothing else reading through the documentation is an enlightening experience.
If you need to write a little code
try this: https://github.com/fulminati/class-framework
#include "class-framework.h"
CLASS (People) {
int age;
};
int main()
{
People *p = NEW (People);
p->age = 10;
printf("%d\n", p->age);
}
The open-source Dynace project does exactly that. It's at https://github.com/blakemcbride/Dynace
I have managed to implement inheritance and polymorphism in C.
I can do single inheritance with virtual tables and I can implement multiple interfaces with a technique where the struct that implements an interface simply creates the interface struct by giving it its own methods and a pointer to itself. The interface struct then calls these methods and, among other parameters, it passes them the pointer to the struct which created the implementation of the interface.
When it comes to inheriting non abstract classes, I have achieved that with virtual tables, I have already explained inheritance with virtual tables in this answer. The code from that answer doesn't allow implementation of multiple interfaces. In this answer however, I changed my code so that it allows implementation of multiple interfaces. Here is the entire code that I posted on github. I will post the code here as well but maybe it is more readable on github, as I put the code in multiple files.
Here is the code, I have structs Zivotinja, Pas, Automobil and the struct MozeProizvestiZvuk. This last struct is an interface. Pas and Automobil implement it. Struct Pas also inherits from Zivotinja.
Here is the code for the main function
Pas *pas = Pas_new_sve(4, 20, "some dog name");
MozeProizvestiZvuk *mozeProizvestiZvuk = pas->getMozeProizvestiZvuk(pas);
mozeProizvestiZvuk->proizvediZvuk(mozeProizvestiZvuk->strukturaKojuMetodeInterfejsaKoriste);
mozeProizvestiZvuk->proizvediZvuk(mozeProizvestiZvuk->strukturaKojuMetodeInterfejsaKoriste);
printf("number of times it made noise = %d\n", mozeProizvestiZvuk->getKolikoPutaJeProizveoZvuk(mozeProizvestiZvuk->strukturaKojuMetodeInterfejsaKoriste));
Automobil *automobil = Automobil_new("Sandero", 2009);
MozeProizvestiZvuk *zvukAutomobil = automobil->getMozeProizvestiZvuk(automobil);
for(int i=0; i<3; i++){
zvukAutomobil->proizvediZvuk(zvukAutomobil->strukturaKojuMetodeInterfejsaKoriste);
}
printf("number of times it made noise = %d\n", zvukAutomobil->getKolikoPutaJeProizveoZvuk(zvukAutomobil->strukturaKojuMetodeInterfejsaKoriste));
Zivotinja *zivotinja = Zivotinja_new(10);
zivotinja->vTable->ispisiPodatkeOZivotinji(zivotinja);
zivotinja->vTable->obrisi(&zivotinja);
Zivotinja *pasKaoZivotinja = Pas_new_sve(5, 50, "Milojko");
pasKaoZivotinja->vTable->ispisiPodatkeOZivotinji(pasKaoZivotinja);
int godine = pasKaoZivotinja->vTable->dajGodine(pasKaoZivotinja);
printf("age of the dog which was upcasted to an animal = %d \n", godine);
pasKaoZivotinja->vTable->obrisi(&pasKaoZivotinja);
Here is the MozeProizvestiZvuk.h file
#ifndef MOZE_PROIZVESTI_ZVUK_H
#define MOZE_PROIZVESTI_ZVUK_H
typedef struct MozeProizvestiZvukStruct{
void (*proizvediZvuk)(void *strukturaKojuMetodeInterfejsaKoriste);
unsigned int (*getKolikoPutaJeProizveoZvuk)(void *strukturaKojaImplementiraInterfejs);
void *strukturaKojuMetodeInterfejsaKoriste;
}MozeProizvestiZvuk;
#endif
Here is the Automobil struct which implements this interface.
#include"MozeProizvestiZvuk.h"
#include<stdlib.h>
typedef struct AutomobilStruct{
const char *naziv;
int godinaProizvodnje;
unsigned int kolikoPutaJeProizveoZvuk;
MozeProizvestiZvuk* (*getMozeProizvestiZvuk)(struct AutomobilStruct *_this);
}Automobil;
MozeProizvestiZvuk* Automobil_getMozeProizvestiZvuk(Automobil *automobil);
Automobil* Automobil_new(const char* naziv, int godiste){
Automobil *automobil = (Automobil*) malloc(sizeof(Automobil));
automobil->naziv = naziv;
automobil->godinaProizvodnje = godiste;
automobil->kolikoPutaJeProizveoZvuk = 0;
automobil->getMozeProizvestiZvuk = Automobil_getMozeProizvestiZvuk;
return automobil;
}
void Automobil_delete(Automobil **adresaAutomobilPointera){
free(*adresaAutomobilPointera);
*adresaAutomobilPointera = NULL;
}
unsigned int Automobil_getKolikoJeZvukovaProizveo(Automobil *automobil){
return automobil->kolikoPutaJeProizveoZvuk;
}
void Automobil_proizvediZvuk(Automobil *automobil){
printf("Automobil koji se zove %s, godiste %d proizvodi zvuk. \n", automobil->naziv, automobil->godinaProizvodnje);
automobil->kolikoPutaJeProizveoZvuk++;
}
MozeProizvestiZvuk* Automobil_getMozeProizvestiZvuk(Automobil *automobil){
MozeProizvestiZvuk *mozeProizvestiZvuk = (MozeProizvestiZvuk*) malloc(sizeof(MozeProizvestiZvuk));
mozeProizvestiZvuk->strukturaKojuMetodeInterfejsaKoriste = automobil;
mozeProizvestiZvuk->proizvediZvuk = Automobil_proizvediZvuk;
mozeProizvestiZvuk->getKolikoPutaJeProizveoZvuk = Automobil_getKolikoJeZvukovaProizveo;
return mozeProizvestiZvuk;
}
Here is the Zivotinja struct, this struct doesn't inherit from anything, neither does it implement any interfaces, but the struct Pas will inherit from Zivotinja.
#include<stdio.h>
#include<stdlib.h>
typedef struct ZivotinjaVTableStruct{
void (*ispisiPodatkeOZivotinji)(void *zivotinja);
int (*dajGodine) (void *zivotinja);
} ZivotinjaVTable;
typedef struct ZivotinjaStruct{
ZivotinjaVTable *vTable;
int godine;
} Zivotinja;
void ispisiPodatkeOOvojZivotinji(Zivotinja* zivotinja){
printf("Ova zivotinja ima %d godina. \n", zivotinja->godine);
}
int dajGodineOveZivotinje(Zivotinja *z){
return z->godine;
}
void Zivotinja_obrisi(Zivotinja **adresaPointeraKaZivotinji){
Zivotinja *zivotinjaZaBrisanje = *adresaPointeraKaZivotinji;
free(zivotinjaZaBrisanje);
*adresaPointeraKaZivotinji = NULL;
}
struct ZivotinjaVTableStruct zivotinjaVTableGlobal = {Zivotinja_obrisi, ispisiPodatkeOOvojZivotinji, dajGodineOveZivotinje};
Zivotinja* Zivotinja_new(int godine){
ZivotinjaVTable *vTable = &zivotinjaVTableGlobal;
Zivotinja *z = (Zivotinja*) malloc(sizeof(Zivotinja));
z->vTable = vTable;
z->godine = godine;
}
And finally, here is the struct Pas which inherits from Zivotinja and implements MozeProizvestiZvuk interface.
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include"Zivotinja.h"
#include"MozeProizvestiZvuk.h"
typedef struct PasVTableStruct{
bool (*obrisi)(void **Pas);
void (*ispisiPodatkeOZivotinji)(void *Pas);
int (*dajGodine) (void *Pas);
bool (*daLiJeVlasnikStariji) (void *Pas);
} PasVTable;
typedef struct PasStruct{
PasVTable *vTable;
int godine;
const char* vlasnik;
int godineVlasnika;
unsigned int kolikoPutaJeProizveoZvuk;
MozeProizvestiZvuk* (*getMozeProizvestiZvuk)(struct PasStruct *_this);
} Pas;
MozeProizvestiZvuk* Pas_getMozeProizvestiZvuk(Pas *_this);
void ispisiPodatkeOPsu(void *pasVoid){
Pas *pas = (Pas*)pasVoid;
printf("Pas ima %d godina, vlasnik se zove %s, vlasnik ima %d godina. \n", pas->godine, pas->vlasnik, pas->godineVlasnika);
}
int dajGodinePsa(void *pasVoid){
Pas *pas = (Pas*) pasVoid;
return pas->godine;
}
bool daLiJeVlasnikStariji(Pas *pas){
return pas->godineVlasnika >= pas->godine;
}
void Pas_obrisi(Pas **adresaPointeraPsa){
Pas *pasZaBrisanje = *adresaPointeraPsa;
free(pasZaBrisanje);
*adresaPointeraPsa = NULL;
}
struct PasVTableStruct pasVTableGlobal = {
Pas_obrisi,
ispisiPodatkeOPsu,
dajGodinePsa,
daLiJeVlasnikStariji
};
Pas* Pas_new(int godine){
Pas *z = (Pas*) malloc(sizeof(Pas));
z->godine = godine;
z->kolikoPutaJeProizveoZvuk = 0;
z->vTable = (&pasVTableGlobal);
z->getMozeProizvestiZvuk = Pas_getMozeProizvestiZvuk;
return z;
}
Pas *Pas_new_sve(int godine, int godineVlasnika, char* imeVlasnika){
Pas *pas = (Pas*) malloc(sizeof(Pas));
pas->kolikoPutaJeProizveoZvuk = 0;
pas->godine = godine;
pas->godineVlasnika = godineVlasnika;
pas->vlasnik = imeVlasnika;
pas->vTable = &pasVTableGlobal;
pas->getMozeProizvestiZvuk = Pas_getMozeProizvestiZvuk;
return pas;
}
unsigned int Pas_getBrojZvukova(Pas *_this){
return _this->kolikoPutaJeProizveoZvuk;
}
void Pas_proizvediZvuk(Pas *_this){
printf("Pas godina %d, vlasnika %s je proizveo zvuk.\n", _this->godine, _this->vlasnik);
_this->kolikoPutaJeProizveoZvuk++;
}
MozeProizvestiZvuk* Pas_getMozeProizvestiZvuk(Pas *_this){
MozeProizvestiZvuk *mozeProizvestiZvuk = (MozeProizvestiZvuk*) malloc(sizeof(MozeProizvestiZvuk));
mozeProizvestiZvuk->getKolikoPutaJeProizveoZvuk = Pas_getBrojZvukova;
mozeProizvestiZvuk->proizvediZvuk = Pas_proizvediZvuk;
mozeProizvestiZvuk->strukturaKojuMetodeInterfejsaKoriste = _this;
return mozeProizvestiZvuk;
}

Resources