Jump to content

  • Log In with Google      Sign In   
  • Create Account


Struct Inheritance in C


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
21 replies to this topic

#1 NUCLEAR RABBIT   Members   -  Reputation: 247

Like
4Likes
Like

Posted 17 February 2014 - 08:50 PM

Hello,

 

I am trying to make a struct type and make another struct built on top of the other struct and I got it working, but I'm not sure what the tutorial means when it says I need to do pointer casting. I know how, but not sure why I have to do this? I seem to have it going without the casting so I'm not sure why they mentioned it. Can anyone help me understand why? rolleyes.gif

 

my code:

#include <stdio.h>
#include <string.h>

typedef struct {
    float wage;
    char name[15];
} Employee;

typedef struct{
    Employee super;
    char title[15];
} Manager;

int main(int argc, const char * argv[])
{
    Manager manager1;
    strcpy(manager1.super.name, "Steve");
    strcpy(manager1.title, "Manager On Duty");
    
    Manager manager2;
    strcpy(manager2.super.name, "Jack");
    strcpy(manager2.title, "CEO");
    
    printf("Title:\t\t\t\tEmployee Name:\n%s\t\t%s\n", manager1.title, manager1.super.name);
    printf("%s\t\t\t\t\t%s\n", manager2.title, manager2.super.name);
    
    return 0;
}

tutorial code: (not same example)

typedef struct
{
    // base members

} Base;

typedef struct
{
    Base base;

    // derived members

} Derived;

then they say "As Derived starts with a copy of Base, you can do this:"

Base *b = (Base *b)d;

"Where d is an instance of Derived. So they are kind of polymorphic. But having virtual methods is another challenge - to do that, you'd need to have the equivalent of a vtable pointer in Base, containing function pointers to functions that accept Base as their first argument (which you could name this)"


Edited by NUCLEAR RABBIT, 17 February 2014 - 08:59 PM.

------------------------My band: RISE OVER ME!

Sponsor:

#2 Álvaro   Crossbones+   -  Reputation: 11701

Like
12Likes
Like

Posted 17 February 2014 - 09:00 PM

What you have done is called "composition", not "inheritance". The good news is that composition is the right tool to use more often than inheritance.

The pointer cast is what makes inheritance look like inheritance: Some other part of the code can take a pointer to an Employee and do something with it, and if instead of giving it a pointer to an Employee you give it a pointer to a Manager (after casting), it would work just fine.

However, the main reason to use inheritance is not to build neat little hierarchies the way most OOP texts seem to suggest: The main reason is polymorphism, which means you can call a function that takes a pointer to Employee as an argument, and it might do something different depending on the specific type of employee that was passed. In C you would achieve this by making `Employee' have a function pointer as part of its data, so each derived type can provide an appropriate function to call there. (The mechanism that C++ uses is not exactly this; C++ compilers typically use a vtable instead (look it up).)

Edited by Álvaro, 17 February 2014 - 09:01 PM.


#3 NUCLEAR RABBIT   Members   -  Reputation: 247

Like
4Likes
Like

Posted 17 February 2014 - 09:05 PM

What you have done is called "composition", not "inheritance". The good news is that composition is the right tool to use more often than inheritance.

The pointer cast is what makes inheritance look like inheritance: Some other part of the code can take a pointer to an Employee and do something with it, and if instead of giving it a pointer to an Employee you give it a pointer to a Manager (after casting), it would work just fine.

However, the main reason to use inheritance is not to build neat little hierarchies the way most OOP texts seem to suggest: The main reason is polymorphism, which means you can call a function that takes a pointer to Employee as an argument, and it might do something different depending on the specific type of employee that was passed. In C you would achieve this by making `Employee' have a function pointer as part of its data, so each derived type can provide an appropriate function to call there. (The mechanism that C++ uses is not exactly this; C++ compilers typically use a vtable instead (look it up).)

 

Sweet, thank you! Makes more sense now. And I'll will look into vtables 


------------------------My band: RISE OVER ME!

#4 Ravyne   Crossbones+   -  Reputation: 6472

Like
5Likes
Like

Posted 17 February 2014 - 09:06 PM

The idea is that you'll be able to access and use a Manager as an Employee when you want to treat them as just another employee. For example, if you just wanted to do payroll, you don't care who's a manager or what their title is.

 

