我再次阅读了这个问题,发现它比你描述的要简单得多。
根据您的说法,您已经知道execute()函数中数据检索的类型和顺序。这让这个问题变得更容易了。
我必须说,这个问题在c 中有点难以解决,因为c 无法在运行时解析类型或在运行时动态转换类型。 c 必须事先知道所有类型,即在编译时。
也就是说,c 提供了一种处理可变长度参数的方法。这是一个优势。
所以,我们要做的是:
- 缓存来自可变长度参数的所有参数,即 va_list。
- 并且,提供一种从该缓存中检索提供的参数的方法。
首先,如果您知道类型,我将向您展示如何从缓存中检索元素。我们将使用宏来完成。我将其命名为sarah_next()。好吧,毕竟,我要写它是因为你。你可以随意命名。其定义如下:
#define sarah_next(cache, type) \
(((cache) = (cache) + sizeof(type)), \
*((type*) (char *) ((cache) - sizeof(type))))
所以,简单来说,sarah_next() 从cache 中检索next element 并将其转换为type。
现在,让我们讨论第一个问题,我们必须缓存来自 va_list 的所有参数。您可以通过以下方式轻松完成:
void *cache = malloc(sizeof(char) * cacheSize);
// itr is an iterator, which iterates over cache
char *itr = (char *)cache;
// now, you can do
*(type *)itr = va_arg(buf, type);
// and then
itr += sizeof(type);
我想讨论的另一点是,我使用类型提示来确定缓存大小。为此,我使用了函数getSize()。如果您只看它就会明白(另请注意:这使您能够使用自己的自定义类型):
// getSize() is a function that returns type size based on type hint
size_t getSize(char type) {
if(type == 's') {
return sizeof(char *);
}
if(type == 'c') {
return sizeof(char);
}
if(type == 'i') {
return sizeof(int);
}
if(type == 'u') { // 'u' represents 'unsigned char'
return sizeof(unsigned char);
}
if(type == 'x') { // let's, say 'x' represents 'unsigned char *'
return sizeof(unsigned char *);
}
// you can add your own custom type here
// also note: you can easily use 'unsigned char'
// use something like 'u' to represent 'unsigned char'
// and you're done
// if type is not recognized, then
printf("error: unknown type while trying to retrieve type size\n");
exit(1);
}
好的,我想,这些想法已经完成了。在继续之前,请尝试正确掌握这些想法。
现在,让我提供完整的源代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
// note: it is the most fundamental part of this solution
// 'sarah_next' is a macro, that
// returns *(type *)buf means a value of type "type", and also
// increments 'buf' by 'sizeof(type)', so that
// it may target next element
// 'sarah_next' is used to retrieve data from task cache
// I've named it after you, you may choose to name it as you wish
#define sarah_next(cache, type) \
(((cache) = (cache) + sizeof(type)), \
*((type*) (char *) ((cache) - sizeof(type))))
// defining pool size for task pool
#define POOL_SIZE 1024
// notice: getSize() has been updated to support unsigned char and unsigned char *
// getSize() is a function that returns type size based on type hint
size_t getSize(char type) {
if(type == 's') {
return sizeof(char *);
}
if(type == 'c') {
return sizeof(char);
}
if(type == 'i') {
return sizeof(int);
}
if(type == 'u') { // 'u' represents 'unsigned char'
return sizeof(unsigned char);
}
if(type == 'x') { // let's, say 'x' represents 'unsigned char *'
return sizeof(unsigned char *);
}
// you can add your own custom type here
// also note: you can easily use 'unsigned char'
// use something like 'u' to represent 'unsigned char'
// and you're done
// if type is not recognized, then
printf("error: unknown type while trying to retrieve type size\n");
exit(1);
}
typedef struct __task {
int id;
void *cache;
} Task;
// notice: constructTask has been updated to support unsigned char and unsigned char *
// note: here, types contains type hint
Task *constructTask(int id, char *types, ...) {
// determine the size of task cache
int cacheSize = 0;
for(int i=0; types[i]; i++) {
cacheSize += getSize(types[i]);
}
// allocate memory for task cache
void *cache = malloc(sizeof(char) * cacheSize);
va_list buf;
va_start(buf, types);
// itr is an iterator, which iterates over cache
char *itr = (char *)cache;
for(int i=0; types[i]; i++) {
if(types[i] == 's') {
*(char **)itr = va_arg(buf, char *);
} else if(types[i] == 'x') { // added support for 'unsigned char *'
*(unsigned char **)itr = va_arg(buf, unsigned char *);
} else if(types[i] == 'c') {
// notice: i used 'int' not 'char'
// cause: compiler-warning: 'char' is promoted to 'int' when passed through '...'
// also note: this promotion helps with 'unsigned char'
*(char *)itr = (char)va_arg(buf, int); // so cast it to char
} else if(types[i] == 'u') { // added support 'unsigned char'
// notice: i used 'int' not 'unsigned char'
// cause: compiler-warning: 'unsigned char' is promoted to 'int' when passed through '...'
// also note: this promotion helps with 'unsigned char'
*(unsigned char *)itr = (unsigned char)va_arg(buf, int); // so cast it to unsigned char
} else if(types[i] == 'i') {
*(int *)itr = va_arg(buf, int);
}
// it won't come to else, cause getSize() would
// caught the type error first and exit the program
itr += getSize(types[i]);
}
va_end(buf);
// now, construct task
Task *task = malloc(sizeof(Task));
task->id = id;
task->cache = cache;
// and return it
return task;
}
// destroyTask is a function that frees memory of task cache and task
void destroyTask(Task *task) {
free(task->cache);
free(task);
}
// notice: that 'task->id == 4' processing part
// it is equivalant to your 'execute()' function
int taskProcessor(Task *task) {
// define ret i.e. return value
int ret = 999; // by default it is some code value, that says error
// note: you already know, what type is required in a task
if(task->id == 1) {
// note: see usage of 'sarah_next()'
int x = sarah_next(task->cache, int);
int y = sarah_next(task->cache, int);
ret = x + y;
} else if(task->id == 2) {
char *name = sarah_next(task->cache, char *);
if(strcmp(name, "sarah") == 0) {
ret = 0; // first name
} else if (strcmp(name, "cartenz") == 0) {
ret = 1; // last name
} else {
ret = -1; // name not matched
}
} else if(task->id == 3) {
int x = sarah_next(task->cache, int);
char *name = sarah_next(task->cache, char *);
int y = sarah_next(task->cache, int);
printf("%d %s %d\n", x, name, y); // notice: we've been able to retrieve
// both string(i.e. char *) and int
// you can also see for ch and int, but i can assure you, it works
ret = x + y;
} else if(task->id == 4) { // working with 'unsigned char *'
int a = sarah_next(task->cache, int);
unsigned char *x = sarah_next(task->cache, unsigned char *); // cast to unsigned char *
// char *x = sarah_next(task->cache, char *); // this won't work, would give wrong result
int b = sarah_next(task->cache, int);
printf("working with 'unsigned char *':");
for(int i=0; x[i]; i++) {
printf(" %d", x[i]); // checking if proper value is returned, that's why using 'integer'
}
printf("\n");
ret = a + b;
} else {
printf("task id not recognized\n");
}
return ret;
}
int main() {
Task *taskPool[POOL_SIZE];
int taskCnt = 0;
taskPool[taskCnt++] = constructTask(1, "ii", 20, 30); // it would return 50
taskPool[taskCnt++] = constructTask(1, "ii", 50, 70); // it would return 120
taskPool[taskCnt++] = constructTask(2, "s", "sarah"); // it would return 0
taskPool[taskCnt++] = constructTask(2, "s", "cartenz"); // it would return 1
taskPool[taskCnt++] = constructTask(2, "s", "reyad"); // it would return -1
taskPool[taskCnt++] = constructTask(3, "isi", 40, "sarah", 60); // it would print [40 sarah 60] and return 100
// notice: I've added an exmaple to showcase the use of unsigned char *
// also notice: i'm using value greater than 127, cause
// in most compiler(those treat char as signed) char supports only upto 127
unsigned char x[] = {231, 245, 120, 255, 0}; // 0 is for passing 'NULL CHAR' at the end of string
// 'x' is used to represent 'unsigned char *'
taskPool[taskCnt++] = constructTask(4, "ixi", 33, x, 789); // it would print ['working with unsigned char *': 231 245 120 255] and return 822
// note: if you used 'char *' cast to retrieve from 'cache'(using a compiler which treats char as signed), then
// it would print [-25 -11 120 -1] instead of [231 245 120 255]
// i guess, that makes it clear that you can perfectly use 'unsigned char *'
for(int i=0; i<taskCnt; i++) {
printf("task(%d): %d\n", i+1, taskProcessor(taskPool[i]));
printf("\n");
}
// at last destroy all tasks
for(int i=0; i<taskCnt; i++) {
destroyTask(taskPool[i]);
}
return 0;
}
输出是:
// notice the updated output
task(1): 50
task(2): 120
task(3): 0
task(4): 1
task(5): -1
40 sarah 60
task(6): 100
working with 'unsigned char *': 231 245 120 255
task(7): 822
因此,您可能想知道,与您给定的解决方案相比,它可能会产生什么优势。好吧,首先您不必使用%s %d 等确定格式,这不容易为每个任务更改或创建,并且您可能已经写了很多(对于每个任务,您可能必须写不同的fmt),并且你没有使用 vsprintf 等......它只处理内置类型。
第二点也是很重要的一点是,您可以使用自己的custom type。声明一个您自己的struct type 并且您可以使用。而且addnew type也很方便。
更新:
我忘了提到另一个优点,你也可以使用 unsigned char 。看,更新的getSize() 函数。 unsigned char 可以使用 'u' 符号,因为 unsigned char 被提升为 int,你可以将其转换为 (unsigned char) 并完成...
update-2(支持unsigned char *):
我已更新代码以支持unsigned char 和unsigned char *。为了支持新类型,您需要更新的功能是getSize() 和constructTask()。比较之前的代码和新更新的代码……你就会明白如何添加新类型(你也可以添加自己的自定义类型)。
另外,看看task->id == 4 部分在taskProcessor() 函数中。我添加了这个来展示unsigned char * 的用法。希望这能清除一切。
如果您有任何问题,请在评论中问我...