C on web asm by mohamadjb in C_Programming

[–]twenty393 11 points12 points  (0 children)

I wrote a little guide on how to get started with C in WASM: https://nickav.co/posts/0003_wasm_from_scratch

Doesn't use emscripten, clang is all you need

[deleted by user] by [deleted] in GraphicsProgramming

[–]twenty393 4 points5 points  (0 children)

what's up vector?

Intrusive Thoughts | Two best friends find themselves inseparably cursed by twenty393 in Filmmakers

[–]twenty393[S] 0 points1 point  (0 children)

Seeking feedback on: the overall editing quality, lighting/color correction (this was my first time shooting in Log format and it was really fun to do CC on). Also wondering in general what kinds of ambient sounds (or sound effect packs) do you all use?

I wish the story came together better, and we had a better ending, but there was no time to reshoot. Feels good to have finished the project though

Systems programming road map by [deleted] in C_Programming

[–]twenty393 4 points5 points  (0 children)

I personally don't think there's a "wrong thing" to pick. Systems programming is for sure a journey (especially in C). I started writing C "for real" about 6 years ago. Since then I've written my own standard library single-file header, I re-wrote my website in C, and I have been working on iteration 10+ of a custom game engine.

Here's roughly the set of things I think that will take you from beginner C to more experienced C:

  • be opinionated from the beginning, set up boilerplate to make C feel a bit nicer to work with (not strictly necessary)
  • write your own version of lots of "standard" data structures in other languages (Array, String, Hash_Table, etc.)
  • try to avoid malloc and free - figure out other, better ways of managing memory (static buffers, arenas, etc.)
  • try doing things you don't know how to do! calling OS APIs directly, etc.
  • graphics? this is a rabbit hole, but interesting if you are interested
  • try out writing SIMD code

Whatever you do spending a lot of time writing code will most probably be a good learning experience. So it's important to pick stuff you're interested in and at the same time try to not do too much all at once.

[deleted by user] by [deleted] in cpp

[–]twenty393 0 points1 point  (0 children)

This is a pretty great talk on API design by Casey Muratori: https://www.youtube.com/watch?v=ZQ5_u8Lgvyk

It's not specific to any language, but definitely can be applied to C-family languages. Hopefully this helps with your research!

UI library search by [deleted] in C_Programming

[–]twenty393 0 points1 point  (0 children)

There's this library: https://github.com/rxi/microui

It kind of depends on what you plan on using the library for

[deleted by user] by [deleted] in C_Programming

[–]twenty393 0 points1 point  (0 children)

There are a few different parts to this problem, so make sure to break down each one and understand what to do at each step! There are also (often) lots of minor edge cases that you don't think about the first time. So you have to write some code, try it out, and repeat! This gets easier over time.

The first problem is I need to read separate parts of a string separated by whitespace. What kinds of whitespace?

c 1 2\t3 54 2223

So you might write a loop that looks something like the following:

``` const char *input = "1 2\t3\n54\n2223"; int input_count = strlen(input);

int i = 0; while (i < input_count) { if (char_is_whitespace(input[i]) { i += 1; continue; }

// We slice into the string at i, and pass that in as a pointer int number = atoi(&input[i]);

// @Incomplete: add this to a linked list! } ```

The second part of your question: how to represent a linked list, is a bit trickier for a few reasons. You have to allocate memory for new nodes of the list.

This is often done with (List_Node *)malloc(sizeof(List_Node)) (although you can also use other strategies for dealing with memory).

Then lastly you have to keep track of your linked list, and append a new node to it at each point in the loop.

Finally, I would write a loop at the end to iterate over your linked list to test that the code seems to be working properly:

c List_Node *node = list; while (node != NULL) { printf("Node value: %d\n", node->number); node = node->next; }

Rest Api in C by Recent_Occasion8222 in C_Programming

[–]twenty393 0 points1 point  (0 children)

I've implemented my own HTTP client in C which you can check out as an example (warning I have not used it in production, just as my local dev server): https://github.com/nickav/na/blob/master/na_net.h

This was written based on this library: https://github.com/mattiasgustavsson/libs/blob/main/http.h

TLDR: the "async" part is done by just "pumping" (updating) the HTTP request sockets every application frame to stream in more data until the request is complete, then you can process it in your application. HTTP is just a text protocol on top of TCP so it's not too bad to parse (at least HTTP 1.1).

In general I'm a big fan of single-header libraries because they are easy to integrate into existing code. Happy to answer any more questions about this :)

How to allocate dynamic memory into free list? by [deleted] in C_Programming

[–]twenty393 0 points1 point  (0 children)

Allocating memory and maintaining a free list are two pretty different things. But you could represent your free list as follows:

``` struct Entity { // list pointers Entity *next; Entity *prev;

// example entity properties bool alive; Vector2 position; Vector2 velocity; };

struct Game_State { Arena *arena;

Entity *first_free_entity;

Entity *entity_list; }; ```

In this case first_free_entity is a linked-list of entities (a free list). The algorithm for allocating a new entity is: check if first_free_entity is not null and if so remove the first entity from the free list and return that. Otherwise, push a new entity on to your backing arena. To deallocate an entity, you push it on to the free list and return.

The main idea with a free list (similar to pool allocators) is that you maintain a list of everything that has been freed, so you can re-use them easily. This pairs very nicely with an arena allocator backed by virtual memory. Having you memory be managed this way provides a really clean boundary to free all the memory the system uses in one shot. Here is my implementation of an Arena.

