Handmade Hero»Forums»Code
17 posts
Why use C++ instead of C
I am wondering why casey is using cpp files but writing in a C fashion, (and not just use plain C then) I remember seeing some differences in the earlier video's todo with the syntax of instanciating structs. But I wonder what and how would the code differ if it were 'modern' ansi C?

I myself have always worked in higher level languages and because of these videos slowly are getting hyped about the brutal limitations and history C offers. So I don't know about the history of writing C in C++ and why people do it like that.

bye!
Mārtiņš Možeiko
2238 posts / 1 project
Why use C++ instead of C
Casey have mentioned that he does like some of C++ features and that is why he uses C++ not pure C. Features like operator overloading, possibility to omit "struct" keyword when you are using structure types and maybe few more.
Abner Coimbre
318 posts / 1 project
Founder
Why use C++ instead of C
Edited by Abner Coimbre on Reason: Editing explanations for more clarity.
I am wondering why casey is using cpp files but writing in a C fashion, (and not just use plain C then) I remember seeing some differences in the earlier video's todo with the syntax of instanciating structs. But I wonder what and how would the code differ if it were 'modern' ansi C?

Casey hasn't made a big deal out of it other than enjoying some extra features provided by C++ that wouldn't be available on a strictly-C compiler:

1. Function overloading: Write multiple functions of the same name but with different implementations.

1
2
3
4
5
6
void reset_entity(Entity *e)
{ ... }
void reset_entity(Monstar *m)
{ ... }
void reset_entity(Hero *h)
{ ... }


2. Operator overloading: Redefine or overload most operators available. It is used to express operations on user-defined data types more naturally:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>

struct Point2D
{
    double x;
    double y;
    Point2D operator+(Point2D p2)
    {
        Point2D new_point;
        new_point.x = this->x + p2.x;
        new_point.y = this->y + p2.y;
        return new_point;
    }
};


int main()
{
    Point2D p1;
    p1.x = 1;
    p1.y = 1;

    Point2D p2;
    p2.x = 7;
    p2.y = 7;

    Point2D p3 = p1 + p2; // Wouldn't have worked without overloading operator 

    printf("p3(%.1f, %.1f)\n", p3.x, p3.y);
}


3. A struct is a class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct Point2D
{
    double x;
    double y;
    Point2D operator+(Point2D p2)
    {
        Point2D new_point;
        new_point.x = this->x + p2.x;
        new_point.y = this->y + p2.y;
        return new_point;
    }
};

and

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Point2D
{
public:
    double x;
    double y;
public:
   Point2D operator+(Point2D p2)
    {
        Point2D new_point;
        new_point.x = this->x + p2.x;
        new_point.y = this->y + p2.y;
        return new_point;
    }
};

Are exactly the same thing.

It allows you to place a function such as operator+ inside the struct itself, as shown earlier, because struct == class. However, Casey semantically treats it as a C struct (i.e. a group of variables placed under one single name in a block of memory).

By and large, Casey ignores the majority of the other features provided by C++ and its compiler (e.g. class-authoring mechanisms, facilities to aid in object-oriented programming, the Standard Template Library, references, move semantics, and pretty much everything else).

He ignores these to such an extent, that we really just say he's programming in C. And in a sense, he is. He just hand-selected those simple features that he liked from C++, and you'll notice they're just syntactic sugar anyway.
popcorn
70 posts
Why use C++ instead of C
Edited by popcorn on
For a long time I have put cpp after my file but only use classes. I knew about inharence but never really found a use for it. I did try to get into because I just thought it was the way to do things, but I found it, it was more confusing then it should be.
Anyways, why c++? Most popular console games, pc games are programmed in c++. Even androids games are now using ndk to optimize games to get that constant fps. And iPhone user are using objective c to write games. there are some good small games that weren't programmed in other languages.
Adam
7 posts
Why use C++ instead of C
abnercoimbre
I am wondering why casey is using cpp files but writing in a C fashion, (and not just use plain C then) I remember seeing some differences in the earlier video's todo with the syntax of instanciating structs. But I wonder what and how would the code differ if it were 'modern' ansi C?

