0

I’m having a problem with some C syntax and I’m hoping that someone knows the answer or knows of an article that explains a close solution that I can learn from. I know how to do this in a dozen other languages including multiple Assemblers, but I can’t figure out the C syntax (not C++, or C99+ must be C98 because of 5 operating systems support) to do this.

I have a library API that accepts a two dimensional array of "strings" N x 2 (N rows, 2 columns) with an integer with the number of rows. This array will almost always be defined as a constant within the calling application’s code because the information will almost always be known at compile time of that application. I have the code working that accepts the array and properly processes it, so the API actually works fine. The intent is to avoid memory allocation/deallocation for something that will never change until a new version of the calling application is released (i.e. I’m helping the application developers using the API by allowing them to use a compile time constant).

The problem I’m having is creating a comprehensive tester where I want to run a number of scenarios through the same API with slightly different content. In Java, JavaScript, COBOL, Ruby, Python, Assembler, etc.. it’s as "simple" as an array of arrays... in C, not so much.

The working API I have defined is:

CTS parse(const char* contents, size_t len, const char* app
            , const char* pVs[][2], const int pvLen);

And here’s the section of annoying code that I’m testing with; this first section works fine and tests the code properly, but if I want to add more variations the stupid case statement will become ugly(er) and moronic (which reasonably closely matches my skills in C at the moment :D):

int i = 0, f = 0, lf = 0, len = 0;
int lengths[] = {1, 3, 1, 1, 1};
char* filt1[][2] = { {"CS","5"},{"AS","5.4"},{"OS","16.*"} };
char* filt2[][2] = { {"CS","6.2"} };
char* filt3[][2] = { {"CS","5.4"} };
char* filt4[][2] = { {"CS","*"} };
char* filt5[][2] = { {"",""} };

for(i = 1; i < 6; i++) {         // Filter cases (filt 1=5)
     lf = lengths[i];
     for(f = 0; f < 6; f++) {   // Input files to be checked
           printf("Test #%i:\n", f);
           file = loadFile(f);
           len = (file == NULL ? 0 : strlen(file));
           switch(i) {                     // WTF Code!! stupid way to do this!
           case 1:
                l = parse(file, len, NULL, filt1, lf);
                break;
           case 2:
                l = parse(file, len, NULL, filt2, lf);
                break;
           case 3:
                l = parse(file, len, NULL, filt3, lf);
                break;
           case 4:
                l = parse(file, len, NULL, filt4, lf);
                break;
           case 5:
                l = parse(file, len, NULL, filt5, lf);
                break;
           }
           // Finish testing "l" result
           ...
     }
}

I’m curious to know how to rewrite the above to something like this (I don’t really care about the NULL option, I can leave that out of the group tests):

?????? filters[6] = {NULL, filt1, filt2, filt3, filt4, filt5};

for(i = 1; i < 6; i++) {
     lf = lengths[i];
     for(f = 0; f < 6; f++) {
           char* filter[][2] = filters[i];       // What goes here?

           printf("Test #%i:\n", f);
           file = loadFile(f);
           len = (file == NULL ? 0 : strlen(file));

           l = parse(file, len, NULL, filter, lf);

           // Finish testing "l" result
     }
}

Ignoring the NULL option, how do I build a (technically) 3 dimensional array of sparse content (i.e. Not all "depth" rows are the same dimensions) and pass it into the API function? I’ve been digging through StackOverflow for almost 2 days and no example ever works with char* or with what I want the receiving API signature to be like.

I've checked Error passing 2D char* array into a function but it only references fixed array sizes and only 2 dimensions.

Not a duplicate of How to pass 2D array (matrix) in a function in C? because that question deals with Integers and not "strings"/char*. Using the suggestions from that question results in code that won't compile as there is no way to cast the results coming out of the "list of tables"/3d array into a usable type that can be passed into the API.

