Author Topic: Learning C through a fun project?  (Read 9002 times)

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Learning C through a fun project?
« on: April 11, 2018, 08:38:19 AM »
Hey everyone,

I've enjoyed learning C++, but I tend to avoid facets of the language pre C++11. This means I don't have a great grasp on topics like raw pointers, the manual destruction of allocated resources, etc.

Anyways, I wouldn't mind spending time working with C instead of C++ to force myself to work on these features in an environment where things like classes and std::string and std::vector, etc are not available.

If anyone has an idea for a new or existing project that would be useful and/or interesting and that an intermediate programmer (for a hobbyist anyways) who is a newbie to C could contribute to, I would appreciate suggestions. It could be Outpost 2 related or completely not Outpost 2 related.

I would want to avoid using Linux or Macintosh as the primary development environment on my side (learn one skill at a time here). I would be comfortable working with Git or Mercurial for source control or something else.

I would probably buy a book to work on concurrently through.

Thanks,
-Brett

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: Learning C through a fun project?
« Reply #1 on: April 11, 2018, 06:37:31 PM »
My suggestion is unless you intend to be writing kernel level drivers or writing software for embedded systems, don't bother. C is genuinely painful. You'd think it'd be very similar but I learned the hard way that it's very, very, very much not.

A possible good way to get started is to grab an open-source engine... say Quake or Quake 2, and pull it apart. That's basically how I learned it by pulling apart the Quake 2 code and rewriting half the engine. I ultimately ended up porting it C++ anyway but it was a huge learning experience.

Offline Arklon

  • Administrator
  • Hero Member
  • *****
  • Posts: 1269
Re: Learning C through a fun project?
« Reply #2 on: April 11, 2018, 10:28:57 PM »
On the other hand, C has designator initializers for arrays and structs (and has since C99), but because the C++ ISO committee prefers insanity, that incredibly useful feature was never ported to C++.

Otherwise, you're probably better off writing something in C++ just using the cstdlib and no STL, which is the bulk of what you need to learn to write C, or what you would be doing if you were making an STL replacement, etc. - just to save yourself the tedium that is the rest of what writing C is like, which is hacking in object-oriented programming using nothing but structs and pointers like what the Quake 1/2 engine do.

I would say it's valuable to learn a little about how C++'s extra features over C, i.e. OOP, overloads, operator overloads, STL, exceptions (yuck), etc. are typically implemented/how they get compiled, and understand the overhead of them and how to mitigate that. If you're conscious about that, it's not too difficult to write C++ code that's just as efficient as the equivalent in C.
« Last Edit: April 11, 2018, 10:54:17 PM by Arklon »

Offline lordpalandus

  • Banned
  • Hero Member
  • *****
  • Posts: 825
Re: Learning C through a fun project?
« Reply #3 on: April 12, 2018, 01:50:23 AM »
Libtcod, the library I'm using for my python roguelike, can be compiled for C and C++ as well. Could always make something with the C version of the libtcod library.
Currently working on Cataclysm of Chaos, Remade.
Link to OPU page = http://forum.outpost2.net/index.php/topic,6073.0.html

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: Learning C through a fun project?
« Reply #4 on: April 12, 2018, 09:48:36 PM »
On the other hand, C has designator initializers for arrays and structs (and has since C99)

C++11 has something similar (or maybe identical?) for arrays. It's nice to be able to initialize a std::map<std::string, int> like this:

Code: [Select]
std::map<std::string, int> whatever =
{
    { "whatever1", 1 },
    { "whatever2", 2 },

    /* ... snip ... */

    { "whatever99", 99 }
};

For production/live code that does this see ProductPool.cpp from the OutpostHD project.

but because the C++ ISO committee prefers insanity

I haven't laughed so hard in quite awhile. But yes, I agree, C++ has lagged behind in a lot of very useful features. C++17 thankfully includes a lot of them.

I would say it's valuable to learn a little about how C++'s extra features over C, i.e. OOP, overloads, operator overloads, STL, exceptions (yuck), etc. are typically implemented/how they get compiled, and understand the overhead of them and how to mitigate that. If you're conscious about that, it's not too difficult to write C++ code that's just as efficient as the equivalent in C.