So you can create a function, for example, that takes an employee by pointer and prints out the payroll check, and you can pass it pointers to regular employees or pointers to managers by casting them as pointers to employees. The one function will handle both -- If you continue to access a Manager's Employee attributes through the Super member variable as you're doing in your code, then you'd need to separate functions.

 

You'd be doing functionally the same thing by passing the address of a manager's 'super' member variable -- but because its the first struct element, simply casting the Manager pointer to an Employee pointer achieves the same thing. All it says is "Treat this manager as a regular employee."



#5 NUCLEAR RABBIT   Members   -  Reputation: 247

Like
3Likes
Like

Posted 17 February 2014 - 09:12 PM

The idea is that you'll be able to access and use a Manager as an Employee when you want to treat them as just another employee. For example, if you just wanted to do payroll, you don't care who's a manager or what their title is.

 

So you can create a function, for example, that takes an employee by pointer and prints out the payroll check, and you can pass it pointers to regular employees or pointers to managers by casting them as pointers to employees. The one function will handle both -- If you continue to access a Manager's Employee attributes through the Super member variable as you're doing in your code, then you'd need to separate functions.

 

You'd be doing functionally the same thing by passing the address of a manager's 'super' member variable -- but because its the first struct element, simply casting the Manager pointer to an Employee pointer achieves the same thing. All it says is "Treat this manager as a regular employee."

 

You're awesome!


------------------------My band: RISE OVER ME!

#6 SeanMiddleditch   Members   -  Reputation: 3507

Like
3Likes
Like

Posted 17 February 2014 - 10:44 PM

The idea is that you'll be able to access and use a Manager as an Employee when you want to treat them as just another employee. For example, if you just wanted to do payroll, you don't care who's a manager or what their title is.

 

It's very difficult for us humans to properly explain inheritance and polymorphism without resorting to some kind of taxonomic metaphor, isn't it? smile.png

 

Java-like object models teach you that you need inheritance for generic programming. If you want to have a list of all Employees and then call some function Payroll on the whole list you must clearly ensure that all the Employees (even if different) share some common base in order to enable a common interface, right?

 

Functional approaches and those enabled with more sophisticated C++ idioms don't need any kind of is-a relationship between objects to enable a common interface to be applied to them.  Consider free (non-member) operator<< overloads for various types; you don't need to modify either std::ostream or your custom type in order to allow the custom type to be streamed.  All a Manager aggregating an Employee needs to work in a generic payroll system is something that knows how to extract the Employee field from a Manager.  It could also be done with adapter classes or several other approaches; which is best depends on the situation and architecture of the rest of the system.

 

C doesn't easily afford this kind of design, unfortunately, since it lacks tools like overloading or generics, but there's a reason so many non-trivial applications and frameworks are in C++ instead of C these days. :)



#7 Buckeye   Crossbones+   -  Reputation: 3596

Like
4Likes
Like

Posted 17 February 2014 - 11:44 PM


strcpy(manager1.title, "Manager On Duty");

Nothing to do with your post, but FYI, you should use strcpy_s for fixed character buffers. You reserved 15 characters in the struct. "Manager On Duty" has 15 characters and a terminating null. That needs a 16 character buffer. You're asking for problems if you ignore possibilities like that.

 

EDIT: from the docs for:

char *strcpy( char *strDestination, const char *strSource );

"The strcpy function copies strSource, including the terminating null character, to the location that's specified by strDestination.

Security Note

Because strcpy does not check for sufficient space in strDestination before it copies strSource, it is a potential cause of buffer overruns. Therefore, we recommend that you use strcpy_s instead."


Edited by Buckeye, 17 February 2014 - 11:48 PM.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#8 SeanMiddleditch   Members   -  Reputation: 3507

Like
7Likes
Like

Posted 18 February 2014 - 12:16 AM


Nothing to do with your post, but FYI, you should use strcpy_s for fixed character buffers.

 

strcpy_s is not a part of the C standard language.  It is a Win32 specific function.

 

See https://stackoverflow.com/questions/4570147/safe-string-functions-in-mac-os-x-and-linux for some suggestions on alternatives for other platforms.  There is no cross-platform solution that doesn't require using some third-party library, unfortunately.



#9 Buckeye   Crossbones+   -  Reputation: 3596

Like
0Likes
Like

Posted 18 February 2014 - 07:12 AM


strcpy_s is not a part of the C standard language. It is a Win32 specific function.

Good point.

 

Using std::string for names, titles, etc., would obviate that problem.


Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#10 BitMaster   Crossbones+   -  Reputation: 3576

Like
0Likes
Like