millebi
  • 351
  • 4
  • 9
  • 2
    I think it's clear that what you really need is a `struct` instead of a complicated set of arrays. – Iharob Al Asimi Mar 01 '18 at 19:28
  • Possible duplicate of [How to pass 2D array (matrix) in a function in C?](https://stackoverflow.com/questions/3911400/how-to-pass-2d-array-matrix-in-a-function-in-c) – ack Mar 01 '18 at 19:44

1 Answers1

0

This is a good question. I hope someone can come up with the good answer. For me solution with the array of structs seems to be more clear and less complicated. If

typedef struct sar{
    char* (*f)[2];
}SAR

Then your loop simplifies to:

SAR ar[6];     
ar[0].f = NULL;
ar[1].f = filt1;   
ar[2].f = filt2; 
ar[3].f = filt3;  
ar[4].f = filt4; 
ar[5].f = filt5; 

for(i = 1; i < 6; i++) {
    lf = lengths[i];
    for(f = 0; f < 6; f++) {
       printf("Test #%i:\n", f);
       file = loadFile(f);
       len = (file == NULL ? 0 : strlen(file));
      l = parse(file, len, NULL, ar[i].f, lf);
       // Finish testing "l" result
    }
}

I did not have all the data for your program but with some simplification I verified the output.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct sar{
    char* (*f)[2];
}SAR;

int parse(const char* contents, size_t len, const char* app
            ,char* pVs[][2], const int pvLen)
{

    for(int i=0; i < pvLen; i++)
        printf("%s %s   ", pVs[i][0], pVs[i][1]);       
    printf("\n");       
    return 1;                
}

char * loadFile(int f)
{
    return NULL;
}

int main(void) 
{            
    char * file;
    int l;

    int i = 0, f = 0, lf = 0, len = 0;

    int lengths[] = {1, 3, 1, 1, 1};

    char* filt1[][2] = { {"CS","5"},{"AS","5.4"},{"OS","16.*"} };
    char* filt2[][2] = { {"CS","6.2"} };
    char* filt3[][2] = { {"CS","5.4"} };
    char* filt4[][2] = { {"CS","*"} };
    char* filt5[][2] = { {"",""} };

    for(i = 1; i < 6; i++) {         // Filter cases (filt 1=5)
         lf = lengths[i];

        for(f = 0; f < 6; f++) {   // Input files to be checked

           printf("Test #%i:\n", f);
           file = loadFile(f);
           len = (file == NULL ? 0 : strlen(file));
           switch(i) {                     // WTF Code!! stupid way to do this!
           case 1:
                l = parse(file, len, NULL, filt1, lf);
                break;
           case 2:
                l = parse(file, len, NULL, filt2, lf);
                break;
           case 3:
                l = parse(file, len, NULL, filt3, lf);
                break;
           case 4:
                l = parse(file, len, NULL, filt4, lf);
                break;
           case 5:
                l = parse(file, len, NULL, filt5, lf);
                break;
           }
           // Finish testing "l" result
           //...
        }
    }

    printf("............\n");

    SAR ar[6];

    ar[0].f = NULL;
    ar[1].f = filt1;   
    ar[2].f = filt2; 
    ar[3].f = filt3;  
    ar[4].f = filt4; 
    ar[5].f = filt5; 

    for(i = 1; i < 6; i++) {
        lf = lengths[i];
        for(f = 0; f < 6; f++) {
           printf("Test #%i:\n", f);
           file = loadFile(f);
           len = (file == NULL ? 0 : strlen(file));
          l = parse(file, len, NULL, ar[i].f, lf);
           // Finish testing "l" result
        }
    }

    return 0
}

Output:

Test #0:
CS 5   AS 5.4   OS 16.*   
Test #1:
CS 5   AS 5.4   OS 16.*   
Test #2:
CS 5   AS 5.4   OS 16.*   
Test #3:
CS 5   AS 5.4   OS 16.*   
Test #4:
CS 5   AS 5.4   OS 16.*   
Test #5:
CS 5   AS 5.4   OS 16.*   
Test #0:
CS 6.2   
Test #1:
CS 6.2   
Test #2:
CS 6.2   
Test #3:
CS 6.2   
Test #4:
CS 6.2   
Test #5:
CS 6.2   
Test #0:
CS 5.4   
Test #1:
CS 5.4   
Test #2:
CS 5.4   
Test #3:
CS 5.4   
Test #4:
CS 5.4   
Test #5:
CS 5.4   
Test #0:
CS *   
Test #1:
CS *   
Test #2:
CS *   
Test #3:
CS *   
Test #4:
CS *   
Test #5:
CS *   
Test #0:

Test #1:

Test #2:

Test #3:

Test #4:

Test #5:

............
Test #0:
CS 5   AS 5.4   OS 16.*   
Test #1:
CS 5   AS 5.4   OS 16.*   
Test #2:
CS 5   AS 5.4   OS 16.*   
Test #3:
CS 5   AS 5.4   OS 16.*   
Test #4:
CS 5   AS 5.4   OS 16.*   
Test #5:
CS 5   AS 5.4   OS 16.*   
Test #0:
CS 6.2   
Test #1:
CS 6.2   
Test #2:
CS 6.2   
Test #3:
CS 6.2   
Test #4:
CS 6.2   
Test #5:
CS 6.2   
Test #0:
CS 5.4   
Test #1:
CS 5.4   
Test #2:
CS 5.4   
Test #3:
CS 5.4   
Test #4:
CS 5.4   
Test #5:
CS 5.4   
Test #0:
CS *   
Test #1:
CS *   
Test #2:
CS *   
Test #3:
CS *   
Test #4:
CS *   
Test #5:
CS *   
Test #0:

Test #1:

Test #2:

Test #3:

Test #4:

Test #5:

I hope it helps.

sg7
  • 6,108
  • 2
  • 32
  • 40
  • Thanks for the solution, but I was hoping to learn some C syntax/trick that would allow the "raw" array instead of redesigning the API just to make testing easier. I had thought of a struct, but I like the simple definition that a typical API user would actually use with just a simple 2d array as input. Now if the "trick" is that "this is not the C way, annoying other developers is fine", I'm fine with using a struct. :) I'm a Noob to C, so if a struct is more common than a 2d array, Yay! – millebi Mar 02 '18 at 16:11
  • @millebi Struct is more clear here. Honestly, I tried to come up with an array approach but I did not succeed. The fact that one of the dimension is floating + C98 killed me. – sg7 Mar 02 '18 at 16:16