具有不同函数原型的函数查找 table
Function lookup table with different function prototypes
除了 if
和 strcmp
系列之外,根据用户输入调用指定函数的最佳方法是什么?
例如:
p 2 2 -> call func_p(2, 2)
a 8 -> call func_a(7)
m -> call func_m(void)
我知道查找由具有相同原型的函数指针组成的查找 table 既简单又优雅,但是不同原型呢?我考虑过在原型中使用 ...
但我不知道这是否是一个好的解决方案。
Define all the functions so they take a single array argument.
Comment from Barmar
在这种情况下,将所有函数统一到同一个原型正是人们通常所做的,尽管我会使用带有两个参数的原型:一个指向具有实际参数及其大小的数组的指针。这样,并非每个函数都必须 split/parse 自己的参数。
我真的很喜欢这样的东西,所以我做了一个简短的演示。我是在我的手机上制作的,所以它有点粗糙,如果在野外使用(例如内存管理和错误检测)需要一些改进。这是:
#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 *
。但是将实际参数传递给这样编码的函数是相当困难的,而且它也不能很好地扩展。
除了 if
和 strcmp
系列之外,根据用户输入调用指定函数的最佳方法是什么?
例如:
p 2 2 -> call func_p(2, 2)
a 8 -> call func_a(7)
m -> call func_m(void)
我知道查找由具有相同原型的函数指针组成的查找 table 既简单又优雅,但是不同原型呢?我考虑过在原型中使用 ...
但我不知道这是否是一个好的解决方案。
Define all the functions so they take a single array argument.
Comment from Barmar
在这种情况下,将所有函数统一到同一个原型正是人们通常所做的,尽管我会使用带有两个参数的原型:一个指向具有实际参数及其大小的数组的指针。这样,并非每个函数都必须 split/parse 自己的参数。
我真的很喜欢这样的东西,所以我做了一个简短的演示。我是在我的手机上制作的,所以它有点粗糙,如果在野外使用(例如内存管理和错误检测)需要一些改进。这是:
#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 *
。但是将实际参数传递给这样编码的函数是相当困难的,而且它也不能很好地扩展。