Rudimentary OOP in C - c

Experimenting with primitive OOP ideas in C.
main.c:
#include <stdio.h>
#include <stdlib.h>
#include "reptile.h"
int main()
{
const char *name = "Spot";
turtle_t *t = maketurtle(name);
t->hide(t); // <---- "Error: dereferencing pointer to incomplete type"
return 0;
}
reptile.h:
#ifndef REPTILE_H
#define REPTILE_H
typedef struct turtle_t turtle_t;
turtle_t* maketurtle(const char *name);
void hide(turtle_t *self);
#endif // REPTILE_H
reptile.c:
#include <stdio.h>
#include <stdlib.h>
#include "reptile.h"
typedef struct turtle_t
{
int numoflegs;
const char name[25];
void (*hide)(turtle_t *self);
} turtle_t;
turtle_t* maketurtle(const char *name)
{
turtle_t *t = (turtle_t*)malloc(sizeof(turtle_t));
t->name = name;
return t;
}
void hide(turtle_t *self)
{
printf("The turtle %s has withdrawn into his shell!", self->name);
}
Is there something I am missing? I have looked at a similar case here on stack overflow and my code looks identical at least in structure, so I am a bit confused. Thanks in advance!
p.s. if this is a linker error how do I get it to compile in an IDE without throwing an error?

When the compiler works on the main.c file, it knows that there is a structure named turtle_t but it knows nothing about it, it's not completely defined.
You need to make the structure "public", or at least the parts that are supposed to be public. This can easily be done by using two structures, one for the public "methods" and member variables, and another nested that contains the private data. Something like
typedef struct turtle_private_t turtle_private_t;
typedef struct turtle_t turtle_t;
struct turtle_t
{
turtle_private_t *private; // For private data
void (*hide)(turtle_t *self);
};
As an alternative, and one that is common, is that you don't place public functions in the structure, but instead use normal functions, with a special prefix to their name to indicate class. Something like
turtle_t *turtle_create(void); // Creates the turtle
void turtle_hide(turtle_t *); // Hides the turtle