Casey hasn't made a big deal out of it other than enjoying some extra features provided by C++ that wouldn't be available on a strictly-C compiler:

1. Function overloading: Write multiple functions of the same name but with different implementations.

1
2
3
4
5
6
void reset_entity(Entity *e)
{ ... }
void reset_entity(Monstar *m)
{ ... }
void reset_entity(Hero *h)
{ ... }


2. Operator overloading: Redefine or overload most operators available. It is used to express operations on user-defined data types more naturally:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>

struct Point2D
{
    double x;
    double y;
    Point2D operator+(Point2D p2)
    {
        Point2D new_point;
        new_point.x = this->x + p2.x;
        new_point.y = this->y + p2.y;
        return new_point;
    }
};


int main()
{
    Point2D p1;
    p1.x = 1;
    p1.y = 1;

    Point2D p2;
    p2.x = 7;
    p2.y = 7;

    Point2D p3 = p1 + p2; // Wouldn't have worked without overloading operator 

    printf("p3(%.1f, %.1f)\n", p3.x, p3.y);
}


3. A struct is a class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct Point2D
{
    double x;
    double y;
    Point2D operator+(Point2D p2)
    {
        Point2D new_point;
        new_point.x = this->x + p2.x;
        new_point.y = this->y + p2.y;
        return new_point;
    }
};

and

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Point2D
{
public:
    double x;
    double y;
public:
   Point2D operator+(Point2D p2)
    {
        Point2D new_point;
        new_point.x = this->x + p2.x;
        new_point.y = this->y + p2.y;
        return new_point;
    }
};

Are exactly the same thing.

It allows you to place a function such as operator+ inside the struct itself, as shown earlier, because struct == class. However, Casey semantically treats it as a C struct (i.e. a group of variables placed under one single name in a block of memory).

By and large, Casey ignores the majority of the other features provided by C++ and its compiler (e.g. class-authoring mechanisms, facilities to aid in object-oriented programming, the Standard Template Library, references, move semantics, and pretty much everything else).

He ignores these to such an extent, that we really just say he's programming in C. And in a sense, he is. He just hand-selected those simple features that he liked from C++, and you'll notice they're just syntactic sugar anyway.


Just curious why struct==class is a benefit? Does that mean they're interchangeable or would using struct in that fashion be preferable?
70 posts
innovation through innovation™
Why use C++ instead of C
Edited by d7samurai on
Sealatron
Just curious why struct==class is a benefit? Does that mean they're interchangeable or would using struct in that fashion be preferable?


struct and class are the exact same thing, except for one aspect: A class is private by default and a struct is public by default.

This means that instead of writing
1
2
3
4
class Foo {
public:
    int Member;
};

you can write
1
2
3
struct Foo {
    int Member;
};

and save yourself some typing / clutter.

Similarly, these two are the same:
1
2
3
4
5
6
7
8
class Bar {
    int Member;
};

struct Bar {
private:
    int Member;
};
Abner Coimbre
318 posts / 1 project
Founder
Why use C++ instead of C
Edited by Abner Coimbre on
Sealatron wrote:
Just curious why struct==class is a benefit?

I didn't mean that the C struct is better than a C++ struct (or vice-versa). I tried to show the 3 things I know are used in the Handmade Hero code that are strictly C++. In fact, removing them and compiling under C is trivial, a testament to just how little of C++ Casey actually uses.
-----------------------------------------------
Now, to talk about the infamous struct (for interested newcomers):

A struct in pure C

Let's pretend I'm interested in making a data type for my 2D hero. I would experiment with loose variables that represent the properties of said hero:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Headers go here...

#define BOOL unsigned
#define TRUE 1
#define FALSE 0

#define INITIAL_HERO_SIZE 6 // Feet

int main()
{
    // Our hero, Muratori!
    char *name = "Muratori";
    BOOL is_alive = TRUE;
    int x = 0;
    int y = 0;
    int x_step = 1;
    int y_step = 1;
    unsigned size = INITIAL_HERO_SIZE;

    BOOL should_reset = FALSE;

    while (TRUE)
    {
        if (should_reset) // Assume flag handled elsewhere
        {
            name = "Muratori";
            is_alive = TRUE;
            x = 0;
            y = 0;
            x_step = 1;
            y_step = 1;
            size = INITIAL_HERO_SIZE;
        }

        // Play game...
    }

    return 0;
}

