Table of contents

Ah… good ol C

I have been always a big fan of C.

It’s:

  1. Simple - All you need to really wrap your head around are pointers and memory!
  2. Compact - The C standard library is tiny compared to the monster libraries that Java and C++ have.
  3. Powerful - C is the high-level language that is closest to assembly. One can translate C to assembly just by looking at C and have an general idea of what is happening to memory.

C has been around for ages. It hasn’t changed much, but it is still a popular language among the low-level programmers (ex. kernel dev, function/memory tools, Linus Torvald’s rants)

C the ugly side

However, there are a plethora of reasons as to why C is not an attractive modern language to work with.

  1. Memory management - Other languages like Java have a garbage collector, so one doesn’t have to worry about managing memory
  2. Suitable standard library - C standard library. There are many gotchas in the standard library. There are many deprecated functions in the standard library. There are many thread unsafe functions in the standard library. The standard library will make you sad sometimes. :(
  3. Getting something done - C doesn’t have vectors. C doesn’t have maps. C doesn’t really even have proper strings. C doesn’t have anything really besides pointers and memory that the programmer has to figure out how to use. This makes developing something in a timely manner incredibly frustrating and difficult.

C++ like C?

Wait. There’s still hope!

Introducing OOC! (Object oriented C library) - Github link

OOC is a library wrapper I wrote around a year ago. It is mainly influenced by C++ and has C++ like syntax.

Here is a quick example of string splitting in OOC:

Vector(String) SplitByDelimiter(String str, String delimiter)
{
	//Create a vector containing all the strings
	Vector(String) directories = New(Vector(String));

	//Parse line 
	int start_index = 0;
	int index_of_slash = 0;

	//Find the index of the next occurence of "/"
	while ((index_of_slash = Call(str, find, delimiter, start_index)) != NPOS)
	{
		//Get the substring between the last occurence and next occurence of "/"
		String directory = Call(str, substring, start_index, index_of_slash);

		//Insert the substring into the vector
		MoveCall(directories, push_back, directory);

		//Update the index to one past the occurence of "/"
		start_index = index_of_slash + 1;
	}

	//There is still one substring after the last occurence of "/"
	String last_directory = Call(str, substring, start_index, index_of_slash);

	MoveCall(directories, push_back, last_directory);

	return directories;
}

An example of string splitting in C++:

std::vector<std::string> SplitByDelimiter(std::string& str, std::string& delimiter)
{
	//Create a vector containing all the strings
	std::vector<std::string> directories;

	//Parse line 
	int start_index = 0;
	int index_of_slash = 0;

	//Find the index of the next occurence of "/"
	while ((index_of_slash = str.find(delimiter, start_index) != std::string::npos))
	{
		//Get the substring between the last occurence and next occurence of "/"
		std::string directory = str.substr(start_index, index_of_slash);

		//Insert the substring into the vector
		directories.push_back(std::move(directory));

		//Update the index to one past the occurence of "/"
		start_index = index_of_slash + 1;
	}

	//There is still one substring after the last occurence of "/"
	std::string directory = str.substr(start_index, index_of_slash);

	directories.push_back(std::move(directory));

	return directories;
}

As you can see, OOC’s syntax is very similar C++’s syntax, which is pretty neat since OOC is implemented in just C.

Breaking down the example

Vector(String) SplitByDelimiter(String str, String delimiter)

Objects in OOC are pointers since references in C do not exist. By default, everything is passed by reference (pointers, cough, cough) in OOC.

Therefore, String type here is actually a pointer to a string struct. Vector(String) as well is a pointer to a vector of string structs.

//Create a vector containing all the strings
Vector(String) directories = New(Vector(String));

Every object must be allocated with the New keyword. This is because C cannot automatically invoke the constructor when the object is declared unlike C++, which is allocated on the stack.

std::vector<std::string> directories; //automatic allocation on the stack!

In addition, as I mentioned in the previous paragraph, objects in OOC are actually pointers, so they work similar to having a pointer to a class in C++.

//More like
std::vector<std::string>* directories = new std::vector<std::string>(); //allocating the vector in heap!
//Find the index of the next occurence of "/"
while ((index_of_slash = Call(str, find, delimiter, start_index)) != NPOS)

You might have noticed that Call(...) is used to call the appropriate function unlike:

while ((index_of_slash = str.find(delimiter, start_index) != std::string::npos))

Call is how one calls an class’ function/method in OOC. Call requires at least two parameters to be passed, of which the first two are the object variable, which is str in the example and the second is the object’s function that we wish to call, which is find. The rest are arguments to the function call. So, in the example above, we want str to call find the delimiter at offset start_index.

Using Call definitely is harder to read than the C++ way, but the variable/function names are formatted in the correct order. On the technical side, the reason why Call(...) is necessary will be discussed in a future post.

Also, NPOS is used instead of std::string::npos as C doesn’t have namespaces.

//Get the substring between the last occurence and next occurence of "/"
String directory = Call(str, substring, start_index, index_of_slash);

//Insert the substring into the vector
MoveCall(directories, push_back, directory);

This is where it gets interesting. In C++11, move semantics became part of the C++ standard. To summarize, one could “move” the ownership of an object to another object or as some people describe it – “moves its guts to the new object”.

Literally taking its guts

Basically, in the example above, MoveCall(directories, push_back, directory); would transfer move directory into directories vector, thus making directory invalid to use afterwards.

Example of what I mean by invalid usage after the item has been moved:

//Insert the substring into the vector
MoveCall(directories, push_back, directory);

//Invalid code (directory is null, its contents were moved into directories)
//char* directory_c_str = Call(directory, c_str);

//Correct code (grab directory from within directories vector)
String directory = Call(directories, get, Call(directories, size) - 1); 
//Get the last element in directories 

The rest of the code should be pretty self explanatory :). Just look at the OOC and C++ example side by side.

So, how does one copy then?

To, copy, you just use Call and not MoveCall.

//Insert a copy of the substring into the vector
Call(directories, push_back, directory);

How does heck does this even work?

Magic

Magic macros.

Did you just say macros, the awful, undebuggable, copy paste preprocessor magic?

Yes.

In order to implement psuedo templating and have more usable and readable API, macros are necessary.

In the next few posts, we will dive into more examples and how OOC works internally.


Extra tid bits

Documentation

More examples

Current version is version 2 (OOC_V2) where the API has been simplified. If you see version 1 code (OOC_V1), the type is passed to every Call. By default, version 2 is enabled.

Snaipe implemented smart pointers in C with gcc extensions libcsptr. This is really awesome!

Go check out his post about his project. This could be used to create a RAII-like effect with OOC.


Object Oriented C Series:

Introduction