luohaha / CSpider

A scalable and convenient crawler framework in C:).
https://github.com/luohaha/CSpider
MIT License
368 stars 98 forks source link

所有的回调函数都建议添加一个void*型的user data。 #7

Closed wks closed 8 years ago

wks commented 8 years ago

原因是:用户提供的回调函数除了调用者提供的参数(如spider本身,以及url等),往往还有用户自己的上下文。比如,如果一个用户打算把所有的url写进一个文件,那么他会遇到麻烦:

void p(cspider_t *spider, char*url) {
  // 这个p函数如何得到main函数里创建的fp呢?
}

int main() {
  cspider_t *spider1 = init_cspider();
  cspider_t *spider2 = init_cspider();

  FILE *fp1 = open("my_url_list1.txt", "w");
  FILE *fp2 = open("my_url_list2.txt", "w");

  cs_setopt_process(spider1, p);  // 如何告诉spider1往fp1里写,
  cs_setopt_process(spider2, p);  // 而spider2往fp2里写呢?
}

建议所有的回调函数都多取一个void* user_data参数,这个参数的值在设定回调函数的时候指定。比如:

// 需要修改一下API

// 方便起见,单独定义callback的函数签名
typedef void (*ProcessFuncPtr)(cspider_t* spider, char* url, void* user_data);

// cs_setopt_process会在回调callback的时候把这里提供的user_data作为第三个函数传给callback
void cs_setopt_process(cspider_t *, ProcessFuncPtr callback, void* user_data);

// 下面是使用
struct my_context {
  FILE *fp;
};

void p(cspider_t *spider, char*url, void *user_data) {
  struct my_context *ctx = (struct my_context*)user_data;  // 转换一下类型
  fprintf(ctx->fp, "URL: %s\n", url);
}

int main() {
  cspider_t *spider1 = init_cspider();
  cspider_t *spider2 = init_cspider();

  FILE *fp1 = open("my_url_list1.txt", "w");
  FILE *fp2 = open("my_url_list2.txt", "w");

  struct my_context ctx1 = { .fp = fp1 };
  struct my_context ctx2 = { .fp = fp2 };

  cs_setopt_process(spider1, p, &ctx1);  // 都调用p,但让spider1传&ctx1作为第三个参数,
  cs_setopt_process(spider2, p, &ctx2);  // 而spider2传&ctx2作为第三个参数。

  fclose(fp1);
  fclose(fp2);
}
luohaha commented 8 years ago

非常非常感谢,确实没有考虑到这点。今晚有空就完善这个问题。再次感谢您的指点。

luohaha commented 8 years ago

已完善,再次感谢。