You have a point. I didn't realize how amazingly useful the STL was until I started pulling apart the idTech 2 engine. Holy shit, the ways to avoid pointer bugs in C++ makes C look like I'm carving stone tablets in Sumerian. But I also do appreciate what I can do with it on a very low level... e.g., if I was going to be writing software for an embedded system like a Rasberry Pi or even lower specs than that I'd probably stick with C with potentially some hand-tuned assembly.
« Last Edit: April 12, 2018, 09:51:09 PM by leeor_net »

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Re: Learning C through a fun project?
« Reply #5 on: April 13, 2018, 12:30:18 PM »
Thanks for everyone's advice. I actually like the idea of using C for embedded programming the most as this is something C really excels at. Maybe that isn't the best way to learn the language though.

I think my local library allows checking out Arduino (spelling?) boards or similar other products. Might be a good idea to check that out in more detail and try it out.

-Brett

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: Learning C through a fun project?
« Reply #6 on: April 17, 2018, 04:23:19 AM »
Yeah, I was thinking embedded stuff. That seems to be the main use case for C these days. A C compiler is a bit easier to write than a full C++ compiler, so it may have more support for various low end and embedded systems. There's also less going on under the hood to surprise developers with hidden performance costs. That can sometimes be an issue with C++, especially for people who are less experienced with it.

A possibly fun and educational project might be to figure out how C++ features can be implemented in C. Such as how classes can be implemented with structs. How virtual functions work. How function overloads work. How namespaces work. If you're really looking for trouble, you can look into how multiple inheritance works. Though I should note, most post C++ languages dropped the concept of multiple inheritance, at least outside the more limited concept of multiple interfaces.

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: Learning C through a fun project?
« Reply #7 on: April 17, 2018, 07:24:19 PM »
Multiple inheritance (especially the way C++ does it) leads to all sorts of ways to create massive (and very expensive) headaches.

My general rule of thumb is "If you're using multiple inheritance, you're doing it wrong". Granted there are edge cases where it makes sense but the second you run into the Diamond ProblemTM you find ways to build better inheritance graphs.

Offline lordpalandus

  • Banned
  • Hero Member
  • *****
  • Posts: 825
Re: Learning C through a fun project?
« Reply #8 on: April 17, 2018, 11:27:39 PM »
I'm assuming multiple inheritance means something like:

great-grandparent->grandparent->parent->child->grandchild ... etc.

Does C++ allow for Composition, like Python does?
Currently working on Cataclysm of Chaos, Remade.
Link to OPU page = http://forum.outpost2.net/index.php/topic,6073.0.html

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Re: Learning C through a fun project?
« Reply #9 on: April 18, 2018, 03:41:16 PM »
I bought the book Programming in C, 4th edition by Stephen G. Kochan https://www.amazon.com/Programming-C-4th-Developers-Library/dp/0321776410/

The book assumes no prior programming knowledge. The first chapter was pretty informative as an overview of how C came about and how a program compiles. Interestingly enough, Visual Studio 2017 doesn't fully support C11 and a lot of people will not use VS2017 when programming in C, I think due to it being a bit of a second class language on Visual Studio.

I've been picking up interesting tidbits like a c function assumes an int return if no return is specified in the prototype. Also, I didn't realize you could declare a variable that exists inside a function as static. Actually, I don't know if declaring a variable in a function as static is a C only thing or if it applies to C++. There is a keyword called auto that explicity states a function variable is not static as well.

He uses lots of mathematic examples like calculating all the prime numbers, calculating Fibonacci numbers, finding the square root, etc. He is easy to read and follow, so it is an enjoyable read so far.



lordpalandus,

multiple inheritance doesn't mean that. What it looks like you are describing is 'single' inheritance as supported by C# (only 1 parent per class). More specifically C++ also supports single inheritance in addition to multiple, but C# does not support multiple inheritance. Multiple inheritance is where one class has more than one parent class.

parent1 parent2
   |       |
   ChildClass

Using multiple inheritance on a regular basis is considered a poor practice as it can make the code base difficult to understand, debug, and continue to practice future inheritance on. The Archive code actually has a class that uses multiple inheritance. I'm sure there are edge cases where it is a good practice, although I've never come across a design on my own where it seemed the correct choice.

I've never heard of composition before. After briefly looking at it online, I think it is foreign to both C# and C++ although it does seem like an interesting idea.

-Brett

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: Learning C through a fun project?
« Reply #10 on: April 18, 2018, 07:08:47 PM »
Quote
I'm assuming multiple inheritance means something like:

great-grandparent->grandparent->parent->child->grandchild ... etc.

What you described is a single inheritance hierarchy. Each child class only inherits from a single parent class.

In C++, multiple inheritance is when an object inherits from more than one base class. E.g.:


Code: [Select]
class Foo
{
    /* Whatever stuff here */
};

