1

So I was experimenting with a few lines of code in C, and came across this problem. I have a structure with the following definition:

typedef struct menuScreen
{
    char *lines[MENU_MAX_LINES];
}menuScreen;

With that, I have a 2D character array declared as:

static char array1[][MENU_MAX_CHAR_PER_LINE] = {
    "Line 1",
    "Line 2",
    "Line 3",
    "Line 4"
};

Then I have a function:

void buildMenu(menuScreen *menu, const char lines[][MENU_MAX_CHAR_PER_LINE])
{
    int i = 0;
    for(i = 0; i < MENU_MAX_LINES; i++)
    {
        menu->lines[i] = (char *) lines[i];
    }
}

And finally a print function for the menu:

void printMenu(menuScreen *menu)
{
    int i = 0;
    for(i = 0; i < MENU_MAX_LINES; i++)
    {
        printf("%s\n", menu->lines[i]);
    }
}

In main.c:

    menuScreen selectMenu[2];
    buildMenu(&selectMenu[0], array1);
    printMenu(&selectMenu[0]);

Now everything works fine. Printing is fine as well. Then I decided to convert my character array as follows:

static char *array1[MENU_MAX_CHAR_PER_LINE] = {
    "Line 1",
    "Line 2",
    "Line 3",
    "Line 4"
};

and changed the function to:

void buildMenu(menuScreen *menu, const char **lines)
{
    int i = 0;
    for(i = 0; i < MENU_MAX_LINES; i++)
    {
        menu->lines[i] = (char *) lines[i];
    }
}

And finally in main.c, I call the functions as:

    menuScreen selectMenu[2];
    buildMenu(&selectMenu[0], (const char**) array1);
    printMenu(&selectMenu[0]);

And again, everything is great. Works as required.

Now, given all the above, I would like to know how to generalize the buildMenu() function so that I can pass both char[][] and char *[] in one function itself. Cant use function overloading as I am using C, and not C++. Maybe can be done with function pointers, but just want to know if it is possible by adjusting the argument to the function.

vinit320
  • 11
  • 3
  • Yeah. Seemed to be a typo. Have edited the original post. But still the question remains how to generalize it. – vinit320 Jun 10 '21 at 01:18
  • 1
    I don't think it's possible to generalize it the way you'd like. One the one hand, you have `char line[][N]`, which is adjusted to `char (*line)[N]`. On the other hand, you have `char *line[]`, which is adjusted to `char **line`. These are incompatible types. In particular, the latter requires one more memory access than the former. So any such function would need to know which style it was dealing with, and would need to access the array differently depending on the style. – Tom Karzes Jun 10 '21 at 01:49
  • Hmm. Makes sense. I was thinking similar. Well, guess I'll have to stick to one. Thanks. – vinit320 Jun 10 '21 at 02:18

1 Answers1

0

If your compiler supports C11, you can use _Generic to overload the function:

#define buildMenu(a,b) _Generic((&b), \
    char (*)[][MENU_MAX_CHAR_PER_LINE] : buildMenu1(a,(const char (*)[MENU_MAX_CHAR_PER_LINE])(b)), \
    char *(*)[MENU_MAX_CHAR_PER_LINE]  : buildMenu2(a,(const char**)(b)))

(See https://stackoverflow.com/a/43500229/6874310 and https://gustedt.wordpress.com/2012/05/19/type-generic-functions-taking-pointers-or-arrays/)

#include <stdio.h>

#define MENU_MAX_CHAR_PER_LINE (100)
#define MENU_MAX_LINES (4)

#define buildMenu(a,b) _Generic((&b), \
    char (*)[][MENU_MAX_CHAR_PER_LINE] : buildMenu1(a,(const char (*)[MENU_MAX_CHAR_PER_LINE])(b)), \
    char *(*)[MENU_MAX_CHAR_PER_LINE]  : buildMenu2(a,(const char**)(b)))

typedef struct menuScreen
{
    char *lines[MENU_MAX_LINES];

} menuScreen;

static char array1[][MENU_MAX_CHAR_PER_LINE] = {
    "array1_Line 1",
    "array1_Line 2",
    "array1_Line 3",
    "array1_Line 4"
};

static char *array2[MENU_MAX_CHAR_PER_LINE] = {
    "array2_Line 1",
    "array2_Line 2",
    "array2_Line 3",
    "array2_Line 4"
};

void buildMenu2(menuScreen *menu, const char **lines)
{
    int i = 0;
    for(i = 0; i < MENU_MAX_LINES; i++)
    {
        menu->lines[i] = (char *)lines[i];
    }
}

void buildMenu1(menuScreen *menu, const char lines[][MENU_MAX_CHAR_PER_LINE])
{
    int i = 0;
    for(i = 0; i < MENU_MAX_LINES; i++)
    {
        menu->lines[i] = (char *) lines[i];
    }
}

void printMenu(menuScreen *menu)
{
    int i = 0;
    for(i = 0; i < MENU_MAX_LINES; i++)
    {
        printf("%s\n", menu->lines[i]);
    }
}

int main()
{
    menuScreen selectMenu[2] = {0,};

    buildMenu(&selectMenu[0], array1);
    printMenu(&selectMenu[0]);

    printf("\n");

    buildMenu(&selectMenu[1], array2);
    printMenu(&selectMenu[1]);

    return 0;
}

This outputs:

array1_Line 1
array1_Line 2
array1_Line 3
array1_Line 4

array2_Line 1
array2_Line 2
array2_Line 3
array2_Line 4
Jardel Lucca
  • 1,115
  • 3
  • 10
  • 19