3

When I was making my terminal i was wondering if I can call a function by array. (This code is not done yet so please code is a bit messy.)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <unistd.h>
#include <limits.h>
#define true 1
#define false 0
typedef int bool;

/* Static */
static char Input[CHAR_MAX];
static char CurrentDirectory[CHAR_MAX];
static char *Command;
static char *Argument;
static char *Commands[]={"test","test2"};

/* Functions */
int Check_Command();
int test();
int test2();
/* --------- */

int main(){
    printf("#######################\n\tterminal\n\tType \"help\" for the list of commands\n#######################\n");
    prompt:
    printf(">");
    fgets(Input,CHAR_MAX,stdin);
    int res=Check_Command();
    if(res==0){printf("Unknown Command!\n");}
    goto prompt;
}
/* Check_Command() function returns 0 if doesn't suceed and returns 1 of it suceeds */
int Check_Command(){
    //Since input variable is static, no need to send in arguments
    Input[strcspn(Input,"\r\n")]=0;
    Command=strtok(Input," ");
    Argument=strtok(NULL," ");
    int x=0;
    while(x<sizeof(Commands)){
        if(strcmp(Command,Commands[x])==0){
            Commands[x](); <----- Can I call a function like this?
            return 1;
        }
        x++;
    }
    return 0;
}

/* Commands */
int test(){
    printf("Success!\n");
    getchar();
    exit(0);
}

int test2(){
    print("Success [2] \n");
    getchar();
    exit(0);
}

If this possible then this would be lit, Im too lazy to make commands into a executable and using if statements for all commands. if you are too lazy to read the whole code here is a basic concept (UNTESTED):

static *Commands[]={"test","test2"};
int main(){
    char *Command="test";
    int x=0;
    while(x<sizeof(Commands)){
        if(strcmp(Command,Commands)==0){
            Commands[x]();
        }
        x++
    }
}
int test(){
   printf("Hi");
}
int test2(){
    printf("hey");
}

Edit:

static char Commands[]={test,test2}; DOES NOT WORK This also includes the "possible duplicate" answer. (Im using Mingw, Windows 10)

  • 1
    Don't forget that the `sizeof` operator returns the size in ***bytes***. That means your loop condition `x – Some programmer dude Nov 03 '18 at 18:01
  • As for your problem, do some research about *function pointers*, and optionally (but recommended) about type-aliases using `typedef`. With that, it should be easy to create a second array for the functions to call. – Some programmer dude Nov 03 '18 at 18:02
  • @OldProgrammer I've bumped into that answer before I asked this question but I felt like it doesn't look like my answer. But I respect you thinking it was a duplicate. –  Nov 03 '18 at 18:06
  • 1
    regarding:`#define true 1 #define false 0 typedef int bool;` much better to just use the statement `#include ` – user3629249 Nov 04 '18 at 00:14

1 Answers1

2

It appears that you want to be able to take in a string such as test2 from the user, and then invoke the function test2(). There are two main ways you can approach this:

  1. Homebrew structure mapping names to function pointers.
  2. Using 'dynamic library loading' and function name resolution.

Array of structures

For the first, you define a structure such as:

struct FuncName
{
    const char *name;
    int (*function)(void);
};

And you can then define an array of these:

struct FuncName functions[] =
{
    { "test",  test  },
    { "test2", test2 },
};
enum { NUM_FUNCTIONS = sizeof(functions) / sizeof(functions[0]) };

When you get a name from the user, you can search through the array of names and find the matching function pointer to call.

int invoke_function(const char *name)
{
    for (int i = 0; i < NUM_FUNCTIONS; i++)
    {
        if (strcmp(name, functions[i].name) == 0)
        {
            return (*functions[i].function)();
            // Or just: return functions[i].function();
        }
    }
    return -1;   // No match found
}

This works reliably on all systems, but the demerit is that you must create the table of function pointers when you compile the program.

Dynamic library

The alternative is to use functions dlopen() and dlsym() from the <dlsym.h> header on Unix (POSIX) systems, or the equivalent on Windows.

Normally, you expect to find the functions in dynamically loaded libraries loaded with dlopen(), but there's usually a way to search the main executable for the names instead (pass a null pointer as the file name to dlopen() on POSIX systems). You can then call dlsym() to get the function pointer corresponding to the name you specify, which you can call.

void *dlh = dlopen(NULL, RTLD_NOW);

int (*funcptr)(void) = (int (*)(void))dlsym("test", dlh);

return (*funcptr)();

This omits error checking and you need the cast to convert from an object pointer (void *) to a function pointer because the C standard does not require that to be doable, but POSIX does (see the specification of dlsym() already linked to).

Non-uniform function signatures

With both solutions, life is easy if all the callable functions have the same interface. Life is much messier if the different functions have different interfaces (so some expect no arguments, some expect one, some expect two, and the types of the arguments vary between functions, as do the return types). Expect to use lots of casts and be prepared to bludgeon the compiler into submission — isolate the code from everything else so as to leave the non-portable part well separated from the main code.

Beware: no compiler was consulted about the validity of any of this code!

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    You need `functions[i].function()` – Craig Estey Nov 03 '18 at 19:02
  • @CraigEstey: You're so right — thanks for the correction. The 'Beware' footnote was there for a purpose; your comment illustrates that purpose. – Jonathan Leffler Nov 03 '18 at 19:04
  • I had built a tested/working version that did the same thing. I was creating my answer when you posted, so I didn't feel the need to duplicate. – Craig Estey Nov 03 '18 at 19:09
  • When i get home i will test this. –  Nov 03 '18 at 23:24
  • Yep, your solution works! I was a bit conscious and was trying my hardest not to copy your code because I wanna learn something new. –  Nov 04 '18 at 01:39
  • The struct's are similar, just renamed it and stuff but changed the for loop into a while loop. ( while(x<=LOC){ if(strcmp(Command,Existing_Commands[x].Expected_Command_Call)==0){ Existing_Commands[x].function(); return 1; } –  Nov 04 '18 at 01:40