3

I really like to make tools prior to starting some assignment, so for the C programming assignments I have prepared dynamic array library. We are restrained to only using C99 standard.

Note that all programming I'm describing below is, as per question title, done for education purposes. This is not production code but code I use to practice programming. And AFAIK specifically the subject I'll be submitting the homeworks to has no external libraries rule anyway.

I will intentionally skip implementation details now, the "library" has following format (interface, if you wish):

typedef struct {

  //How many elements are in array
  // READ ONLY!!! Do not change
  size_t length;
  /** all the rest is private, do not touch **/
    ... omitted for brevity ...
} Array;

/** Creates new empty array. The returned pointer must be 
 * correctly destroyed using array_destroy.
 * Inital size is allowed to be zero
 *   **/
Array* array_create(size_t elm_size, size_t initalSize);
/**
 * Allocate memory so that `count` ENTRIES fits into the array.
 * If the array already has enough memory, nothing happens.
*/
void array_reserve(Array* a, size_t count);
/** Expands the array to required size. The value of
 * new fields is undefined. There is little reason to use
 * this instead of array_push or array_reserve */ 
void array_expand(Array* a, size_t length);
/**
 * Adds an element at the end of the array, realocating memory if necesary **/
void array_push(Array* a, const void* element);
/**
 * Sets value at offset in array. This ofset MUST be valid.
 * The `element` must be pointer to correct ammount of memory, that is
 * the correct type */  
void array_set(Array* a, size_t offset, const void* element);

Now this looked nice, but I started using it and it's not very fun. Simple arrays are OK I guess...

   int numbers[] = {5,4,3,2,1};
   Array* ar = array_create(sizeof(int), 0);
   // Oh, one thing that sucks is that you can't add literals
   // because you can't provide pointer to them
   array_push(ar, numbers+0);
   array_push(ar, numbers+1);
   ...
   array_push(ar, numbers+4);


   int number;
   // This is super cool macro
   // Remember this is gcc with -pedantic -Wall  -std=c99
   AR_FOREACH(number, ar, int) {
     printf("Number: %d\n", number);
   }

That prints:

Number: 5
Number: 4
Number: 3
Number: 2
Number: 1

But it really gets complicated with multidimensional arrays. I want to use my "library" for matrices. Now, just to make Array of Arrays and put one array inside, I have to do this:

   Array* array2d = array_create(sizeof(Array*), 0);
   // Matrix 3x3
   // first fill up
   for(short row = 0; row<3; ++row) {
     Array* tmp = array_create(sizeof(int), 3);
     array_push(array2d, &tmp);
     for(short col = 0; col<3; ++col) {
       array_push(tmp, numbers+row*3+col);
     }
   }
   // The double * here is just plain crazy...
   printf("Final dimensions: %d rows, %d columns.\n", array2d->length, (*(Array**)array_get(array2d, 0))->length);
   // Set some value at X Y
   const size_t x = 1;
   const size_t y = 1;
   const int value = 666;
   array_set(*(Array**)array_get(array2d, y), x, &value);

   Array* row = NULL;
   AR_FOREACH(row, array2d, Array*) {
     AR_FOREACH(number, row, int) {
         printf("%2d ", number);
     }
     printf("\n");
   }
   // Finally destroy all sub items and the array itself
   // Note that the foreach loop here helps a lot by already changing Array** to Array*
   AR_FOREACH(row, array2d, Array*) {
     array_destroy(*row);
   }

Note that I actually got several segfaults before I got this example to work. All of the errors came from me doing to many/not enough dereference operations. There were no errors from the library. The whole *(Array**)array_get(array2d, y) is totally crazy.

I want to use this library for programming simple matrix operations, but can I first somehow make it less verbose? The only thing that comes to mind is manually writing all array2d_XXX methods that work with two indexes.

Are there any C macros that could help me?