Notice we're setting these values to their initial state when resetting, and we're probably "resetting our hero" in other places throughout the lifetime of our game. We can compress these operations into a function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// Headers go here...

#define BOOL unsigned
#define TRUE 1
#define FALSE 0

#define INITIAL_HERO_SIZE 6 // Feet

void reset_hero(char *name,
                BOOL *is_alive,
                int *x,
                int *y,
                int *x_step,
                int *y_step,
                unsigned *size)
{
    if (name)
    {
        free(name);
    }

    name = strdup("Muratori");
    assert(name);

    *is_alive = TRUE;
    *x = *y = 0;
    *x_step = *y_step = 1;

    *size = INITIAL_HERO_SIZE;
}

int main()
{
    // Our hero, Muratori!
    char *name;
    BOOL is_alive;
    int x;
    int y;
    int x_step;
    int y_step;
    unsigned size;

    reset_hero(name, &is_alive, &x, &y, &x_step, &y_step, &size);

    BOOL should_reset = FALSE;

    while (TRUE)
    {
        if (should_reset) // Assume flag handled elsewhere
        {
            reset_hero(name, &is_alive, &x, &y, &x_step, &y_step, &size);
        }

        // Play game...
    }

    return 0;
}

Wait. Were we not talking about what a dang struct is?

Using a struct

The struct keyword is used to specify a block of memory, able to store arbitrary values of potentially different data types, under a single name:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
struct Hero
{
    char *name;
    BOOL is_alive;
    int x;
    int y;
    int x_step;
    int y_step;
    unsigned size;
}; 

Here I'm telling the compiler that there may be a block of memory, referred to as Hero, that can store seven different values: pointer to a char, a boolean, four ints, and an unsigned int. Let us replace those loose variables in the previous code, and use this struct instead:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Headers go here...

#define BOOL unsigned
#define TRUE 1
#define FALSE 0

#define INITIAL_HERO_SIZE 6 // Feet

struct Hero
{
    char *name;
    BOOL is_alive;
    int x;
    int y;
    int x_step;
    int y_step;
    unsigned size;
};

void reset_hero(char *name,
                BOOL *is_alive,
                int *x,
                int *y,
                int *x_step,
                int *y_step,
                unsigned *size)
{
    if (name)
    {
        free(name);
    }

    name = strdup("Muratori");
    assert(name);

    *is_alive = TRUE;
    *x = *y = 0;
    *x_step = *y_step = 1;

    *size = INITIAL_HERO_SIZE;
}

int main()
{
    struct Hero muratori;

    reset_hero(muratori.name,
               &muratori.is_alive,
               &muratori.x,
               &muratori.y,
               &muratori.x_step,
               &muratori.y_step,
               &muratori.size);

    BOOL should_reset = FALSE;

    while (TRUE)
    {
        if (should_reset) // Assume flag handled elsewhere
        {
            reset_hero(muratori.name,
                       &muratori.is_alive,
                       &muratori.x,
                       &muratori.y,
                       &muratori.x_step,
                       &muratori.y_step,
                       &muratori.size);
        }

        // Play game...
    }

    return 0;
}

We can compact this even further, because a function can take in a struct as an argument:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Headers go here...

#define BOOL unsigned
#define TRUE 1
#define FALSE 0

#define INITIAL_HERO_SIZE 6 // Feet

struct Hero
{
    char *name;
    BOOL is_alive;
    int x;
    int y;
    int x_step;
    int y_step;
    unsigned size;
};

void reset_hero(struct Hero *hero)
{
    if (hero->name)
    {
        free(hero->name);
    }

    hero->name = strdup("Muratori");
    assert(hero->name);

    hero->is_alive = TRUE;
    hero->x = hero->y = 0;
    hero->x_step = hero->y_step = 1;

    hero->size = INITIAL_HERO_SIZE;
}

