Can I use getters and setters in pure C instead of using extern variables?
First of all, don't listen to anyone saying "there is no object-orientation in language x" because they have truly not understood that OO is a program design method, completely apart from language syntax.
Some languages have elegant ways to implement OO, some have not. Yet it is possible to write an object-oriented program in any language, for example in C. Similarly, your program will not automagically get a proper OO design just because you wrote it in Java, or because you used certain language keywords.
The way you implement private encapsulation in C is a bit more crude than in languages with OO support, but it does like this:
// module.h
void set_x (int n);
int get_x (void);
// module.c
static int x; // private variable
void set_x (int n)
{
x = n;
}
int get_x (void)
{
return x;
}
// main.c
#include "module.h"
int main (void)
{
set_x(5);
printf("%d", get_x());
}
Can call it "class" or "ADT" or "code module" as you prefer.
This is how every reasonable C program out there is written. And has been written for the past 30-40 years or so, as long as program design has existed. If you say there are no setters/getters in a C program, then that is because you have no experience of using C.
Yes, it's very much possible and sometimes even useful. C supports opaque types:
struct Context;
C code compiled with only this declaration in scope can not access any hypothetical members of the struct, and can't use value of type Context either. But it can still handle pointers to Context values, so functions like these are possible:
Context *make_context(...);
int context_get_foo(Context *);
void context_set_foo(Context *, int);
This pattern insulates the client C code from any changes to the size or internal layout of Context. Note that this is a stronger guarantee than simply declaring but not documenting the members: Even if the programmers duly ignore the undocumented members, by-value use of the struct is permitted (and will certainly slip in), and now the code has to be recompiled when the size changes. In other words, opaque types only handled through pointers give greater ABI stability.
Another approach is by using a global variable and inline functions:
// module.h
inline void set_x (int n) {extern int x; x = n;}
inline int get_x (void) {extern int x; return x;}
// module.c
int x; // global variable
// main.c
#include "module.h"
int main (void)
{
set_x(5);
printf("%d", get_x());
}
It has two advantages:
Getters and setters become easily inlineable
It becomes clear to the compiler that getters have no side effects, which allows further optimizations and produces no warnings in cases like this one:
// warning: compound statement with side effects
if(get_x() || get_y())
Of course, a "dedicated" (read: dumb) programmer can always write extern int x; in their code and use the variable directly. On the other hand, a "dedicated" programmer can also easily remove the static keyword and use it anyway...
Related
Files: A(main), B
I have learned that B's function can't be use in A without
funcntion definitions.
But my code ran normally with A, B files without function definitions
This is my code:
B.c
void a()
{
printf("hi");
}
A.c
#include <stdio.h>
void main()
{
a();
}
What is it? I'm confused.
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
addendum...
sorry for my bad question.
my code works well with error.
but i couldn't see error.
but i have more question for difference between
'void a();'
'extern void a();'
Once upon a time, C did not require prior declaration of all functions. Many compilers still let you get away with this.
In file A.c, when you called
a();
where a was a function the compiler has never seen before, the compiler assumed that the declaration
extern int a();
was in scope. That is, the compiler assumed that a was a function taking unspecified arguments and returning int.
Or, that used to be the rule. That rule is no longer in C, so yes, you are supposed to explicitly declare all your functions before you call them. Most of today's compilers will warn you when they apply the old rule, and many aren't willing to apply the rule at all, or at least, not unless you use a non-default option flag requesting them to. But it sounds like your compiler is still willing to apply the rule without warning or error. That's great if you're compiling a bunch of very old code, but it's not so great if you're trying to learn modern C.
Now, in this case you have the additional problem that the actual definition of function a in file B.c defines it as returning void, not int, so theoretically that's wrong, too. But, in practice, the error of misdeclaring (or mis-calling) void- versus int-returning functions is an innocuous one, that doesn't cause any real problems. (It's still wrong, though, and worth avoiding.)
I think you know this, but a correct setup would either be to have file A.c look like this:
#include <stdio.h>
extern void a(void);
int main()
{
a();
}
or else to create the file B.h containing
extern void a(void);
and then to have file A.c look like this:
#include <stdio.h>
#include "B.h"
int main()
{
a();
}
(Note that I have also changed void main() to int main(), for correctness. If you're using an old compiler, as it sounds like you are, you may also have to add the line return 0; at the end of main().)
Addendum. You had also asked about that extern keyword. It has to do with the distinction between declarations and definitions. But this distinction plays out slightly differently for functions, versus global variables.
Declarations explain what type something has. Definitions explain what type something has, and they additionally allocate memory for the something, and supply its initial value.
These are declarations:
extern int i;
int f(int);
extern int f2(int, double);
These are definitions:
int i;
int i2 = 2;
int f(int x) { return 2 * x; }
int f2(int n, double x) { return n * x; }
The keyword extern explicitly says, "This is a declaration, the definition is somewhere else." For global variables, this makes a big difference. But for functions, when you say int f(int);, the compiler can tell, when it finds a , instead of a {, that this is a declaration (not a definition), so the keyword extern is optional in function declarations.
(Also, functions are always global in C; there are no local functions.)
See also section 4.2 and section 4.3 of these course notes.
For me it's a rule to define and declare static functions inside source files, I mean .c files.
However in very rare situations I saw people declaring it in the header file.
Since static functions have internal linkage we need to define it in every file we include the header file where the function is declared. This looks pretty odd and far from what we usually want when declaring something as static.
On the other hand if someone naive tries to use that function without defining it the compiler will complaint. So in some sense is not really unsafe to do this even sounding strange.
My questions are:
What is the problem of declaring static functions in header files?
What are the risks?
What the impact in compilation time?
Is there any risk in runtime?
First I'd like to clarify my understanding of the situation you describe: The header contains (only) a static function declaration while the C file contains the definition, i.e. the function's source code. For example
some.h:
static void f();
// potentially more declarations
some.c:
#include "some.h"
static void f() { printf("Hello world\n"); }
// more code, some of it potentially using f()
If this is the situation you describe, I take issue with your remark
Since static functions have internal linkage we need to define it in every file we include the header file where the function is declared.
If you declare the function but do not use it in a given translation unit, I don't think you have to define it. gcc accepts that with a warning; the standard does not seem to forbid it, unless I missed something. This may be important in your scenario because translation units which do not use the function but include the header with its declaration don't have to provide an unused definition.
Now let's examine the questions:
What is the problem of declaring static functions in header files?
It is somewhat unusual. Typically, static functions are functions needed in only one file. They are declared static to make that explicit by limiting their visibility. Declaring them in a header therefore is somewhat antithetical. If the function is indeed used in multiple files with identical definitions it should be made external, with a single definition. If only one translation unit actually uses it, the declaration does not belong in a header.
One possible scenario therefore is to ensure a uniform function signature for different implementations in the respective translation units. The common header leads to a compile time error for different return types in C (and C++); different parameter types would cause a compile time error only in C (but not in C++' because of function overloading).
What are the risks?
I do not see risks in your scenario. (As opposed to also including the function definition in a header which may violate the encapsulation principle.)
What the impact in compilation time?
A function declaration is small and its complexity is low, so the overhead of having additional function declarations in a header is likely negligible. But if you create and include an additional header for the declaration in many translation units the file handling overhead can be significant (i.e. the compiler idles a lot while it waits for the header I/O)
Is there any risk in runtime? I cannot see any.
This is not an answer to the stated questions, but hopefully shows why one might implement a static (or static inline) function in a header file.
I can personally only think of two good reasons to declare some functions static in a header file:
If the header file completely implements an interface that should only be visible in the current compilation unit
This is extremely rare, but might be useful in e.g. an educational context, at some point during the development of some example library; or perhaps when interfacing to another programming language with minimal code.
A developer might choose to do so if the library or interaface implementation is trivial and nearly so, and ease of use (to the developer using the header file) is more important than code size. In these cases, the declarations in the header file often use preprocessor macros, allowing the same header file to be included more than once, providing some sort of crude polymorphism in C.
Here is a practical example: Shoot-yourself-in-the-foot playground for linear congruential pseudorandom number generators. Because the implementation is local to the compilation unit, each compilation unit will get their own copies of the PRNG. This example also shows how crude polymorphism can be implemented in C.
prng32.h:
#if defined(PRNG_NAME) && defined(PRNG_MULTIPLIER) && defined(PRNG_CONSTANT) && defined(PRNG_MODULUS)
#define MERGE3_(a,b,c) a ## b ## c
#define MERGE3(a,b,c) MERGE3_(a,b,c)
#define NAME(name) MERGE3(PRNG_NAME, _, name)
static uint32_t NAME(state) = 0U;
static uint32_t NAME(next)(void)
{
NAME(state) = ((uint64_t)PRNG_MULTIPLIER * (uint64_t)NAME(state) + (uint64_t)PRNG_CONSTANT) % (uint64_t)PRNG_MODULUS;
return NAME(state);
}
#undef NAME
#undef MERGE3
#endif
#undef PRNG_NAME
#undef PRNG_MULTIPLIER
#undef PRNG_CONSTANT
#undef PRNG_MODULUS
An example using the above, example-prng32.h:
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#define PRNG_NAME glibc
#define PRNG_MULTIPLIER 1103515245UL
#define PRNG_CONSTANT 12345UL
#define PRNG_MODULUS 2147483647UL
#include "prng32.h"
/* provides glibc_state and glibc_next() */
#define PRNG_NAME borland
#define PRNG_MULTIPLIER 22695477UL
#define PRNG_CONSTANT 1UL
#define PRNG_MODULUS 2147483647UL
#include "prng32.h"
/* provides borland_state and borland_next() */
int main(void)
{
int i;
glibc_state = 1U;
printf("glibc lcg: Seed %u\n", (unsigned int)glibc_state);
for (i = 0; i < 10; i++)
printf("%u, ", (unsigned int)glibc_next());
printf("%u\n", (unsigned int)glibc_next());
borland_state = 1U;
printf("Borland lcg: Seed %u\n", (unsigned int)borland_state);
for (i = 0; i < 10; i++)
printf("%u, ", (unsigned int)borland_next());
printf("%u\n", (unsigned int)borland_next());
return EXIT_SUCCESS;
}
The reason for marking both the _state variable and the _next() function static is that this way each compilation unit that includes the header file has their own copy of the variables and the functions -- here, their own copy of the PRNG. Each must be separately seeded, of course; and if seeded to the same value, will yield the same sequence.
One should generally shy away from such polymorphism attempts in C, because it leads to complicated preprocessor macro shenanigans, making the implementation much harder to understand, maintain, and modify than necessary.
However, when exploring the parameter space of some algorithm -- like here, the types of 32-bit linear congruential generators, this lets us use a single implementation for each of the generators we examine, ensuring there are no implementation differences between them. Note that even this case is more like a development tool, and not something you ought to see in a implementation provided for others to use.
If the header implements simple static inline accessor functions
Preprocessor macros are commonly used to simplify code accessing complicated structure types. static inline functions are similar, except that they also provide type checking at compile time, and can refer to their parameters several times (with macros, that is problematic).
One practical use case is a simple interface for reading files using low-level POSIX.1 I/O (using <unistd.h> and <fcntl.h> instead of <stdio.h>). I've done this myself when reading very large (dozens of megabytes to gigabytes range) text files containing real numbers (with a custom float/double parser), as the GNU C standard I/O is not particularly fast.
For example, inbuffer.h:
#ifndef INBUFFER_H
#define INBUFFER_H
typedef struct {
unsigned char *head; /* Next buffered byte */
unsigned char *tail; /* Next byte to be buffered */
unsigned char *ends; /* data + size */
unsigned char *data;
size_t size;
int descriptor;
unsigned int status; /* Bit mask */
} inbuffer;
#define INBUFFER_INIT { NULL, NULL, NULL, NULL, 0, -1, 0 }
int inbuffer_open(inbuffer *, const char *);
int inbuffer_close(inbuffer *);
int inbuffer_skip_slow(inbuffer *, const size_t);
int inbuffer_getc_slow(inbuffer *);
static inline int inbuffer_skip(inbuffer *ib, const size_t n)
{
if (ib->head + n <= ib->tail) {
ib->head += n;
return 0;
} else
return inbuffer_skip_slow(ib, n);
}
static inline int inbuffer_getc(inbuffer *ib)
{
if (ib->head < ib->tail)
return *(ib->head++);
else
return inbuffer_getc_slow(ib);
}
#endif /* INBUFFER_H */
Note that the above inbuffer_skip() and inbuffer_getc() do not check if ib is non-NULL; this is typical for such functions. These accessor functions are assumed to be "in the fast path", i.e. called very often. In such cases, even the function call overhead matters (and is avoided with static inline functions, since they are duplicated in the code at the call site).
Trivial accessor functions, like the above inbuffer_skip() and inbuffer_getc(), may also let the compiler avoid the register moves involved in function calls, because functions expect their parameters to be located in specific registers or on the stack, whereas inlined functions can be adapted (wrt. register use) to the code surrounding the inlined function.
Personally, I do recommend writing a couple of test programs using the non-inlined functions first, and compare the performance and results to the inlined versions. Comparing the results ensure the inlined versions do not have bugs (off by one type is common here!), and comparing the performance and generated binaries (size, at least) tells you whether inlining is worth it in general.
Why would you want a both global and static function? In c, functions are global by default. You only use static functions if you want to limit the access to a function to the file they are declared. So you actively restrict access by declaring it static...
The only requirement for implementations in the header file, is for c++ template functions and template class member functions.
I'm experimenting with OOP in C, based off of this answer. I came across something I can't quite get around. Take this example:
struct foo {
int val;
int (*bar)(struct foo, int);
}
int foo_bar(struct foo mod, int val)
{
mod.val = val;
}
int main(void)
{
struct foo foo;
foo.bar = foo_bar;
foo.bar(foo, 8);
}
I think it would be much simpler and clearer if there was a way to use the this keyword in C:
struct foo {
int val;
int (*bar)(struct foo, int);
}
int foo_bar(int val)
{
this.val = val;
}
int main(void)
{
struct foo foo;
foo.bar = foo_bar;
foo.bar(8);
}
It sounds impossible, but there may be some workaround out there, a bit like OOP in C itself. Is there any way to achieve the functionality of the this keyword in Object-Oriented C?
No. this keyword in C++ is a reference to the object at hand, and is actually explicitly passed to the member functions at the ABI level. Explicitly passing a pointer to the object (as the first parameter) in functions is the best equivalent in C. Note that this means
struct foo {
int value;
int (*func)(struct foo *, int);
};
void foo_bar(struct foo *f, int value)
{
f->value = value;
}
i.e. the pointer to the object is passed as the first parameter, rather than the structure itself. This makes it explicit that the object is passed by reference, and makes understanding and maintaining such code easier.
It is not sane to expect features seen in one programming language to be valid in some other programming language, even if the two are related somehow.
You see, each programming language has their own approach to problem solving, their own paradigm. Because there is no universal best paradigm possible, problems are best solved using a programming language that has the most applicable/efficient/useful paradigm. For example, you don't write a C++ program to expedite common command-line tasks; you use a shell script or other simple scripting language instead.
As a programmer, having the ability to switch from one programming language paradigm to another means you have the ability to look at a problem from different viewpoints. Looking at current software projects, the most robust, vital, and efficient ones are written by programmers who have that ability.
This is why I stated, above, that it is not sane to expect the features or paradigm of one programming language to be portable to others. You should not do that, because it is equivalent to having a single tool, and looking at all problems as if your tool at hand is the only possible tool in solving them. (If all you have is a hammer, all problems start looking like nails.) Learning, and especially learning to accept, the different paradigms in different programming languages, makes for a better programmer, better problem-solver.
In order to have a clean code, using some OO concept can be useful, even in C.
I often write modules made of a pair of .h and .c files. The problem is that the user of the module have to be careful, since private members don't exist in C. The use of the pimpl idiom or abstract data types is ok, but it adds some code and/or files, and requires a heavier code. I hate using accessor when I don't need one.
Here is a idea which provides a way to make the compiler complain about invalid access to "private" members, with only a few extra code. The idea is to define twice the same structure, but with some extra 'const' added for the user of the module.
Of course, writing in "private" members is still possible with a cast. But the point is only to avoid mistakes from the user of the module, not to safely protect memory.
/*** 2DPoint.h module interface ***/
#ifndef H_2D_POINT
#define H_2D_POINT
/* 2D_POINT_IMPL need to be defined in implementation files before #include */
#ifdef 2D_POINT_IMPL
#define _cst_
#else
#define _cst_ const
#endif
typedef struct 2DPoint
{
/* public members: read and write for user */
int x;
/* private members: read only for user */
_cst_ int y;
} 2DPoint;
2DPoint *new_2dPoint(void);
void delete_2dPoint(2DPoint **pt);
void set_y(2DPoint *pt, int newVal);
/*** 2dPoint.c module implementation ***/
#define 2D_POINT_IMPL
#include "2dPoint.h"
#include <stdlib.h>
#include <string.h>
2DPoint *new_2dPoint(void)
{
2DPoint *pt = malloc(sizeof(2DPoint));
pt->x = 42;
pt->y = 666;
return pt;
}
void delete_2dPoint(2DPoint **pt)
{
free(*pt);
*pt = NULL;
}
void set_y(2DPoint *pt, int newVal)
{
pt->y = newVal;
}
#endif /* H_2D_POINT */
/*** main.c user's file ***/
#include "2dPoint.h"
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
2DPoint *pt = new_2dPoint();
pt->x = 10; /* ok */
pt->y = 20; /* Invalid access, y is "private" */
set_y(pt, 30); /* accessor needed */
printf("pt.x = %d, pt.y = %d\n", pt->x, pt->y); /* no accessor needed for reading "private" members */
delete_2dPoint(&pt);
return EXIT_SUCCESS;
}
And now, here is the question: is this trick OK with the C standard?
It works fine with GCC, and the compiler doesn't complain about anything, even with some strict flags, but how can I be sure that this is really OK?
This is almost certainly undefined behavior.
Writing/modifying an object declared as const is prohibited and doing so results in UB. Furthermore, the approach you take re-declares struct 2DPoint as two technically different types, which is also not permitted.
Note that this (as undefined behavior in general) does not mean that it "certainly won't work" or "it must crash". In fact, I find it quite logical that it works, because if one reads the source intelligently, he may easily find out what the purpose of it is and why it migh be regarded as correct. However, the compiler is not intelligent - at best, it's a finite automaton which has no knowledge about what the code is supposed to do; it only obeys (more or less) to the syntactical and semantical rules of the grammar.
This violates C 2011 6.2.7 1.
6.2.7 1 requires that two definitions of the same structure in different translation units have compatible type. It is not permitted to have const in one and not the other.
In one module, you may have a reference to one of these objects, and the members appear to be const to the compiler. When the compiler writes calls to functions in other modules, it may hold values from the const members in registers or other cache or in partially or fully evaluated expressions from later in the source code than the function call. Then, when the function modifies the member and returns, the original module will not have the changed value. Worse, it may use some combination of the changed value and the old value.
This is highly improper programming.
In Bjarne Stroustrup's words: C is not designed to support OOP, although it enables OOP, which means it is possible to write OOP programs in C, but only very hard to do so. As such, if you have to write OOP code in C, there seems nothing wrong with using this approach, but it is preferable to use a language better suited for the purpose.
By trying to write OOP code in C, you have already entered a territory where "common sense" has to be overridden, so this approach is fine as long as you take responsibility to use it properly. You also need to ensure that it is thoroughly and rigourously documented and everyone concerned with the code is aware of it.
Edit Oh, you may have to use a cast to get around the const. I fail to recall if the C-style cast can be used like C++ const_cast.
You can use different approach - declare two structs, one for user without private members (in header) and one with private members for internal use in your implementation unit. All private members should be placed after public ones.
You always pass around the pointer to the struct and cast it to internal-use when needed, like this:
/* user code */
struct foo {
int public;
};
int bar(void) {
struct foo *foo = new_foo();
foo->public = 10;
}
/* implementation */
struct foo_internal {
int public;
int private;
};
struct foo *new_foo(void) {
struct foo_internal *foo == malloc(sizeof(*foo));
foo->public = 1;
foo->private = 2;
return (struct foo*)foo; // to suppress warning
}
C11 allows unnamed structure fields (GCC supports it some time), so in case of using GCC (or C11 compliant compiler) you can declare internal structure as:
struct foo_internal {
struct foo;
int private;
};
therefore no extra effort required to keep structure definitions in sync.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
Assuming I have to use C (no C++ or object oriented compilers) and I don't have dynamic memory allocation, what are some techniques I can use to implement a class, or a good approximation of a class? Is it always a good idea to isolate the "class" to a separate file? Assume that we can preallocate the memory by assuming a fixed number of instances, or even defining the reference to each object as a constant before compile time. Feel free to make assumptions about which OOP concept I will need to implement (it will vary) and suggest the best method for each.
Restrictions:
I have to use C and not an OOP
because I'm writing code for an
embedded system, and the compiler and
preexisting code base is in C.
There is no dynamic memory allocation
because we don't have enough memory
to reasonably assume we won't run out
if we start dynamically allocating
it.
The compilers we work with have no problems with function pointers
That depends on the exact "object-oriented" feature-set you want to have. If you need stuff like overloading and/or virtual methods, you probably need to include function pointers in structures:
typedef struct {
float (*computeArea)(const ShapeClass *shape);
} ShapeClass;
float shape_computeArea(const ShapeClass *shape)
{
return shape->computeArea(shape);
}
This would let you implement a class, by "inheriting" the base class, and implementing a suitable function:
typedef struct {
ShapeClass shape;
float width, height;
} RectangleClass;
static float rectangle_computeArea(const ShapeClass *shape)
{
const RectangleClass *rect = (const RectangleClass *) shape;
return rect->width * rect->height;
}
This of course requires you to also implement a constructor, that makes sure the function pointer is properly set up. Normally you'd dynamically allocate memory for the instance, but you can let the caller do that, too:
void rectangle_new(RectangleClass *rect)
{
rect->width = rect->height = 0.f;
rect->shape.computeArea = rectangle_computeArea;
}
If you want several different constructors, you will have to "decorate" the function names, you can't have more than one rectangle_new() function:
void rectangle_new_with_lengths(RectangleClass *rect, float width, float height)
{
rectangle_new(rect);
rect->width = width;
rect->height = height;
}
Here's a basic example showing usage:
int main(void)
{
RectangleClass r1;
rectangle_new_with_lengths(&r1, 4.f, 5.f);
printf("rectangle r1's area is %f units square\n", shape_computeArea(&r1));
return 0;
}
I hope this gives you some ideas, at least. For a successful and rich object-oriented framework in C, look into glib's GObject library.
Also note that there's no explicit "class" being modelled above, each object has its own method pointers which is a bit more flexible than you'd typically find in C++. Also, it costs memory. You could get away from that by stuffing the method pointers in a class structure, and invent a way for each object instance to reference a class.
I had to do it once too for a homework. I followed this approach:
Define your data members in a
struct.
Define your function members that
take a pointer to your struct as
first argument.
Do these in one header & one c.
Header for struct definition &
function declarations, c for
implementations.
A simple example would be this:
/// Queue.h
struct Queue
{
/// members
}
typedef struct Queue Queue;
void push(Queue* q, int element);
void pop(Queue* q);
// etc.
///
If you only want one class, use an array of structs as the "objects" data and pass pointers to them to the "member" functions. You can use typedef struct _whatever Whatever before declaring struct _whatever to hide the implementation from client code. There's no difference between such an "object" and the C standard library FILE object.
If you want more than one class with inheritance and virtual functions, then it's common to have pointers to the functions as members of the struct, or a shared pointer to a table of virtual functions. The GObject library uses both this and the typedef trick, and is widely used.
There's also a book on techniques for this available online - Object Oriented Programming with ANSI C.
C Interfaces and Implementations: Techniques for Creating Reusable Software, David R. Hanson
http://www.informit.com/store/product.aspx?isbn=0201498413
This book does an excellent job of covering your question. It's in the Addison Wesley Professional Computing series.
The basic paradigm is something like this:
/* for data structure foo */
FOO *myfoo;
myfoo = foo_create(...);
foo_something(myfoo, ...);
myfoo = foo_append(myfoo, ...);
foo_delete(myfoo);
you can take a look at GOBject. it's an OS library that give you a verbose way to do an object.
http://library.gnome.org/devel/gobject/stable/
I will give a simple example of how OOP should be done in C. I realize this thread is from 2009 but would like to add this anyway.
/// Object.h
typedef struct Object {
uuid_t uuid;
} Object;
int Object_init(Object *self);
uuid_t Object_get_uuid(Object *self);
int Object_clean(Object *self);
/// Person.h
typedef struct Person {
Object obj;
char *name;
} Person;
int Person_init(Person *self, char *name);
int Person_greet(Person *self);
int Person_clean(Person *self);
/// Object.c
#include "object.h"
int Object_init(Object *self)
{
self->uuid = uuid_new();
return 0;
}
uuid_t Object_get_uuid(Object *self)
{ // Don't actually create getters in C...
return self->uuid;
}
int Object_clean(Object *self)
{
uuid_free(self->uuid);
return 0;
}
/// Person.c
#include "person.h"
int Person_init(Person *self, char *name)
{
Object_init(&self->obj); // Or just Object_init(&self);
self->name = strdup(name);
return 0;
}
int Person_greet(Person *self)
{
printf("Hello, %s", self->name);
return 0;
}
int Person_clean(Person *self)
{
free(self->name);
Object_clean(self);
return 0;
}
/// main.c
int main(void)
{
Person p;
Person_init(&p, "John");
Person_greet(&p);
Object_get_uuid(&p); // Inherited function
Person_clean(&p);
return 0;
}
The basic concept involves placing the 'inherited class' at the top of the struct. This way, accessing the first 4 bytes in the struct also accesses the first 4 bytes in the 'inherited class' (assuming non-crazy optimizations). Now, when the pointer of the struct is cast to the 'inherited class', the 'inherited class' can access the 'inherited values' in the same way it would access its members normally.
This and some naming conventions for constructors, destructors, allocation, and deallocation functions (I recommend _init, _clean, _new, and _free) will get you a long way.
As for Virtual functions, use function pointers in the struct, possibly with Class_func(...); wrapper too.
As for (simple) templates, add a size_t parameter to determine size, require a void* pointer, or require a 'class' type with just the functionality you care about. (e.g. int GetUUID(Object *self); GetUUID(&p);)
Use a struct to simulate the data members of a class. In terms of method scope you can simulate private methods by placing the private function prototypes in the .c file and the public functions in the .h file.
GTK is built entirely on C and it uses many OOP concepts. I have read through the source code of GTK and it is pretty impressive, and definitely easier to read. The basic concept is that each "class" is simply a struct, and associated static functions. The static functions all accept the "instance" struct as a parameter, do whatever then need, and return results if necessary. For Example, you may have a function "GetPosition(CircleStruct obj)". The function would simply dig through the struct, extract the position numbers, probably build a new PositionStruct object, stick the x and y in the new PositionStruct, and return it. GTK even implements inheritance this way by embedding structs inside structs. pretty clever.
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <uchar.h>
/**
* Define Shape class
*/
typedef struct Shape Shape;
struct Shape {
/**
* Variables header...
*/
double width, height;
/**
* Functions header...
*/
double (*area)(Shape *shape);
};
/**
* Functions
*/
double calc(Shape *shape) {
return shape->width * shape->height;
}
/**
* Constructor
*/
Shape _Shape() {
Shape s;
s.width = 1;
s.height = 1;
s.area = calc;
return s;
}
/********************************************/
int main() {
Shape s1 = _Shape();
s1.width = 5.35;
s1.height = 12.5462;
printf("Hello World\n\n");
printf("User.width = %f\n", s1.width);
printf("User.height = %f\n", s1.height);
printf("User.area = %f\n\n", s1.area(&s1));
printf("Made with \xe2\x99\xa5 \n");
return 0;
};
In your case the good approximation of the class could be the an ADT. But still it won't be the same.
My strategy is:
Define all code for the class in a separate file
Define all interfaces for the class in a separate header file
All member functions take a "ClassHandle" which stands in for the instance name (instead of o.foo(), call foo(oHandle)
The constructor is replaced with a function void ClassInit(ClassHandle h, int x, int y,...) OR ClassHandle ClassInit(int x, int y,...) depending on the memory allocation strategy
All member variables are store as a member of a static struct in the class file, encapsulating it in the file, preventing outside files from accessing it
The objects are stored in an array of the static struct above, with predefined handles (visible in the interface) or a fixed limit of objects that can be instantiated
If useful, the class can contain public functions that will loop through the array and call the functions of all the instantiated objects (RunAll() calls each Run(oHandle)
A Deinit(ClassHandle h) function frees the allocated memory (array index) in the dynamic allocation strategy
Does anyone see any problems, holes, potential pitfalls or hidden benefits/drawbacks to either variation of this approach? If I am reinventing a design method (and I assume I must be), can you point me to the name of it?
Also see this answer and this one
It is possible. It always seems like a good idea at the time but afterwards it becomes a maintenance nightmare. Your code become littered with pieces of code tying everything together. A new programmer will have lots of problems reading and understanding the code if you use function pointers since it will not be obvious what functions is called.
Data hiding with get/set functions is easy to implement in C but stop there. I have seen multiple attempts at this in the embedded environment and in the end it is always a maintenance problem.
Since you all ready have maintenance issues I would steer clear.
My approach would be to move the struct and all primarily-associated functions to a separate source file(s) so that it can be used "portably".
Depending on your compiler, you might be able to include functions into the struct, but that's a very compiler-specific extension, and has nothing to do with the last version of the standard I routinely used :)
The first c++ compiler actually was a preprocessor which translated the C++ code into C.
So it's very possible to have classes in C.
You might try and dig up an old C++ preprocessor and see what kind of solutions it creates.
Do you want virtual methods?
If not then you just define a set of function pointers in the struct itself. If you assign all the function pointers to standard C functions then you will be able to call functions from C in very similar syntax to how you would under C++.
If you want to have virtual methods it gets more complicated. Basically you will need to implement your own VTable to each struct and assign function pointers to the VTable depending on which function is called. You would then need a set of function pointers in the struct itself that in turn call the function pointer in the VTable. This is, essentially, what C++ does.
TBH though ... if you want the latter then you are probably better off just finding a C++ compiler you can use and re-compiling the project. I have never understood the obsession with C++ not being usable in embedded. I've used it many a time and it works is fast and doesn't have memory problems. Sure you have to be a bit more careful about what you do but its really not that complicated.
C isn't an OOP language, as your rightly point out, so there's no built-in way to write a true class. You're best bet is to look at structs, and function pointers, these will let you build an approximation of a class. However, as C is procedural you might want to consider writing more C-like code (i.e. without trying to use classes).
Also, if you can use C, you can probally use C++ and get classes.