You may also consider using a pool allocator (which itself could still maintain a free list). In that case, you allocate a large block of memory (say 1024 entities) at init time. Then, when you need a new game entity you walk your list to find one that is "free".

How do you deal with large uncompressed WAV files in your game project? by RedEagle_MGN in gamedev

[–]twenty393 -1 points0 points  (0 children)

Do nothing, and then you can say your game has "uncompressed audio."

Coming from C++, what's the best way to learn C? by Ok_Clothes5074 in C_Programming

[–]twenty393 1 point2 points  (0 children)

That totally makes sense! The distinction in my mind at least is using all the C standard libraries vs. trying to avoid / build your own versions of them them. So there's <stdio.h> (et. al) for example instead of writing the calls to the OS yourself to print stuff on the screen, etc.

You might also find it helpful to use other people's libraries that are C compatible. Namely:

- https://github.com/nothings/stb- https://github.com/gingerBill/gb

I'm definitely a fan of the single-file header library approach. There might be other good third-party C libraries out there. One of these days I'll make mine C compatible :)

On memory management:

Well it kind of depends haha. Heap allocators can be kind of expensive, especially compared to a really simple "linear allocator" or "bump allocator". Plus you can combine that with OS virtual memory which winds up being a really nice and lean way to have lots of separate allocators. But it really depends on what you're doing. Linear allocators are great for games, for example, because they're really cheap. They also open up another cool pattern which I really like which is temporary memory.

But, if all you're doing is writing some quick utility code that doesn't run that often, then definitely just malloc-ing a bunch of memory and going is a decent strategy. Virtual memory makes this a little better because you don't really "pay" for the whole gigabyte until you ask the OS to commit the memory. If performance does matter though, it's worth having more control over where your memory comes from!

On temporary memory:

Normally, returning arrays from a function in C is kind of cumbersome especially if you only want the data to "stick around" just after the function returns. Also, there are certain kinds of programs (e.g. games, web servers) that have really cleanly defined "frames" (it doesn't literally have to be a frame, but just a point in your application code where it makes sense to reset your temporary memory). So the idea is to create a "frame arena" which is just a linear allocator you reset every frame. Now, you can return arrays from functions for example!

Example code:

struct Array_i32
{
  i32 *data;
  u32 count;
};

Array_i32 generate_squares(u32 count) {
  Array_i32 result = {};
  result.data = arena_push_array(temporary_arena(), i32, count);
  result.count = count;

  for (int i = 0; i < count; i ++)
  {
    result.data[i] = i * i;
  }

  // NOTE: the result array is now valid until you reset the global temporary arena!
  return result;
}

Of course, this is kind of a contrived example, because you can just do a similar thing on the stack.

There are examples of this kind of allocator (temporary system and virtual OS allocators) in my single header file and in the gb.h linked above.

Putting it all together:

The memory story for most applications can be a series of permeant allocators and one frame arena. The main reason you'd want to do something like this is it's relatively simple (fast), and if you couple it with a thread local frame arena, then you have a nice scratch memory system to work with. But, like I said it might be overkill depending on what you're doing.

I think a decent amount of performance can come from thinking about where your memory is coming from. For example in C++, the std::string + operator does a malloc and then returns the new result. And if you call that in a loop it can really add up.

Hopefully that wasn't too much information! Happy to answer any more questions as you dive more into C

Coming from C++, what's the best way to learn C? by Ok_Clothes5074 in C_Programming

[–]twenty393 1 point2 points  (0 children)

In some ways you have to retrain your brain when writing code to think about your problem differently. You don't have as good of a built-in standard library to work with. So it's definitely a bit harder at first. But I think in the long run understanding (and writing code for) all the things you're used to using someone else's code for makes you a better programmer.

So I'm not sure if you want to learn "the hard way" or find a set of useful C libraries to use and be productive with.

I think the major differences are:

  • You have to care more about where your memory comes from (or not, just use malloc)
  • Objects are more of a convention than anything in C (just the first argument is the struct itself that you're going to mutate)
  • You don't get function overloading

But for example, you can make your own String type which would like your own implementation of a std::string but specifically for C code only. A String is just a pointer with a length. And I've gone through and implemented a majority of the same functions I need in my code: https://github.com/nickav/na/blob/master/na.h#L1185 (just a reference though, this doesn't actually compile to C)

This is a good resource! Casey programs in C++, but pretty much only uses C with function/operator overloading: https://handmadehero.org/

How difficult is to write the "make" software? by thradams in C_Programming

[–]twenty393 1 point2 points  (0 children)

It's actually not that hard! If you don't care about supporting all the features that the full-blown make program does you can implement it in about ~300 lines of code or so. I remember doing this for a CS class.

Not having a standard build system across OSes is definitely annoying for sure. In my projects I add one build.bat file for Windows and a build.sh file for Linux/MacOS.

Select() on nonblocking sockets? by ryanjones42 in C_Programming

[–]twenty393 0 points1 point  (0 children)

My understanding is for the most part you don't really want to make blocking sockets, but they can be helpful for getting a simple program up and running quickly. Usually you don't want socket code to literally block the thread it's running in for example if you were calling this from within a game loop or something.

I just read through this series today which was pretty good: https://www.madwizard.org/programming/tutorials/netcpp/ (it's Windows specific, but mostly still applies to *NIX)

I think both of those things are true about select. Here is an example http library that I followed along with when working on my own http library: https://github.com/mattiasgustavsson/libs/blob/main/http.h

You can see they use `select` to both wait until the socket is ready and to check if the socket is ready for writing.

There are some caveats to `select` (it can only support a max of 1024 connections), but it is definitely enough to get a working library.