class Bar
{
    /* Whatever stuff here */
};

class FooBar: public Foo, public Bar
{
    /* I inherit from two parents! */
};


Quote
Does C++ allow for Composition, like Python does?

Yes. It's just a fancy way of saying that a class can use other classes as member variables. E.g.:

Code: [Select]
class Contrived
{
    /* useless stuff here */
};

class SomeClass
{
public:
    SomeClass() {}
    ~SomeClass() {}

private:
    int _someInt;
    std::string _someString; /* std::string is a class */
    Contrived _someContrivedObject; /* Contrived is a class declared above */
};

The above code is basically useless but it does illustrate composition. Basically, you're composing a class using other classes (and also non-classes if you want to be very broad about the definition).

A good way to think about it is like this: Inheritance means Class B is a Class A vs. composition in which Class B has a Class A.
« Last Edit: April 18, 2018, 07:11:44 PM by leeor_net »

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Re: Learning C through a fun project?
« Reply #11 on: April 24, 2018, 02:01:05 PM »
I read through the chapter on bit operations and did a couple of the exercises associated with bit operations. Very interesting. Also relevant sense MapData uses a couple of bit operations. Not that my solutions matter to anyone, but they are pasted below.

Code: [Select]
// Checks if the compiler performs bit right shifts as arithmetic or logical
_Bool ArithmeticRightShift(void)
{
signed int test = -1;
test >>= 1;

return test < 0;
}

// returns how many bits are contained within an int for the compilation
unsigned int IntBitSize()
{
unsigned int test = ~0;
unsigned int intBitSize = 0;

while (test > 0)
{
intBitSize++;
test <<= 1;
}

return intBitSize;
}

// Checks if the supplied bit is set to 1 (ON) in the number. Bit counts from left to right.
_Bool IsBitSet(unsigned int number, unsigned int bitToSet)
{
unsigned int bitShift = IntBitSize() - bitToSet;

if (bitShift < 0) {
return 0;
}

number >>= bitShift;

return number & 1;
}

// Sets the supplied bit in the number to 1. Bit counts from left to right.
unsigned int SetBit(unsigned int number, unsigned int bitToSet)
{
unsigned int bitShift = IntBitSize() - bitToSet;

if (bitShift < 0) {
return number;
}

unsigned int bitToAdd = 1 << bitShift;

return number | bitToAdd;
}

void main(void)
{
_Bool arithmeticRightShift = ArithmeticRightShift();

int intBitSize = IntBitSize();

_Bool isBitSet = IsBitSet(100, 2); //Should be false

int newNumber = SetBit(0, 30); // Should be 4
}

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: Learning C through a fun project?
« Reply #12 on: April 27, 2018, 11:02:38 AM »
Glad to hear you're enjoying the book. Always fun to see exercises like that.

You might enjoy the answer on StackOverflow about how to Set the Most Significant Bit in C. It might provide an alternate solution to a couple of your methods. ;)

If you enjoyed the bit twiddling exercises, another wonderful resource I know of is the book Hacker's Delight. They've got some excellent problems in there, with beautifully explained solutions. A number of those problems are actually quite common everyday things I used when processing binary files, such as for OP2. Things like padding and alignment calculations are covered at the beginning of chapter 3 in the section "rounding up/down to a known multiple of a power of 2".



Alright Leeor, where's the example of Diamond Inheritance!  ;)


And for what it's worth, I regret my decision to use multiple inheritance in the Archive code. That strikes me as bad design in hindsight. I'm guessing I wrote that code shortly after learning how multiple inheritance works, but didn't yet have enough experience to know why it might be bad.



Using static in functions is fully supported in C++. It's basically the same in a function as in a class definition. The variable essentially becomes a global, at least in terms of where it is stored, though the language scope rules limit visibility and access to the variable.

The only particularly odd part, is when the variable is initialized. For simple types, it shouldn't matter much, and the compiler could hard code the initial value into the executable image, same as other globals. For a more complex constructed type, a function static variable would be initialized the first time the function is run, at the point where the variable is initialized (there will be a hidden guard around the initialization code to prevent re-initialization on future calls). For a class static variable, it would be initialized sometime before main runs.

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: Learning C through a fun project?
« Reply #13 on: April 27, 2018, 11:20:39 AM »
Alright Leeor, where's the example of Diamond Inheritance!  ;)


And for what it's worth, I regret my decision to use multiple inheritance in the Archive code. That strikes me as bad design in hindsight. I'm guessing I wrote that code shortly after learning how multiple inheritance works, but didn't yet have enough experience to know why it might be bad.

