typedef void read_overhead_test_func(read_params *Params, repetition_tester *Tester); struct test_function { char const *Name; read_overhead_test_func *Func; };
for loop
You can do cleaner code by using this fact.
int result = 0; for(;;) { if(true) return resul; } return result; // can be skipped by the if
I have experienced a bug (sound) where I wanted to put negative and positive value in a variable of type uint32
. Of course I have overflowed it. Compiler didn't catch it even with explicit -X
. I have to be focused when doing code in C
because it could yield in hours of debugging.
EntityTemplateParams p = { .pos = Add3F32(params->pos, V3F32(0, 0, 2.5f)), .half_extents = V3F32(1, 1, 1), .initial_count = 8, .hair_index = 1, .ingredient_kind = IngredientKind_Wood, };
>>
on a signed
value will produce 1
the reverse is true on a unsigned
It's because of the signed/unsigned shift instruction
if you don't initialize your index in the for
's loop parameters, it will do it for you (meaning = 0
)
Imagine you have two for
loop one after the other (the second outside of the first). You use the same name for both of the for
loop's index.
the index of the second loop will have the value of wathever was in the first.
[YT] Casey Handmade Hero day 006
YAKVI explain it better
[YT] Handmade Hero 021
++
does not always matter
The place of a ++
does not matter if you have no assignment. But does when you write ++Index = 2 * Index
and Index++ = 2 * Index
If you need to bit mask the size of a number like : 1.048.576 you can by having a common number. If you use a value in order to create a number like create a memory size to malloc(SizePow)
, you will be able to create a mask for the number with Mask = (1 << SizePow) - 1
the -1
is important because you will switch all bit to 1 because of the way bit number works.
1 << SizePow
is advancing the bit 1 count to much and when you -1 you will toggle the 1 to zero and the others to 1.
not sure, seems wrong
X << 4
With programming language implementing bitwise operations,
you can do : word = (byte1 << 8) | byte2;
The only reason why you get a 32bits from two 8 bits is because the variable IS a 32 bits value.
Bitwise, so the compiler make the assumption here.
operators permit to incorporate them together. You absolutely need the
<<
to move the byte1
value aside and not be overwrited by
byte2
I was watching [YT] Handmade Hero 033 - 'Q&A'
Basically when you have a memory having separate chunks of data, let's say two for easier example, for the upper part you just shift X
bits to the right and the upper part stay. Then, in order to use the same X
value to isolate the lower part, you have to :
1. You Shift to the left the X
time the number 1
. You will have X
zero on the lower part.
2. You substract 1
from it and as binary mathematics do, you will have X
one on the right. Note that it will invert every number (as binary does).
it looks like :
int ShiftAmount = 8; int Mask = 1 << ShiftAmount; // id est 0010000 Mask = Mask - 1 // 0001111
Make a 'note' class style?
The amount of bits shifted will be 0
.
I don't know why but in Windows Direct Sound API I had:
VOID *Region1; function(&Region1);
it didn't catch that I was doing wrong. Indeed why would I need to&
a pointer?@to-learn
I was trying to double a = int / int;
and as the result had digit after the comma, it was always 0
. The compiler can't tell you that because in C
you do always theses type of casting, as there is no force on the way we write code, if you don't pay attention, you can have the wrong value in your variables.
#define
preprocessortypedef
at compile timeC
/ C++
Union permit you to access a same piece of data in multiple way. id est two different structs and one type (see video). Handmade Hero - Day 014 - Union inside a struct
Handmade Hero - day 13
Intrinsic is a way in C to call a CPU's instruction set.
OOP Rant - "how I use Unions"- Shawn McGrath
@To-Learn: [Wikipedia] Discriminated unions & auto
keyword
they offer fixed size for every "objects".
you can do the following and Casey Muratori this the same thing in union-in-struct :
struct { int X; int Y; union { struct { int health; } Player; struct { int length; } Tree; } }
union
makes free computations (at compilation time) and conditional properties on struct
enum operand_type { Operand_Address, Operand_Register, Operand_Immediate }; struct instruction_operand { operand_type Type; union { effective_address_expression Address; register_access Register; immediate Immediate; }; };
If Type is set to Operand_Address, the Address member of the union will be valid, and you can access it as instruction_operand.Address. Similarly, if Type is set to Operand_Register, the Register member will be valid and can be accessed as instruction_operand.Register. If Type is set to Operand_Immediate, the Immediate member will be valid and can be accessed as instruction_operand.Immediate.
You can create a struct with array inside it then just type the parameters with the struct having array inside it.
struct game_input { game_controller_input Controllers[4]; };
internal void GameUpdateAndRender(game_input *Input)
GameUpdateAndRender(game_input *Input) { game_controller_input *Input0 = &Input->Controllers[0]; }
This trick is used when you want a function called multiple times and don't want to change parameters in multiple location. [YT] Handmade Hero - 021
return
in a for
loop, will break
itfor(int ControllerIndex = 0; ControllerIndex < ControllerCount; ++ControllerIndex ) { game_controller_input *Controller = GetController(Input, ControllerIndex); if(!Controller->IsConnected) return; // stop looping when it's true; }
char*
will make a int64
result
CatString(FullEXEPath - EXEFileName)Gives compilation error:
C2664: 'char *CatString(char *,char *)': cannot convert argument 1 from '__int64' to 'char *'build.bat
char *[]
for(char *Scan = EXEFilePath; *Scan; ++Scan) { }
char*
can stop before you think when using string
char *SourceCodeTempDLLFileName = "handmade_temp.dll";
will give
"handmade"
You have to use char SourceCodeTempDLLFileName[]
instead to avoid it.
sizeof()
with array
if you use sizeof(char String[] = "foo")
it will include the \0
. so you will often do a -1
.
Having a master memory dictate all others, meaning you have a pointer to the main memory block and have a second data type pointing to a part of this memory, will makes everything easier later.
You could have the main memory reloaded from a file and update all your game without causing a crash somewhere or having complex code that could handle this change: You just update One memory chunk and have all your data updated.
This is something to keep in mind for all projects, always try to separate the code that handle the transformation of your data and the code that update the inputs serve to it.
Handmade Hero - 024 - "Can we go over really fast an overview of the game recording code?"
[9][16]
: [16]
is packed contiguously while [9]
will be rows.
I wrote a procedure to convert a real32
to an int32
. When I was calling it in my code I did uint32 x = Procedure(real32)
. When real32
was negative it just wrapped around from the min value to max value. I have some warnings turned off, it should be why I had no warning about it. warnings turned of: -wd4505 -wd4101 -wd4201 -wd4100
int
is more difficult to handle
Indeed it's platform dependent. Using stdint
is safer. [YT] Handmade Hero 029 - 'Question: So int32 is because you want to make the game for 32bit computers?'
Usually a good thing to have it, because it permit you to track the number of data type when having an array inside it. C
doesn't provide a way to get the informations (malloc()
seems to do it but allocate more space than needed), so you have to implement it. I don't think it's bad, when your mind does it automatically, that's fine. Of course you add open doors to get a special bug if you don't set it correctly, but as far as I know, these bugs are easily traced.
struct tile_map { uint32 *Tiles; }; struct world_map { int TileMapCountX; int TileMapCountY tile_map *TileMaps; }; { world_map World = {}; World.TileMapCountX = 2; World.TileMapCountX = 1; // meaning TileMaps[1][2] }The thing to keep in mind is that you can't do a
TileMaps[0][0]
because C
doesn't interpret it as an array, even if arrays are memory address. The cleaner way to do it is by creating an array of the actual data type in the struct, then save the address of the first entry of the array:
tile_map TileMap00 = {}; TileMaps[0][0] = TileMap00; world_map World = {}; World.TileMaps = &TileMaps[0][0];
When you have typed the pointer, you can just do a Tiles[2]
to retrieve the correct data, you're ensure you will always be at the right boundaries and have a proper data, wich would be dramatic if you did as C
doesn't ensure the data inside the pointer is the beginning of the type. C
is famous to not be safe, it's you as a developer to be aware of the code you produce.
@TODO: The reason for the count seem to not be to know the boundary of the array, instead it seems that it's a way to safely iterate on them later. The crucial part is more by first create the array of the data type then store the memory of the first entry
Sometimes you can take advantage of the fact that you can copy a previous struct data then override the properties that are different. It gives you an automatic update when updating the original data type.
float
truncation
If you truncate a negative float
, it will [YT] Handmade Hero 0 - 'truncate towards 0'. So you have different behaviour depending if you're positive or negative. If your logic use the truncation behaviour and can have negative value, you should use floorf()
inline
can be used to remind you that a function should be inline in the future.
inline
is not necessary done by the compiler, so it can be just a reminder
struct
's properties
If you pass specific struct
's properties to a function as pointer
, you can modify it in the function. It seems to be a good practice. Will see.
a_function_name()
and your type like a_type
gives you benefit when changing name
I had to change a struct
named position
and wanted to rename it to tile_map_position
and using my replace all occurences from the file every functions named like get_position()
where renamed like get_tile_map_position()
.
Using underscore and only lower case characters when naming function
(s) and struct
(s) make easy renaming when not using a bloated IDE.
struct
to "automatically allocate memory"I was watching [YT] Handmade Hero 032
If you have an allocated amount of memory initialized by the OS (like a game state) and you know its address in memory, storing data inside it is a pain because you will have to deduce the memory of the last address it was written + the size it was. In order to do it automatically there is an easy trick
struct
inside the game state previously allocated in memory:
struct memory_arena { uint8* Base; memory_index Size; memory_index Used; };
A function to initialize it in order to keep track of it later
internal void initilialize_arena(memory_arena *Arena, memory_index Size, uint8* Base) { Arena->Base = Base; Arena->Size = Size; Arena->Used = 0; }
A function to add memory on the next chunk memory
#define push_struct(Arena, type) (type*)push_size_(Arena, sizeof(type)) #define push_array(Arena, Count, type) (type*)push_size_(Arena, (Count)*sizeof(type)) void* push_size_(memory_arena *Arena, memory_index Size) { Assert(Arena->Used + Size <= Arena->Size); void *Result = Arena->Base + Arena->Used; Arena->Used += Size; return(Result); }
I was watching [YT] Handmade Hero 034 - 'Size type'
When you want to handle a parameter that is a struct
there is no way to make it work normally, as in any programming language I know of. But see C/C++
permits you to use macros in order to do it.
#define push_struct(Arena, Type) (Type*)push_struct_(Arena, sizeof(Type)) void* push_struct_(memory_arena *Arena, memory_index Size) { }
struct
I was wondering: I initiazed a struct
that had tile_chunk *TileChunks
inside it, I was using for
loop in order to set the specific tile_chunk
items inside it. Do I need to first do something like passing an array I had created in the pointer? In order to initialize it. The answer is yes. If you never explicitely point the pointer to an actual memory size (so an explicit array) the compiler will never knows the size he has to allow. Then you will have a null
pointer exception when you will try to write in the X (>0)
item.
If you have a struct
that has an array inside it, the amount of memory needed for them is not initialized. It seems obvious when you think at first about it, but when you're inexperimented and don't have the automatism, you will easily forget to initialize an array. The reason is that you don't declare an array but a pointer to the array. The compiler has no clue on the size you will require. Some programming languages force you to assum a size and if you want to extend it they will ask you to call a method. It's called "dynamic array" and you can implement the behaviour in C
. The better way to do it is by copying stb lib from Sean Barret and the stb_ds.h file
#define
over const
?I was watching [YT] Handmade Hero 035 - 'When do you use #define instead of local variables?'
#define
gives you the benefit to not have a type. So you will be able to use it in several places.
bool32
over bool
native type?I was watching [YT] Handmade Hero 035 - 'Why suffix types with 32 such as bool32?'
C
convert bool
type to true
/false
and have performance cost.
struct
's properties to read adjacent bits[YT] Handmade Hero 036 - 'Packing a struct to avoid padding'
I discovered another great use for arenas: writing custom file formats with variable length sections. If you make an arena and set the align to 1, you can just use that arena to build your file without having to do as much pointer arithmetic. Say you have a section that is struct section { size_t size; HeaderType type; // Section Specific data starts here }; You can construct an arena, then just push that struct and push the data afterwards. You can of course do the same with using plain old fwrite, but then it is harder to debug in the debugger. With arenas you can check in memory easier and you get all the other benefits of the arena. Once you have built the whole thing in memory you can just dump it into a file Yep this is similar to what the PDB -> RADDBGI converter does https://github.com/EpicGamesExt/raddebugger/blob/dev/src/raddbgi_from_pdb/raddbgi_from_pdb.c#L3762
C++
does not use the order of the "properties" to pack bits. It pack bits depending on the bit boundaries of the highest amount of bits in the struct
You will have to guide C
to #pragma pack
from [Microsoft] pragma pack. It's like a stack, you #pragma pack(push,1)
and declare your struct
, then you #pragma pack(pop)
when you're done.
You can either do it with the compiler. But as you don't do it often and there's is no difference, it's fine. Does it apply only with Windows?
I had a realization when I was processing a bitmap and reading the content from it: Before having created a type bitmap_header
I was just casting the content in the watch window of the debugger. Then when I created the data type then casted with it the content in my C
code, I could read all properties nicely. You feel dumb when you realize simple things like it but it's just a way for you to quicker make sense of a memory in the computer, nothing more. I'm pretty sure you could never have to create a data type in your code and be able to do the same computation.
Data types are just a way to nicely make sense of the computer's memory content.
[YT] Handmade Hero 036 - 'Why wouldn't you use a function template in the example mentioned before?'
Template have been created in order to minimize the amount of lines of code you need to write in similar situations. If your program always do the same operations maybe that's an ok situation to be used. But writing lines of code is not hard, your compiler will have better time to optimize it, you will understand easily what a function does and I don't think maintanability is actually gain from it, we have code editors that can handle macros, have powerfull replace function.
Meta programming is a better solution (C/C++
are bad at it though)
[YT] Handmade Hero 037 - 'Bitscan forward'
[Wikipedia] Bit scan manipulation and _BitScanForward, _BitScanForward64
static
keyword[YT] Handmade Hero 040 - 'The reasoning behind internal, local_persist, and global_variable'
Having different meaning depending of where it is used, you need to be able to produce a grep
adequate, as your source code becoming large.
Explanation of the possibilities of preprocessors https://learn.microsoft.com/en-us/cpp/preprocessor/grammar-summary-c-cpp?view=msvc-170
The website gives you very detailed explanation of preprocessing in C/C++
#include
trickAs it's like a copy / paste thing you can do a cool thing like fill an array with another file generated from a build like:
static instruction_encoding InstructionTable8086[] = { #include "sim86_instruction_table.inl" // file created by a previous compilation };
Variadic macros
Defining function macros with variable number of argument:
#define eprintf(...) fprintf (stderr, __VA_ARGS__)
use:
eprintf ("%s:%d: ", input_file, lineno);
Compiled as:
fprintf (stderr, "%s:%d: ", input_file, lineno);
for loop
without break
for(u64 Index = 0; Index < A.Count; ++Index) { if(A.Data[Index] != B.Data[Index]) { return false; } }
Pass a memory pointer, then = {}
in order to reset it and get a proper exception if used after the free
static void FreeBuffer(buffer *Buffer) { if(Buffer->Data) { free(Buffer->Data); } *Buffer = {}; }
Preprocessor permits you to do it, available in Windows and other OS
#if _WIN32 struct __stat64 Stat; _stat64(FileName, &Stat); #else struct stat Stat; stat(FileName, &Stat); #endif
The structure will have several properties of the file.
struct
If you have a data that can have unlimited nested structures, do:
struct json_element { buffer Label; buffer Value; json_element *FirstSubElement; json_element *NextSibling; };
Notice the pointers.
Then in order to iterate through it:
for(json_element *Search = Object->FirstSubElement; Search; Search = Search->NextSibling) {}
Used properly
@To-Learn: Absolutely not sure, on msvc it's right but have tried on a online c compiler and it initialize
static foo;
permit to be certain that the variable is undefined
then you can use the "value" for the memory variable if it fails:
static u8 FailedMemory; if(!Memory) { Memory.Adress = &FailedMemory; }
struct
Don't put directly {type Array[];}
, instead put a pointer {type *Array}
.
You avoid a compiler errors saying you must put array at the end | you can't assigm it to a variable. You store way less data inside the type, as you only need the pointer to the array.
Using directly the pointer sterr
you're to use fprintf("hello", stderr);
without having to open a file handle and more.
struct
property to a static string arraystatic char *Raw[] = {"", "", ""}; struct ascii_table { char **Encodings; int AsciiCount; };
char
and char*
tables
A table char Table1[]
nad char *Table2[]
differ in the way it's interpreted.
A simple example permit to understand it:
static char Table1[] = {'\0', '\}; static char *Table2[] = {"", "};
*
binds to the name, not the type[Twitter] Jonathan Blow complaining on VS formating on pointers.
void example() { int* a,b; a = b; }
Gives you an error.
char
pointer the same will cause bugs
I had created a char Temp[MAX_PATH];
inside a function then another one in the main
.
main
called the function ones. And a weird bug occured when the declaration of char Temp[MAX_PATH];
was passed inside the main
.
Indeed all the data that was from the Temp
from the function updated at the same time.
The reason is that when you call a function that create a data type and set it to a struct. It stays in the call stack of the program. So if you call it again.