int main()
{
    struct Hero muratori;
    reset_hero(&muratori);

    BOOL should_reset = FALSE;

    while (TRUE)
    {
        if (should_reset) // Assume flag handled elsewhere
        {
            reset_hero(&muratori);
        }

        // Play game...
    }

    return 0;
}

You might be passing in struct Hero to many functions. You can typedef the struct to an alias, just like you would with any other type, and use that alias instead:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Headers go here...

#define BOOL unsigned
#define TRUE 1
#define FALSE 0

#define INITIAL_HERO_SIZE 6 // Feet

typedef struct Hero
{
    char *name;
    BOOL is_alive;
    int x;
    int y;
    int x_step;
    int y_step;
    unsigned size;
} H;

void reset_hero(H *hero)
{
    if (hero->name)
    {
        free(hero->name);
    }

    hero->name = strdup("Muratori");
    assert(hero->name);

    hero->is_alive = TRUE;
    hero->x = hero->y = 0;
    hero->x_step = hero->y_step = 1;

    hero->size = INITIAL_HERO_SIZE;
}

int main()
{
    struct Hero muratori;
    reset_hero(&muratori);

    BOOL should_reset = FALSE;

    while (TRUE)
    {
        if (should_reset) // Assume flag handled elsewhere
        {
            reset_hero(&muratori);
        }

        // Play game...
    }

    return 0;
}

Maybe you know you'll always want to use an alias. In that case, you could omit naming the struct:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Headers go here...

#define BOOL unsigned
#define TRUE 1
#define FALSE 0

#define INITIAL_HERO_SIZE 6 // Feet

typedef struct
{
    char *name;
    BOOL is_alive;
    int x;
    int y;
    int x_step;
    int y_step;
    unsigned size;
} Hero; // 'Hero' is now the alias for this unnamed struct.

void reset_hero(Hero *hero)
{
    if (hero->name)
    {
        free(hero->name);
    }

    hero->name = strdup("Muratori");
    assert(hero->name);

    hero->is_alive = TRUE;
    hero->x = hero->y = 0;
    hero->x_step = hero->y_step = 1;

    hero->size = INITIAL_HERO_SIZE;
}

int main()
{
    Hero muratori;
    reset_hero(&muratori);

    BOOL should_reset = FALSE;

    while (TRUE)
    {
        if (should_reset) // Assume flag handled elsewhere
        {
            reset_hero(&muratori);
        }

        // Play game...
    }

    return 0;
}


Notice that while explaining what a struct is, we used it to compress our silly code nicely.

A struct in C++

Let's talk about a class first. A simple, rough definition is that the class keyword allows you to specify (1) a collection of data, (2) operations that can be performed on that data, and (3) related functions, all under a single name. You can restrict access to certain types of data or functions, so it is only used internally. They can have the concept of a lifespan, inheritance, templates, and a ghastly number of so many other things. We will not cover how all of this works, just know that it is its own thing. This is purely C++, not C.

You can actually specify a class with two keywords: class or struct. Their difference was succintly explained by d7samurai:

struct and class are the exact same thing, except for one aspect: A class is private by default and a struct is public by default.

Thus, in C++, there is no concept of a struct, but a class. struct is a class.

Now, if you define a class that is completely public to access its data, and stick to only having variables inside them, and decide to use the struct keyword to have it public by default and save some typing, then you kinda made it into a C struct. You constrained the use of that class to the point of it being treated exactly like one.

But since it's a class, and not a C struct, I can loosen up that constraint and maybe add a function in there:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Headers go here...

#define INITIAL_HERO_SIZE 6 // Feet

struct Hero
{
    char *name;
    bool is_alive;
    int x;
    int y;
    int x_step;
    int y_step;
    unsigned size;

    void reset()
    {
        if (name)
        {
            free(name);
        }

        name = strdup("Muratori");
        assert(name);

        is_alive = true;
        x = y = 0;
        x_step = y_step = 1;

        size = INITIAL_HERO_SIZE;
    }
}

