Life without the OOP – Code conversion ongoing…

(I obviously don’t have a Mac…)

Rewriting my code to C is somewhat tedious (especially the math lib…), I’m nowhere near converting every C++ file I have, but of what I got so far I like the end result a lot, and I enjoy the shift I’m seeing in my reasoning ability.
Not having to worry about object oriented programming allows me to cut a lot of fat in the code, make a lot of things way simpler and clearer, and reason better about what I want to do and how I do it.

Here is my reworked test library header:

#ifndef AME_TEST_H
#define AME_TEST_H

/* @library ame_test: very lightweight C test library */

#ifdef __cplusplus
extern "C" {

#ifndef __cplusplus
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <cstring>

#ifndef AME_TYPES
typedef uint8_t uint8;
typedef int8_t int8;
typedef uint16_t uint16;
typedef int16_t int16;
typedef uint32_t uint32;
typedef int32_t int32;
typedef uint64_t uint64;
typedef int64_t int64;
typedef float real32;
typedef double real64;

/* declares a test */
#define TEST(name) void name(bool *out_error)

/* triggers a test error */
#define TEST_ERROR_EMPTY() printf_s("%s (%i)\n", __FILE__, __LINE__); *out_error = false

/* triggers a test error with message */
#define TEST_ERROR(error) printf_s("%s (%i) : %s\n", __FILE__, __LINE__, error); *out_error = false

/* tests if expressions are equal */
#define TEST_EQUAL(value1, value2) if ((value1 == value2) == false){ printf_s("%s (%i) : %s not equal %s\n", __FILE__, __LINE__, #value1, #value2); *out_error = false; }

/* tests if expressions are different */
#define TEST_NOT_EQUAL(value1, value2) if ((value1 != value2) == false){ printf_s("%s (%i) : %s equal %s\n", __FILE__, __LINE__, #value1, #value2); *out_error = false; }

/* tests if expression is true */
#define TEST_TRUE(value) if (value == false){ printf_s("%s (%i) : %s is false\n", __FILE__, __LINE__, #value); *out_error = false; }

/* tests if expression is false */
#define TEST_FALSE(value)	if (value == true) { printf_s("%s (%i) : %s is true\n", __FILE__, __LINE__, #value); *out_error = false; }

#define C_TEST_NULL_NAME "null" /* name of null test/suite */
#define C_NULL_TEST { C_TEST_NULL_NAME, NULL } /* null test initialization */
#define C_NULL_TEST_SUITE gc_null_test_suite /* null suite initialization */

typedef void (*test_func)(bool*); /* test function type */

/* test struct with test name and function */
typedef struct {
	char      name[64];
	test_func function;
} Test;

/* test suite struct with suite name and tests */
typedef struct {
	char  name[64];
	Test *tests; /* array of tests terminated by a null test */
} TestSuite;
extern const TestSuite gc_null_test_suite; /* null test suite */

/* runs all tests suite
 * @param suites: array of TestSuite terminated with a null test suite
 * @return 0 if test is successful
int32 test_run(const char* module, TestSuite* suites);

#ifdef __cplusplus

#endif // AME_TEST_H


That mini-mini-library by no means has extensive features, but for now it has all I need. Here is a small example of how to use it:

#include "ame_test.h"

	int foo = 4;
	TEST_EQUAL(foo, 4);
	TEST_NOT_EQUAL(foo, 5);
	bool bar = foo == 4;
	bar = foo == 6;

Test _my_tests[] =
	{ "example_test", example_test },

TestSuite _my_suites[] =
	{ "example_suite", _my_tests },
	{ "", NULL }

int main(int argc, char** argv)
	int res = test_run("example module", _my_suites);
	return res;

Output when running this:

start test module example module
running test suite example module/example_suite
example module/example_suite/example_test [ OK     ] [ 0.20 ms ]
1 of 1 tests successful [ 1.00 ms ]
end test module example module [ 1.74 ms ]

When running the currently converted core library tests it gives something like this:

start test_core
start test module core
running test suite core/memory
core/memory/frame_allocator              [ OK     ] [ 0.08 ms ]
core/memory/stack_allocator              [ OK     ] [ 0.07 ms ]
core/memory/pool_allocator               [ OK     ] [ 0.03 ms ]
core/memory/random_access_allocator      [ OK     ] [ 0.15 ms ]
4 of 4 tests successful [ 3.09 ms ]
running test suite core/string
core/string/basic_string                 [ OK     ] [ 0.02 ms ]
core/string/fixed_string                 [ OK     ] [ 0.02 ms ]
core/string/dynamic_string               [ OK     ] [ 0.07 ms ]
3 of 3 tests successful [ 2.07 ms ]
running test suite core/commands
core/commands/interp                     [ OK     ] [ 0.30 ms ]
1 of 1 tests successful [ 1.59 ms ]
running test suite core/filesystem
core/filesystem/dir_functions            [ OK     ] [ 98.37 ms ]
1 of 1 tests successful [ 100.74 ms ]
end test module core [ 115.72 ms ]
terminate test_core

Could be better so I’ll keep improving it. I’ll probably add fixture-like stuff in the future, maybe parameterized test features, even though I can write most of my unit tests without that.

I got a lot of inspiration from a really neat C unit test library: µnit
Check that out if you want a real good unit-test lib.

Now I C

Going full C and not just C-style C++, I am happy about:

  • no operator/function overloading: at first I was very worried about not having those anymore, but now I see why it’s much better without. Not having to worry about implicit conversions anymore, I feel I have more control over what each function does
  • strict naming, documentation & indentation/alignment it’s probably useless because I don’t intend to share or deploy my code any time soon, but a clean looking header with consistent naming just warms my heart. It’s a good exercise in being rigorous anyway
  • slowly training my brain to program C more efficiently after years of corrupting my thinking with the OOP, I find myself going back to the way I was programming in college, right after learning C. Programming now feels simpler, more fluid, and I think I’m getting already good reflexes to not get in my own way

Let’s hear it for the boys

I’ve been following the advice, or the work of:

  • Casey Muratori – I feel like I should watch all of Handmade Hero and I would learn a great deal, so far I’ve watched bits here and there, but mostly I enjoyed his epic rants videos I’ve found (I’m very ashamed of myself…)
  • Ryan Fleury – I’ve been following Ryan’s Youtube channel for a while, his Melodist game looks great, it’s because his way of coding looked really cool that I decided to change my approach, so thanks Ryan!
  • Eskil Steenberg – I’m only a few minutes through this video but I’m already getting an insane amount of good tricks and tips. I have a feeling I will learn A LOT from him…

There is a good amount to learn still and much (MUCH) more code to write to get closer to the goals I want to achieve, but I have a feeling that keep on following these guys is gonna help me a lot!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: