1shekhar / C-Macro-Collections

Header only, macro generated, generic and type-safe Collections in C

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

C-Macro-Collections

Generate Simple Data Structures of any type in C for you to use in your projects.

License Version Build codecov

Project Structure

  • benchmarks - Where all benchmarks are hosted
  • examples - Examples separated by collections
  • src - All headers part of the C Macro Collections Library
    • cmc - The main C Macro Collections Library
    • dev - The main C Macro Collections Library for development (containing logging)
    • ext - Extra collections
    • sac - Stack Allocated Collections
    • utl - Utility like ForEach macros, logging, etc
    • macro_collections.h - Master header containing all collections and utilities
  • tests - Where all tests are hosted

Only Collections located in cmc are to be documented at Documentation.md.

Available Collections

  • Main C Macro Collections Library
    • Deque
    • HashMap
    • HashSet
    • Heap
    • LinkedList
    • List
    • Queue
    • Stack
    • TreeMap
    • TreeSet
  • Extra Collections Library
    • IntervalHeap
    • LinkedQueue
    • MultiMap
    • MultiSet
  • Stack Allocated Collections Library
    • Queue
    • Stack
  • Development Collections Library
    • Deque

Check out Documentation.md for more information about each collection.

Design Decisions

Stack vs Heap Allocation

Currently all collections need to be allocated on the heap. Iterators have both options but it is encouraged to allocate them on the stack since they don't require dynamic memory.

Some collections overlap others in terms of functionality

Yes, you can use a Deque as a Queue or a List as a Stack without any major cost, but the idea is to have the least amount of code to fulfill the needs of a collection.

Take for example the Stack. It is simple, small and doesn't have many functions. If you generate a List to be used (only) as a Stack (which is one of the bulkiest collections) you'll end up with a lot of code generated and compiled for nothing.

The Deque versus Queue situation is a little less problematic, but again, you need to be careful when generating a lot of code as compilation times might go up to 15 seconds even with modern ultra-fast compilers.

Another example is using a HashMap/TreeMap as a HashSet/TreeSet (with a dummy value that is never used), but I just think that this is a bad thing to do and you would be wasting some memory. Also, the sets generate a lot of code related to set theory, whereas maps don't.

But what about the LinkedList ?

You can use them as Stacks, Queues and Deques, but with modern memory hierarchy models, array-based data structures have a significantly faster runtime due to caching, so I didn't bother to have specific implementations of those aforementioned collections.

You can't structurally modify a collection when iterating over it

Modifying a collection will possibly invalidate all iterators currently initialized by it. Currently, the only collection that allows this is the LinkedList (using the node-based functions, not the iterator).

What to use

The following table shows how each collection is implemented and how well they do when using as common abstract data types.

  • Ideal - The collection implements correctly the abstract data type;
  • Not Ideal - The implementation is fulfilled but some functionalities are either not part of the ADT or not present;
  • Bad - It can be done, but its a bad idea.

DataStructuresDiagram

GoodColor AverageColor BadColor

How to use

To generate the collection, all you need to do is to include the necessary header files. You can include the containers you want to use individually or you can include the master header, macro_collections.h, that comes with all collections and other functionalities like the FOR_EACH macro.

There are four C standard headers that all collections use:

  • <stdbool.h>
  • <stdio.h>
  • <stdlib.h>
  • <string.h>

With the last one being used only by array based containers. These headers are already included by default.

Macros

Note here that SNAME represents the uppercase name of the collection.

Every collection is separated by two parts:

  • HEADER - Contains all struct definitions and function definitions.
  • SOURCE - Contains all function implementations.

All collections have three main macros:

  • SNAME_GENERATE - Generates SNAME_GENERATE_HEADER and SNAME_GENERATE_SOURCE.

Or you can generate each part individually:

  • SNAME_GENERATE_HEADER - Generates all struct definitions and function definitions.
  • SNAME_GENERATE_SOURCE - Generates all function implementations.

For Each

There are 2 for-each macros:

  • FOR_EACH - Starts at the start of the collection towards the end.
  • FOR_EACH_REV - Starts at the end of the collection towards the start.

Check out the Documentation.md to know exactly what represents the end and the start of each collection.

Parameters

When including macro_collections.h in your source code you gain access to a macro called COLLECTION_GENERATE with the following parameters:

  • C - Container name in uppercase (LIST, LINKEDLIST, STACK, QUEUE, DEQUE, HEAP, TREESET, TREEMAP, HASHSET, HASHMAP).
  • PFX - Functions prefix or namespace.
  • SNAME - Structure name (typedef struct SNAME##_s SNAME).
  • FMOD - Function modifier (static or empty).
  • *K - Key type. Only used in HASHMAP and TREEMAP; ignored by others.
  • V - Value type. Primary type for most collections, or value to be mapped by HASHMAP and TREEMAP.

* Required only by HASHMAP and TREEMAP

In fact, all macros follow this pattern. So whenever you see a macro with a bunch of parameters and you don't know what they are, you can check out the above list.

When including foreach.h in your source code you gain access to all for-each macros with the following parameters:

  • PFX - Functions prefix or namespace.
  • SNAME - Structure name.
  • TARGET - The variable name of the collection you wish to iterate over.
  • BODY - Block of code.

Inside body you will have access to the iterator variable. With it you can use functions to access the key, value or index from the iterator. Checkout the documentation for more details.


Check out some code reviews that covers some parts the project:

About Link
Unit Test ./utl/test.h Code Review
Interval Heap ./ext/intervalheap.h Code Review
Hash Set ./cmc/hashset.h Code Review
Linked List ./cmc/linkedlist.h Code Review
Others Code Review

About

Header only, macro generated, generic and type-safe Collections in C

License:MIT License


Languages

Language:C 99.9%Language:Makefile 0.1%