Posted 18 February 2014 - 07:19 AM

Using std::string for names, titles, etc., would obviate that problem.


Considering the whole thread is about doing the work in C that is not an option at all.

#11 LennyLen   Crossbones+   -  Reputation: 3179

Like
1Likes
Like

Posted 18 February 2014 - 07:26 AM

 

Using std::string for names, titles, etc., would obviate that problem.


Considering the whole thread is about doing the work in C that is not an option at all.

 

 

I wish I had a dollar for every time I've seen someone be told to use a C++ feature to solve a C issue.



#12 Buckeye   Crossbones+   -  Reputation: 3596

Like
0Likes
Like

Posted 18 February 2014 - 07:39 AM

<deleted post> Not enough coffee yet this morning.


Edited by Buckeye, 18 February 2014 - 07:42 AM.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#13 LennyLen   Crossbones+   -  Reputation: 3179

Like
0Likes
Like

Posted 18 February 2014 - 07:41 AM


He has #include at the top of the file. I made the assumption his "c programming" tag was incorrect.

 

Actually, he has #include <string.h>  ;)



#14 Buckeye   Crossbones+   -  Reputation: 3596

Like
0Likes
Like

Posted 18 February 2014 - 07:45 AM

You're too quick. blink.png Just noticed that.


Edited by Buckeye, 18 February 2014 - 07:45 AM.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#15 Karsten_   Members   -  Reputation: 1365

Like
0Likes
Like

Posted 18 February 2014 - 08:21 AM


typedef struct{
    Employee super;
    char title[15];
} Manager;

 

This may look like composition (and can be) but it is also the start of inheritance in C.

 

Now that you have a struct of Employee (not a pointer) in the first bytes of the Manager struct, this struct can now be passed into any function accepting an Employee and it will work.

Prototype...
void employee_set_name(Employee* employee, char* name).
 
i.e.
employee_set_name(employee, "Fred");
employee_set_name((Employee*)manager, "Harry");

A diagram showing the "virtual" blocks of memory of the struct might be useful here and really it comes down to functions seeing the Manager struct effectively being an Employee struct "with a bunch of trailing bits on the end".

 

The simplicity of how this works is quite cool and I believe it is with the addition of an offset is how C++ supports the rare feature of multiple inheritance.


Edited by Karsten_, 18 February 2014 - 08:32 AM.

Mutiny - Open-source C++ Unity re-implementation.
Defile of Eden 2 - FreeBSD and OpenBSD binaries of our latest game.


#16 Alessio1989   Members   -  Reputation: 1587

Like
1Likes
Like

Posted 18 February 2014 - 09:20 AM

 


Nothing to do with your post, but FYI, you should use strcpy_s for fixed character buffers.

 

strcpy_s is not a part of the C standard language.  It is a Win32 specific function.

 

See https://stackoverflow.com/questions/4570147/safe-string-functions-in-mac-os-x-and-linux for some suggestions on alternatives for other platforms.  There is no cross-platform solution that doesn't require using some third-party library, unfortunately.

 

 

C11 adds strcpy_s (and other "_s" safe functions). Before C11 they were part of some TR... Here's a link to the final C11 draft http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf



#17 KnolanCross   Members   -  Reputation: 1150

Like
0Likes
Like

Posted 18 February 2014 - 11:35 AM

Unless you have dire performance needs use snprintf to manipulate string, it is the best all around function.

 

On topic, most times I have seen heritage system its was like this:

#include <stdio.h>

#define DEFAULT_BUFFER_SIZE 64

struct Employee_t {
    char name[DEFAULT_BUFFER_SIZE];
    float wage;
};

typedef struct Employee_t Employee;

struct Manager_t {
    Employee super;
    char title[DEFAULT_BUFFER_SIZE];
};

typedef struct Manager_t Manager;

struct Intern_t {
    Employee super;
    char college[DEFAULT_BUFFER_SIZE];
};

typedef struct Intern_t Intern;

static void printInfo(void* toPrint){

    Employee* casted = (Employee*)toPrint;
    printf("Employee %s wage is %.2f.\n", casted->name, casted->wage);
}