You need to move your
typedef struct turtle_t
{
int numoflegs;
const char name[25];
void (*hide)(turtle_t *self);
} turtle_t;
from the .c file to the .h file. Incomplete type means, that the type is not known at compile type (it will be only known at link-time, if it's contained in a different translation unit). That means in your main.c turtle_t is only forward-declared and the structure itself is unknown - moving it to your .h file shall do the trick.

Related

pointer to struct in function prototype before defining struct

I am trying to create a struct that I will use in a function via pointers. The issue is that I do not want to use global variables therefore I can't use a pointer to a struct as a parameter for the function prototype if I try to define the struct in main file, since it has not been defined yet.
How would I go about doing this? What I think the solution is, is to define the struct in a header file, then create local variables of that type in the main file. Is this the right way to go about this? Would appreciate some info about what i'm actually doing here if this is correct.
Sorry if I did anything wrong when posting, Its my first time.
Example of what I am thinking the solution is
Main.h
#include <stdio.h>
typedef struct Vehicle{
int a;
char b;
};
function(Vehicle *p);
Main.c
#include "Main.h"
Vehicle Car1;
Vehicle *p=&Car1;
function(p);
The proper syntax for a typedef is
typedef T-IDENTIFIER IDENTIFIER-LIST;
wherein the comma separated identifiers listed in IDENTIFIER-LIST become aliases for T-IDENTIFIER. A lot of the time IDENTIFIER-LIST will consist of a single identifier.
For example, in
typedef int integer, number;
integer and number are now type aliases for int.
When it comes to using typedef with structs, the form
typedef struct foo { /* ... */ } foo_type;
is more or less shorthand for
typedef struct foo foo_type;
struct foo { /* ... */ };
but does allow you to typedef an anonymous struct
typedef struct { /* ... */ } foo_type;
With all that said, in your code you have omitted the IDENTIFIER-LIST from your typedef.
If main.c really does consist entirely of the code you've posted, it will not compile. Every C program needs an entry point, and in a hosted environment that is the function main with the signature int main(void) or int main(int argc, char **argv).
While you can declare variables outside of functions (i.e., globals), you can not call functions from outside of functions. Everything starts from main.
A working example program:
main.h:
typedef struct {
int a;
char b;
} Vehicle;
void function(Vehicle *p);
main.c:
#include <stdio.h>
#include "main.h"
int main(void) {
Vehicle car = { 51, 'A' };
function(&car);
}
void function(Vehicle *v) {
printf("Vehicle: a: %d, b: %c\n", v->a, v->b);
}
I can't use the struct as a parameter for the function prototype
You misunderstood something.
Your typedef is rather useless.
You of course can use pointers to structs as function parameters and in the function prototypes.
typedef struct {
int a;
char b;
} Vehicle;
int foo(Vehicle *); // prototype
You can't call function not outside other functions (as it is shown in the main.c

Abstracting over type implementation in C

I'm new to C programming and trying to write a simple example. Percisely I tried to abstract over a type implementation and simply use typedef and specify operations I can do with this type. I understand that at that point the type is incomplete, but I was intended to complete it into c-file, not header. Here is it:
test.h
#ifndef _TEST_H
#define _TEST_H
typedef my_type_t;
void init(my_type_t **t);
#endif //_TEST_H
test.c
#include <stdlib.h>
#include "test.h"
// implementation details
struct my_type_t{ //<---- completening my_type_t to be a struct with 1 field
int field;
};
void init(struct my_type_t **t){ //<--- error: conflicting type for init
*t = malloc(sizeof(struct my_type_t));
(*t) -> field = 42;
}
Is something like this possible? I wanted the implementation completely hide all the details about the actual type definition exposing only operations that can be done with it.
UPD: If we rewrite the c-file as follows:
#include <stdlib.h>
#include "test.h"
struct internal_my_type_definition_t{
int field;
};
void init(my_type_t **t){
struct internal_my_type_definition_t *st = malloc(sizeof(struct internal_my_type_definition_t));
st -> field = 42;
*t = st;
}
Is there any problem with such an implementation?
In your header, change
typedef my_type_t;
to
struct my_type_t;
It's a pretty common pattern. Just keep in mind that you'll need a function to allocate the struct on the heap and free it; one of the pieces of information you're hiding is the size of the struct, so the API consumer can really only deal with pointers to the struct not the struct itself.
The idiomatic API would be something like
struct my_type_t* my_type_new(void);
void my_type_free(struct my_type_t* self);
my_type_init would typically be used to initialize an already allocated instance, which is really only useful if you want to chain up to it in the *_new function of a subtype.
Edit: in response to your follow-up question, you could conceivably do something like this in your header:
#if !defined(MY_TYPE_NS)
# define MY_TYPE_NS struct
#endif
typedef MY_TYPE_NS my_type_t my_type;
my_type* my_type_new(void);
/* ... */
Then, in your *.c file:
#define MY_TYPE_NS union
#include "test.h"
union my_type_t {
/* ... */
};
my_type* my_type_new(void*) {
my_type* res = malloc(sizeof(my_type));
res->field = 42;
return res;
}
Which I find to be only slightly evil. I'd probably just use a union nested inside of the struct to avoid any surprises in the code.
The design pattern you are looking for is called "opaque type"/"opaque pointers".
You almost have it correctly, you just need to specify the type explicitly in the header:
typedef struct my_type_t my_type_t;
This is both a typedef and a forward declaration of an incomplete type, which is completed in your .c file and not visible to the caller.
Now the caller can declare pointers to this type, but not objects. They can't access struct members - we've achieved private encapsulation. You have to design your functions to always take a pointer type.

What is the equivalent of class in pure C

In OOP languages, we have classes. Is there an equivalent to class in pure C?
There is none. This fact was the original motivation for the development of C++, back when C++ was called "C with Classes". The closest thing you can get is a struct.
There is a feature in C intended to facilitate a sort of pseudo-inheritance, but it doesn't come close to an actual object-oriented class system. A pointer to a struct can legally be cast to and from a pointer to the struct's first member, so you can sort of "extend" a struct type A by having another struct type B start with a member of type A.
For example, you can have a PyObject struct type and a bunch of struct types that all start with a PyObject member, say PyIntObject, PyDictObject, etc:
typedef struct {
...
} PyObject;
typedef struct {
PyObject ob_base;
// more members...
} PyIntObject;
typedef struct {
PyObject ob_base;
// more members...
} PyDictObject;
You could then pass PyIntObjects and PyDictObjects around with PyObject pointers and use the data in the PyObject part to tell what the type of the enclosing struct is.
As you may have guessed from the names, I've taken this example from Python, where this mechanism is used to implement Python's object-oriented type system on top of C.
There is nothing equivalent to classes. Its a totally different paradigm. You can use structures in C. Have to code accordingly to make structures do the job.
You can swap "Class" in C++ for "struct".
I'm not saying you should but one mans object is another mans struct with some functions that operate on that struct and where the first parameter to the function is the struct itself. Obviously C++ adds some extra bits. C and opaque pointers are also Objects and very useful ones at that.
#include <iostream>
struct Cat {
public:
Cat(int initialAge); // constructor
~Cat(); // destructor
int GetAge();
private: // begin private section
int itsAge; // member variable
};
Cat::Cat(int initialAge) {
itsAge = initialAge;
}
int Cat::GetAge() {
return itsAge;
}
int main(void) {
Cat *cat = new Cat(1);
std::cout << "This cat declared as a struct is " << cat->GetAge() << " years old" <<std::endl;
return 1;
}
You can achieve a similar thing in C with a bit more work... Header file is
#ifndef CAT_H
#define CAT_H
#include <stdlib.h>
#include <stdio.h>
typedef struct Cat Cat;
typedef struct CatOps {
int (* GetAge )();
} CatOps;
struct Cat {
void * obj;
CatOps * ops;
};
Cat * new_cat(int age);
void delete_cat(Cat * cat);
#endif /* CAT_H */
.c file is
#include "cat.h"
#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
typedef struct cat_obj {
int age;
} cat_obj;
int get_age();
static CatOps CAT_OPS = {
.GetAge = get_age,
};
Cat * new_cat(int age) {
Cat * imp;
cat_obj * obj;
imp = malloc(sizeof(*imp));
obj = malloc(sizeof(*obj));
imp->obj = obj;
imp->ops = &CAT_OPS;
return (Cat*)imp;
}
void delete_cat(Cat *cat) {
free(cat->obj);
free(cat);
}
static void get_age(Cat *cat) {
cat_obj *c = (cat_obj*)cat->obj;
}
Note, I've not tested it but if you know C/C++ you should recognize the idiom.

C Global Struct Pointer

I have a typedef'ed structure declared in a file. I have a pointer to it and want to use it in multiple files as a global variable. Can someone point out what I am doing wrong?
fileA.h:
typedef struct
{
bool connected;
char name[20];
}vehicle;
extern vehicle *myVehicle;
fileA.c:
#include "fileA.h"
void myFunction(){
myVehicle = malloc(sizeof(vehicle));
myVehicle->connected = FALSE;
}
fileB.c:
#include "fileA.h"
void anotherFunction(){
strcpy(myVehicle->name, "this is my car");
}
The error I get is:
Undefined external "myVehicle" referred to in fileA
This is a declaration:
extern vehicle *myVehicle; /* extern makes this a declaration,
and tells the compiler there is
a definition elsewhere. */
Add a definition:
vehicle *myVehicle;
to exactly one .c file.

Simple header file visibility confusion in C

I have a strange problem in C about including header files.
main.c
#include <stdio.h>
#include <stdlib.h>
#include "location.h"
int waste_new_line();
int main()
{
location *crossroads = malloc(sizeof(*crossroads));
...
location.h
typedef struct Location_Struct location;
location.c
typedef struct Location_Struct {
int ID;
char *name;
char *description;
} location;
int setup_location(location* l, char* name)
{
...
Now this isn't working because
location *crossroads = malloc(sizeof(*crossroads));
is throwing an error:dereferencing pointer to incomplete type meaning that it can see the contents of location.h, yet it doesn't seem to be aware of location.c...
I've looked around and all the tutorials I've seen say that the linker will link both files together.
EDIT:
I have altered the code to include an initializer inside location.c as so:
main.c
...
#include "location.h"
int waste_new_line();
int main()
{
location *crossroads = initialize_location();
....
location.h
typedef struct Location_Struct location;
location* initialize_location();
location.c
...
typedef struct Location_Struct {
int ID;
char *name;
char *description;
} location;
location* initialize_location(location* l)
{
return malloc(sizeof(location));
}
...
This is still throwing the same error, yet only when I try and access the members of crossroads using:
crossroads->description
this will throw the deferencing to incomplete type error.
EDIT 2: For now I've decided to just put the struct definition in the header file...
This behaviour is expected. When you #include "location.h", only the header file is visible to the compiler. The location.c file comes along later, at link time.
You have two options:
Add a function, which you declare in location.h and define in location.c, which does the necessary malloc and returns a pointer.
Move the full definition of the struct to the header file.
The main file knows about a struct called Location_Struct (and a typedef). It has no idea how big it is, thus you can't apply sizeof to it.
Since you are effectively hiding the layout and the implementation of Location_Struct it makes sense to provide a "constructor" that allocates it.
EDIT
It seems I have to mention that by "constructor" I mean an ordinary function that has access to the implementation of the structure and can allocate and possibly pre-populate the object.
You need to put the definition of Location_Struct in the header file location.h. The compiler would not "see" the other source file (unless it were #include'd, which would not typically be a good idea).

Resources