Well, I can do that, but the Wikipedia article explains it pretty well. I could provide a code example but it would be contrived.

General rule of thumb, if you're using multiple inheritance, you're probably doing it wrong. Composition is usually a far better alternative.
« Last Edit: April 27, 2018, 11:22:15 AM by leeor_net »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: Learning C through a fun project?
« Reply #14 on: April 27, 2018, 11:25:28 AM »
Agreed. Or Interfaces. Usually it's interfaces that let you get around multiple inheritance problems. Though technically that is still multiple inheritance, just a really restricted subset of it that provides most of the benefits without most of the problems.

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Re: Learning C through a fun project?
« Reply #15 on: May 04, 2018, 09:14:13 AM »
Hooman,

Thanks for the book suggestion. I downloaded the trial to my Kindle and will add it to the queue.

I randomly wrote a 4 page paper on the basics of Boolean Alegbra and its application to Computer Science (see attached). And no, it isn't for a class or anything. Sitting down and researching the subject really helped me actually understand twos complement form  and how it affects right shift operations. I don't know if anyone is interested in reading it, but if you do, feel free to provide feedback. I don't think anything in it is blatently wrong, but I'm not an expert on the subject.

Also really random, but I solved how long it would take to get to Mars from Earth via a Hohhmann transfer using C. I also practiced some different ways to initialize a structure in C in the process. Maybe I'll share that at some point in the future.

-Brett

Offline Arklon

  • Administrator
  • Hero Member
  • *****
  • Posts: 1269
Re: Learning C through a fun project?
« Reply #16 on: May 05, 2018, 12:27:29 AM »
C++11 has something similar (or maybe identical?) for arrays. It's nice to be able to initialize a std::map<std::string, int> like this:

Code: [Select]
std::map<std::string, int> whatever =
{
    { "whatever1", 1 },
    { "whatever2", 2 },

    /* ... snip ... */

    { "whatever99", 99 }
};
That will actually get constructed at runtime, which makes it crappy for a global const lookup table or whatever, since initializing global non-POD types goes into compiler/OS-specific quirky territory, and you'd really rather be in control of when a constructor is called. There's a few constexpr utility libs that implement compile-time versions of (hash) sets, (hash) maps, etc. I've been looking at though, which would be the ideal way to solve that problem.

https://github.com/bolero-MURAKAMI/Sprout (majority of it is C++11-compatible, with some C++14/17 extensions)
https://github.com/serge-sans-paille/frozen (requires C++14)

That Sprout lib has some, uh, pretty interesting things in it, like a compile-time raytracer renderer (because why not), and a compile-time Brainfuck interpreter (also because why not), both of which are fully C++11-compatible I think. Overall, as a general utility lib, it's a pretty interesting demonstration of what you can do using only C++11-style constexpr functions where it all has to be in a single statement.

Or you can use plain C and not worry about this kind of stuff. :P
« Last Edit: May 05, 2018, 12:29:25 AM by Arklon »

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: Learning C through a fun project?
« Reply #17 on: May 06, 2018, 10:54:37 AM »
That will actually get constructed at runtime, which makes it crappy for a global const lookup table or whatever, since initializing global non-POD types goes into compiler/OS-specific quirky territory, and you'd really rather be in control of when a constructor is called.

You're not wrong. I've run into this issue using NAS2D::Image's as globals... they fail almost every time or cause all sorts of weird behavior unless I make them pointers which solves that problem but leads to some unwieldy syntax. 0.0

But this works very well with POD. :D

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: Learning C through a fun project?
« Reply #18 on: May 13, 2018, 02:20:36 PM »
Vagabond, I love that you went and wrote a paper on this. I feel inspired.

Actually, writing a paper to explain stuff is probably a really good way to learn something. I've heard it said, if you really want to learn something well, try teaching it to someone else. I recently came across mention of some research that had a similar conclusion. You learn best when you struggle with trying to explain a concept.

I had a few thoughts reading through the paper. Some of it is additional knowledge, which you might find interesting.


The fastest 2-input logic gate is NAND. The second fastest is NOR. An AND gate is typically implemented as a NAND gate followed by a NOT gate. An OR gate is typically implemented as a NOR gate followed by a NOT gate. I'm willing to bet that was unexpected. If you're curious, I could give a considerable explanation (read "rant") as to why. =)

