具有不同函数原型的函数查找表

时间:2016-02-16 18:54:40

标签: c function-pointers function-prototypes

根据用户输入调用指定函数的最佳方法是什么,除了ifstrcmp系列之外?

例如:

p 2 2 -> call func_p(2, 2)
a 8   -> call func_a(7)
m     -> call func_m(void)

我知道创建一个包含具有相同原型的函数指针的查找表是简单而优雅的,但是不同的原型呢?我想过在原型中使用...,但我不知道它是否是一个很好的解决方案。

1 个答案:

答案 0 :(得分:1)

  

定义所有函数,使它们采用单个数组参数。

     

Barmar的评论

将所有函数统一到相同的原型正是在这种情况下通常做的事情,尽管我会使用带有两个参数的原型:指向具有实际参数的数组的指针以及它的大小。这样,并非每个函数都必须自己拆分/解析它的参数。

我真的很喜欢这样的东西,所以我做了一个简短的演示。我在我的手机上做了这个,所以它有点粗糙,如果在野外使用(例如内存管理和错误检测)需要一些改进。这是:

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

// a node in the abstract syntax tree. Either a
// value or a call
struct Ast {
  bool isCall;
  union {
    int value;
    struct {
      char const * operator;
      size_t countOperands;
      struct Ast * operands;
    } call;
  };
};

// unified function type. Could've also passed an
// int array, but then evaluate would've needed
// a memory allocation, so ...
typedef int (*Function)(struct Ast *, size_t);


// implementation of + function. Sums the values of
// parameters. (which are hopefully evaluated)
int sum(struct Ast * parameters, size_t num) {
  int result = 0;
  while (num > 0) {
    --num;
    result += parameters [num]. value;
  }
  return result;
}

// implementation of ? function, ignores any
// parameters and just asks for an integer.
int ask (struct Ast * parameters, size_t num) {
  int value;
  scanf("%d", & value);
  return value;
}

// poor man's lookup table
static Function const functions [] = {sum, ask};
static char const * const function_names [] = {"+", "?"};

// poor man's lookup from above static arrays
Function lookup (char const * name) {
  size_t it = sizeof (functions) / sizeof (functions [0]);
  while (it > 0) {
    --it;
    if (strcmp(name, function_names [it]) == 0) {
      return functions [it];
    }
  }
  exit(1);
}

// evaluate an Ast. Normally one wouldn't return
// an Ast node but rather some value_t (assuming
// dynamic typing)
// this function is also destructive on call Ast nodes,
// in order to get around any memory management.
// so be careful!
struct Ast * evaluate (struct Ast * node) {
  if (! node->isCall) {
    // anything that's not a call is a value, thus
    // self evaluating, return it unchanged!
    return node;
  }
  // so it's a call. Get the associated function from
  // the lookup table!
  Function f = lookup(node->call.operator);
  // unconditionally evaluate all operands of the call.
  // thus no macros or conditionals, sorry!
  size_t o;
  for (o = 0; o < node->call.countOperands; ++o) {
    // destructive!
    node->call.operands[o] = *evaluate(&(node->call.operands[o]));
  }
  // use the call node to store the result value.
  // this will blow up if any call node uses any
  // allocated memory!
  node->isCall = false;
  // call the function with the evaluated operands and
  // store the result
  node->value = f(node->call.operands, node->call.countOperands);
  return node;
}

int main () {
  // I didn't want to write a parser, so here's a
  // static Ast of (+ 21 10 (?))
  struct Ast nodes [] = {
    {.isCall=false, .value=21},
    {.isCall=false, .value=10},
    {.isCall=true, .call = {
        .operator="?", .countOperands=0}},
    {.isCall=true, .call = {
        .operator="+", .countOperands=3,
        .operands=nodes}}};
  struct Ast * result = evaluate(&(nodes [3]));
  printf("(+ 21 10 (?)) => %d\n", result->value);
  return 0;
}

Written and "tested" on ideone.

另一种方法是使用带有某些函数类型信息的void *标记。但是将实际参数传递给像这样编码的函数是相当困难的,并且它也不能很好地扩展。