13
  • 1
    There are already nice packages (Blas, Lapack, etc.) which have proven matrix operations that are even optimized to run on your graphics hardware. Just use that and save yourself re-inventing the wheel.
    – user188153
    Commented Nov 15, 2016 at 22:08
  • 1
    This looks very suspicious to me. You are passing the address of a local variable to push. Does that copy elm_size bytes from the pointer? And does array get also copy said bytecount? The beauty of C is how the compiler blindly does what you say. Anyway, looks to me like it is still unfinished. Sadly, we neither write, debug, nor code review here, you might take it to code reviews SO.
    – Erik Eidt
    Commented Nov 15, 2016 at 22:32
  • 2
    I understand that, but I suspect that the casts you're complaining about are evidence of design defects, and I think that should be addressed first before providing a wrapper.
    – Erik Eidt
    Commented Nov 15, 2016 at 22:46
  • 2
    array_push(array2d, &tmp); Big red flag here - the call is passing the address of the pointer to a (void *). This would normally mean the called routine will be changing the parameter value - buts its impossible to tell without better interface comments, or looking at implementation of array_push what is expected and if this is correct. Therefore, the interface is broken.
    – mattnz
    Commented Nov 16, 2016 at 0:53
  • 2
    IMHO you are trying to implement something where you hit the limitations of the language, and that is actually the reason why it is hard to come up with a smoother API. Macros could help to a certain degree, but their usage is often a trade-off because they tend to introduce other problems. If you really want an easier-to-use API, you probably need to switch to C++ or another language.
    – Doc Brown
    Commented Nov 16, 2016 at 7:32

1 Answer 1

0

First, I believe that flexible array members are relevant for your task.

Then, you should use much more inline functions, that is static inline functions defined in some header file. They are in practice as readable as functions, and as fast as macros. BTW, it is common practice to have some header declaring static inline functions, and including itself another "private" or "implementation" header implementing them. Then the public header only declaring types and functions is quite readable.

It looks like you want to make a container library for C. See this question. You could get inspiration from SGLIB (code here) which has several quite long macros expanded to several definitions (and creating new symbols using preprocessor macro concatenation).

Did you thought of having a push function returning the new array, or modifying an existing one? E.g. ar = array_push(ar, numbers+1); or array_push(&ar, numbers+1); ?

BTW, if it is some homework, you might ask your teacher if you are allowed to use existing containers libraries, which IMHO is much better than making your own. Designing and implementing ex nihilo a good reusable container library in C will take you at least several months (and perhaps even several years) of work.

At last, I am not sure that matrices should be implemented as dynamic arrays of dynamic arrays (because that does not enforce the property that all rows have the same length). I would consider instead having matrices as some abstract data type, and have a matrix_get(m,i,j) operation to get matrix element mi, j (probably some static inline function giving m->array[m->width*i+j] - maybe after some validity tests checking m, i, and j).

Another possible approach is some metaprogramming: you'll write some script (perhaps using some generic macroprocessor like GPP or m4) or program emitting some C code suited for your needs (for examples of C code generators, see rpcgen & bison). So given a type name element_t (perhaps typedef double element_t;) your generator would emit the code for matrices of such type, and the generated code would include some well typed matrix_get_element(m,i,j) function or macro. So if you give double as an argument or input to your generator, it would generate some matrix_double_t type and the C code of matrix_get_double(matrix_double_t*m, int i, int j).

BTW, SICP is a very useful book to read (it is an excellent and freely available introduction to programming, and will give you a lot of excellent concepts and terminology) even if it is not about C.

Also, to iterate inside containers, you could either have some iterator abstract type, or use some callback approach (since C does not have closures). Look (at least for inspiration) into Glib data types (code here).

PS. For a homework, if your teacher allows it (and IMHO he should allow that), you really should better use some existing container library (this answer links to several of them) and focus on the homework problem itself. If you are not allowed to use existing stuff, be sure to look for inspiration inside the code of existing free software libraries similar to your homework, and mention them. So, look at least inside the code of SGLIB and of Glib before coding your own thing (and be sure to cite them).

Don't forget to compile with all warnings & debug info (gcc -Wall -Wextra -g if using GCC on Linux) and use valgrind

1
  • 1
    This doesn't directly answer my question, but I was seeking education and this answer contains plenty of it. Also, according to comments under my question, what I seek is almost - if not completely - impossible because it's simply outside the C's capabilities. Commented Nov 16, 2016 at 23:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.