But wait, there's more! NAND is Functionally Complete. Any boolean expression can be represented entirely by NAND. NOR is also functionally complete. NAND and NOR are known as universal gates. Neither AND, nor OR are functionally complete. (See how I expressed that there? ;)) Even together, the set {AND, OR} is still not functionally complete. The set {NOT, AND, OR} is functionally complete, however, it's not minimal. You can remove an operation and still have a functionally complete set. The sets {NOT, AND} and {NOT, OR} are both functionally complete (and minimal).

Though that last part shouldn't be surprising due to De Morgan's Laws, which express AND and OR in terms of each other and NOT. Using fairly typical digital logic notation, these laws can be expressed as:
 (AB)' = (A' + B')   (Ambiguously read: "A (and) B not equals A not or B not")
 (A + B)' = (A'B')   (Ambiguously read: "A or B not equals A not (and) B not")

Which brings me to notation. There are a lot of variants, and it often depends on context, such as mathematically founded boolean algebra, digital logic textbooks, or programming languages. Here are a few common symbols:
Code: [Select]
NOT: ¬, ̅x (overbar), ', !, ~
AND: ∧, ·, (no symbol), &&, &
OR: ∨, +, ||, |

Examples:
Code: [Select]
C++:
 (x && y) || !z  (logical)
 (x & y) | ~z  (bitwise)
Mathematical Boolean Algebra:
 (x ∧ y) ∨ ¬z  =  (x AND y) OR NOT(z)
Digital Logic textbooks:
 AB + C̅   =  (A AND B) OR NOT(C)
 AB + C'  =  (A AND B) OR NOT(C)

You'll sometimes see a dot used for AND, much like for multiplication in Algebra:  ·  (Unicode U+22C5)
Like multiplication, AND is the default assumed operation when the symbol is omitted.
Code: [Select]
 ab  =  a · b  =  a * b  ("a times b")  (Math)
 AB  =  A · B  =  A AND B  (Digital Logic)

There's some fun stuff where given a truth table, you can implementing a corresponding boolean expression, minimizing the number of gates using Karnaugh maps, and converting it to NAND form. Though perhaps I'll save that for another time.



Feedback specific to your paper:

Quote
16-bit Word 0 to 65,536 -32,767 to 32,767
-32,768 ;)

Quote
0 ^ 3 = 0 + 2 ^ 2 = 4 + 0 ^ 1 = 0 + 2 ^ 0 = 1 Decimal 5
The notation looks a bit off. I assume you mean something like:
0*2^3 + 1*2^2 + 0*2^1 + 1*2^0
 = 0 + 4 + 0 + 1 = 5

Quote
XNOR   ~(a ^ b ) OR a == b
Not wrong, but slightly odd. XNOR does represent equivalence of boolean values. As C++ code, the two expressions behave quite differently for non-boolean values. In particular, they would mean something different for int values, producing different results. The left uses bit-wise operators, which work on corresponding bits in parallel. The right side is a logical operator and works on single boolean values. This is the difference between & and &&, or | and ||. The single character forms are bitwise operations, while the double character forms are logical operations.

You're correct in that the size of a WORD is the register size for a particular processor. In the case of x86, the original processor in that family had 16-bit registers. When the architecture was extended in a backwards compatible way to 32-bit, and later to 64-bit, they kept referring to WORD as a 16-bit datatype and began calling 32-bit values DWORDs (double words), and 64-bit values QWORDs (quad words). Other architectures may not support operations smaller than the WORD size, relying on bitmasks and AND to work on smaller sets of bytes.

This one is kind of a nitpick, especially considering there isn't some obvious inherent ordering of operations, but I find it odd your tables have AND and OR next to each other, but split up and re-arrange NAND and NOR.  ;)



Leeor, there's usually a way to use references. ;)

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Re: Learning C through a fun project?
« Reply #19 on: May 14, 2018, 08:42:20 PM »
Hooman, thanks for pointing out those mistakes! I attached the file with the fixes below.

The Karnaugh maps are interesting. I also found it frustrating that there were so many different symbols to represent each logical operator.

I wouldn't put too much more time into explaining the application in Computer Science to me at this point. Without some sort of application for me to practice the theory on, I probably won't be able to follow the information. I guess right now I'm mostly using Boolean Algebra for C++ bit fields and bit flags.

-Brett

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: Learning C through a fun project?
« Reply #20 on: May 15, 2018, 12:46:31 AM »
But... but... but..., there's so much I have to tell you about transistors! *sad face*
 ;)

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: Learning C through a fun project?
« Reply #21 on: May 15, 2018, 07:00:22 PM »
Nope. Denied.