int main(int argc, char* argv[]){

    (void)argc;
    (void)argv;

    Manager first, second;
    Intern third;

    snprintf(first.super.name, sizeof(first.super.name), "Mike");
    first.super.wage = 10000;
    snprintf(first.title, sizeof(first.title), "CEO");

    snprintf(second.super.name, sizeof(second.super.name), "Jack");
    second.super.wage = 6000;
    snprintf(second.title, sizeof(second.title), "Day Shift Manager");

    snprintf(third.super.name, sizeof(third.super.name), "Steve");
    third.super.wage = 700;
    snprintf(third.college, sizeof(third.college), "MIT");

    printInfo(&first);
    printInfo(&second);
    printInfo(&third);

    return 0;

}

The main point here is to take advantage of the fact that both Manager and Intern structs have their Employee struct as their first element, meaning they will in the first bytes of the scruct. This allows you to directly cast them to the parent struct and use it as the parent.

 

Personally I have seem many codes that do that, specially when they are handling with generic containers. I have used it myself while using ORX engine, so all my structs had a parent struct with pointers to functions. This was really useful to free several different structs.

 

EDIT:

 

I used a void* in this example just to explicit show the cast, it could receive an Employee* instead.


Edited by KnolanCross, 18 February 2014 - 11:38 AM.

My blog on programming and games.
http://16bitsflag.blogspot.com.br/

#18 Paradigm Shifter   Crossbones+   -  Reputation: 5092

Like
1Likes
Like

Posted 18 February 2014 - 03:40 PM

 


He has #include at the top of the file. I made the assumption his "c programming" tag was incorrect.

 

Actually, he has #include <string.h>  ;)

 

 

<string.h> is a C header. It's called <cstring> in C++ and it dumps stuff in namespace std.

<string> is the C++ header.


Edited by Paradigm Shifter, 18 February 2014 - 03:40 PM.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

#19 BeerNutts   Crossbones+   -  Reputation: 2514

Like
0Likes
Like

Posted 18 February 2014 - 03:48 PM

Unless you have dire performance needs use snprintf to manipulate string, it is the best all around function.

 

On topic, most times I have seen heritage system its was like this:

#include <stdio.h>

#define DEFAULT_BUFFER_SIZE 64

struct Employee_t {
    char name[DEFAULT_BUFFER_SIZE];
    float wage;
};

typedef struct Employee_t Employee;

struct Manager_t {
    Employee super;
    char title[DEFAULT_BUFFER_SIZE];
};

typedef struct Manager_t Manager;

struct Intern_t {
    Employee super;
    char college[DEFAULT_BUFFER_SIZE];
};

typedef struct Intern_t Intern;

...

...

 

 

Or even better:

typedef struct {
char name[DEFAULT_BUFFER_SIZE];
float wage;
} Employee_t;

typedef struct {
Employee_t super;
char title[DEFAULT_BUFFER_SIZE];
} Manager_t;

typedef struct {
Employee_t super;
char college[DEFAULT_BUFFER_SIZE];
} Intern_t;
 

No need to separate out the typedef and struct definitions in C, and, personally, if you're going to typedef something, it should have the '_t' modifier, not the struct definition that isn't even used.


My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#20 KnolanCross   Members   -  Reputation: 1150

Like
0Likes
Like

Posted 18 February 2014 - 04:34 PM

 

Unless you have dire performance needs use snprintf to manipulate string, it is the best all around function.

 

On topic, most times I have seen heritage system its was like this:

#include <stdio.h>

#define DEFAULT_BUFFER_SIZE 64

struct Employee_t {
    char name[DEFAULT_BUFFER_SIZE];
    float wage;
};

typedef struct Employee_t Employee;

struct Manager_t {
    Employee super;
    char title[DEFAULT_BUFFER_SIZE];
};

typedef struct Manager_t Manager;

struct Intern_t {
    Employee super;
    char college[DEFAULT_BUFFER_SIZE];
};

typedef struct Intern_t Intern;

...

...

 

 

Or even better:

typedef struct {
char name[DEFAULT_BUFFER_SIZE];
float wage;
} Employee_t;

typedef struct {
Employee_t super;
char title[DEFAULT_BUFFER_SIZE];
} Manager_t;

typedef struct {
Employee_t super;
char college[DEFAULT_BUFFER_SIZE];
} Intern_t;
 

No need to separate out the typedef and struct definitions in C, and, personally, if you're going to typedef something, it should have the '_t' modifier, not the struct definition that isn't even used.

 

 

Can you point me any advantage other than coding style? I like to split the declarations from the typedef, I find that it has better reabability when the structs are bigger.


Edited by KnolanCross, 18 February 2014 - 04:38 PM.

My blog on programming and games.
http://16bitsflag.blogspot.com.br/




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS