Я прочитал задачу еще раз и обнаружил, что она намного проще, чем вы описали.
Согласно вашему утверждению, вы уже знаете о типе и порядке извлечения данных в 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()
извлекает next element
из cache
и преобразует его в 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);
}
Хорошо, я думаю, идеи завершены. Прежде чем двигаться дальше, попробуйте asp тщательно продумать идеи.
Теперь позвольте мне предоставить полный исходный код:
#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
et c. определите формат, который нелегко изменить или создать для каждой задачи, и вы можете написать много (для каждой задачи вам, возможно, придется писать разные fmt
), и вы не используете vsprintf et c ... который имеет дело только со встроенными типами.
И второй важный момент - вы можете использовать свой собственный custom type
. Объявите свой собственный struct type
, и вы сможете его использовать. И это также легко add
new type
.
update:
Я забыл упомянуть еще одно преимущество, вы также можете использовать с ним unsigned char . видите, обновленная функция getSize()
. вы можете использовать символ 'u' для unsigned char
, а поскольку unsigned char
повышается до int
, вы можете просто преобразовать его в (unsigned char)
и сделать ...
update-2 (поддержка unsigned char *
):
Я обновил код для поддержки unsigned char
и unsigned char *
. Для поддержки нового типа вам необходимо обновить функции getSize()
и constructTask()
. Сравните предыдущий код и недавно обновленный код ... вы поймете, как добавлять новые типы (вы также можете добавлять свои собственные типы).
Также обратите внимание на task->id == 4
часть в taskProcessor()
функция. Я добавил это, чтобы продемонстрировать использование unsigned char *
. Надеюсь, это все проясняет.
Если у вас есть вопросы, задавайте их в комментариях ...