int main()
{
    Hero muratori;
    muratori.reset();

    bool should_reset = false;

    while (true)
    {
        if (should_reset) // Assume flag handled elsewhere
        {
            muratori.reset();
        }

        // Play game...
    }

    return 0;
}

Notice the above code will not compile under a C compiler, because C does not have classes (or a native bool type).
Adam
7 posts
Why use C++ instead of C
Cheers for the explanations! abnercoimbre, whats the virtue of using typedef life that? Why not just rename the struct altogether?
Mārtiņš Možeiko
2238 posts / 1 project
Why use C++ instead of C
You can also use anonymous struct there:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
typedef struct
{
    char *name;
    BOOL is_alive;
    int x;
    int y;
    int x_step;
    int y_step;
    unsigned size;
} Hero;
Casey Muratori
801 posts / 1 project
Casey Muratori is a programmer at Molly Rocket on the game 1935 and is the host of the educational programming series Handmade Hero.
Why use C++ instead of C
As a sidenote, it is usually good form to do the typedef version when you are making a header that might be called from C, so that the C++ code and the C code look the same (so the C code doesn't have to keep using the struct keyword).

- Casey
Ben
6 posts
Why use C++ instead of C
Some people avoid typedef struct in C because it pollutes the namespace. OP: by 'modern' ANSI C do you mean C11? I'm writing my SDL version using the C11 standard.
17 posts
Why use C++ instead of C
yeah indeed using features of c99 and/or c11, I've found out about that 'rule' of not typedeffing structs, I have mixed feeling about it, but I am completely new so I prefer to follow rules before making them :)
16 posts
Why use C++ instead of C
C++ must have done something right
https://www.youtube.com/watch?v=ltCgzYcpFUI
Timothy McCarthy
52 posts
Why use C++ instead of C
If I understand the POV, a C programmer is dismayed by the size of the generated C++ code; much like the assembly programmer was dismayed by the size of C program. A Game programmer is even more dismayed by this since it directly affects performance. By using a restricted set of C++ you can avoid added code.

FWIW, I'm interested in how Game programmers measure performance with the idea of using the techniques on C++ code. I'd like to know how well my code runs. As an experiment I'e been trying to write the code using C++ but with a few more features: strings, vectors, etc. The claim has been that these features have a reasonable general performance. There are compiler and library that affect this, etc. I haven't gotten very far but the code size is quite different. Thee is a code cost for bounds checking and using STL objects. the obverse of the C++ coin is, "You pay for what you use." The question is if you get value for your dollar. Here, the answer is no.
James Widman
21 posts
Why use C++ instead of C
benwaffle
Some people avoid typedef struct in C because it pollutes the namespace.


Could you please expand on this? My understanding is that "typedef struct" helps to *prevent* namespace pollution.

Background:

The following is valid C and valid C++ code and means the same thing in both languages:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    struct x {int n;}; /* #1 */
    int x(void);    /* #2 */

    void g(void)
    {
        int i = x(); /* Refers to declaration #2 */

        struct x /* Refers to declaration #1 */ 
          j;
    }

Unfortunately, this kind of overloading is possible because a name introduced with "struct", "class", "union", or "enum" lives in a name space (the name space of "tag" names) that is different from the name space for the names of functions, variables, members of an enumeration, and typedef names (known as "ordinary" names in C).

In C++, you can omit the leading "struct" keyword (or "union", etc.), and it will refer to the struct if the name has not also been declared as an "ordinary" identifier.

Horrible name overloading like this exists in certain well-known library interfaces, e.g. "stat()" and "struct stat" in POSIX. This might be viewed as a particularly unfortunate instance of "namespace pollution" (depending on how you define the phrase).

One way to stay out of trouble is to use the same identifier as both the tag name and the typedef name for the same struct:

1
typedef struct Y { int n; } Y; /* ok */

... which means the same thing in C as it does in C++. This may be preferred because the compiler will then catch you if you then try to use the same name as the name of a function:

1
void Y(void); /* error: "Y" was previously declared as a typedef name. */


(You can also just omit the tag name and you get the same effect. Of course, if you want to forward-declare the struct or define a constructor or destructor, you need a tag name.)