C语言的面向对象和多态

bC 语言也可以面向过程的;没有错。就是通过将结构体和指针结合,所以C 语言也可以是面向对象的。

案例:

这里void (*log_func)(logger* this, const char* msg);定义了一个名为log_func的函数指针,该函数接受一个类型为logger的指针和一个指向常量字符的指针作为参数,并且没有返回值。

/// log.h
#ifndef _LOG_H_
#define _LOG_H_

typedef struct logger logger;  
typedef void (*log_func)(logger* this, const char* msg);

typedef struct logger {
 log_func log;
} logger;

void common_log_init(logger** args);
void common_log_uninit(logger* args);

#endif  // _LOG_H_
/// log.c
#include "log.h"

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

static void common_log(logger* this, const char* msg) {
(void)this;
 printf("%s\n", msg);
}

void common_log_init(logger** args) {
 if (!args) {
   perror("args is nullptr");
   return;
}

 *args = (logger*)malloc(sizeof(logger));
 if (!*args) {
   perror("out of memory");
   return;
}
(*args)->log = common_log;
}

void common_log_uninit(logger* args) { free(args); }
/// main.c
#include "log.h"

int main() {
 logger* log;
 common_log_init(&log);
 log->log(log, "test msg");
 common_log_uninit(log);
 return 0;
}
Thinkpad:~/debug$ gcc log.c main.c -o log
Thinkpad:~/debug$ ./log
test msg

2、继承与多态

C 语言结构体也可以继承,用于复用代码。比如我们希望在 logger 能够记录模块信息,则可以继承 logger 结构体,复用 log 函数指针,然后添加新的成员。

// log.h
...

void mylog_init(logger** args, const char* module);
void mylog_uninit(logger* args);

...

/// mylog.c
#include <stdio.h>
#include <stdlib.h>

#include "log.h"

typedef struct mylog {
 logger base;
#define log base.log
 const char* module;
} mylog;

static void my_log(logger* this, const char* msg) {
 mylog* _mylog = (mylog*)(this);
 printf("[%s] %s\n", _mylog->module, msg);
}

void mylog_init(logger** args, const char* module) {
 if (!args) {
   perror("args is nullptr");
   return;
}

 mylog* _mylog = (mylog*)malloc(sizeof(mylog));
 if (!_mylog) {
   perror("out of memory");
   return;
}

 _mylog->log = my_log;
 _mylog->module = module;
 *args = (logger*)_mylog;
}

void mylog_uninit(logger* args) { free((mylog*)args); }
/// main.c
#include "log.h"

int main() {
 logger* log;
 mylog_init(&log, "main");
 log->log(log, "test msg");
 mylog_uninit(log);
 return 0;
}
@Thinkpad:~/debug$ gcc main.c mylog.c -o log
@Thinkpad:~/debug$ ./log
[main] test msg

我们可以将所有函数指针放到 vtable 中

/// log.h
...
typedef struct vtable {
 log_func log_info;
 log_func log_warn;
 log_func log_error;
 log_func log_fatal;
} vtable;

typedef struct logger {
 vtable log;
} logger;
...

3、dlopen/dlsym 实现多态

Linux 提供了如下 API 来动态装载库

#include <dlfcn.h>

void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);

dlopen 打开一个动态链接库,并返回动态链接库的句柄。

  • filename 是 so 库的加载路径,如果 filename 包含了路径符号 /,则 filename 被解释为相对或者绝对路径名,否则按照正常 so 搜索路径查查。

  • flag 必须是下列两个值中的一个:RTLD_LAZY 和 RTLD_NOW。RTLD_LAZY 表示延迟绑定,只有在需要的时候解析符号(symbol);RTLD_NOW 表示在 dlopen 返回前,将 so 库中所有符号。

dlsym 根据 so 库的操作句柄和符号,返回符号对应的地址(不仅可以获取函数地址,也可以获取变量地址)。

使用 dlopen 和 dlsym 可以在运行时加载 so,使用不用的实现。

/// log.h

#ifndef _LOG_H_
#define _LOG_H_

typedef struct logger logger;
typedef struct op_callback op_callback;
typedef void (*log_func)(logger* this, const char* msg);
typedef int (*init_func)(logger** this, void* arg);
typedef int (*uninit_func)(logger* this);
typedef int (*entry_func)(op_callback* callback);

typedef struct logger {
 log_func log_info;
 log_func log_warn;
 log_func log_error;
 log_func log_fatal;
} logger;

typedef struct op_callback {
 init_func init;
 uninit_func uninit;
} op_callback;

int log_entry(op_callback* callback);

#endif  // _LOG_H_
/// common_log.c

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

#include "log.h"

static void log_info(logger* this, const char* msg) {
(void)this;
 printf("[info] %s\n", msg);
}

static void log_warn(logger* this, const char* msg) {
(void)this;
 printf("[warn] %s\n", msg);
}

static void log_error(logger* this, const char* msg) {
(void)this;
 printf("[error] %s\n", msg);
}

static void log_fatal(logger* this, const char* msg) {
(void)this;
 printf("[fatal] %s\n", msg);
}

static int logger_init(logger** this, void* arg) {
(void)arg;
 if (!this) {
   perror("args is nullptr");
   return -1;
}

 *this = (logger*)malloc(sizeof(logger));
 if (!*this) {
   perror("out of memory");
   return -1;
}
(*this)->log_info = log_info;
(*this)->log_warn = log_warn;
(*this)->log_error = log_error;
(*this)->log_fatal = log_fatal;
 return 0;
}

static int logger_uninit(logger* args) {
 free(args);
 return 0;
}

int log_entry(op_callback* callback) {
 if (!callback) {
   perror("callback is nullptr");
   return -1;
}

 callback->init = logger_init;
 callback->uninit = logger_uninit;
 return 0;
}

// mylog.c

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

#include "log.h"

typedef struct mylog {
 logger base;
#define log_info base.log_info
#define log_warn base.log_warn
#define log_error base.log_error
#define log_fatal base.log_fatal
 const char* module;
} mylog;

static void log_info2(logger* this, const char* msg) {
 mylog* mylog_ = (mylog*)this;
 printf("[%s] [info] %s\n", mylog_->module, msg);
}

static void log_warn2(logger* this, const char* msg) {
 mylog* mylog_ = (mylog*)this;
 printf("[%s] [warn] %s\n", mylog_->module, msg);
}

static void log_error2(logger* this, const char* msg) {
 mylog* mylog_ = (mylog*)this;
 printf("[%s] [error] %s\n", mylog_->module, msg);
}

static void log_fatal2(logger* this, const char* msg) {
 mylog* mylog_ = (mylog*)this;
 printf("[%s] [fatal] %s\n", mylog_->module, msg);
}

int mylog_init(logger** this, void* arg) {
 if (!this) {
   perror("args is nullptr");
   return -1;
}

 mylog* _mylog = (mylog*)malloc(sizeof(mylog));
 if (!_mylog) {
   perror("out of memory");
   return -1;
}

 _mylog->log_info = log_info2;
 _mylog->log_warn = log_warn2;
 _mylog->log_error = log_error2;
 _mylog->log_fatal = log_fatal2;
 _mylog->module = (const char*)arg;
 *this = (logger*)_mylog;
 return 0;
}

static int mylog_uninit(logger* args) {
 free((mylog*)args);
 return 0;
}

int log_entry(op_callback* callback) {
 if (!callback) {
   perror("callback is nullptr");
   return -1;
}

 callback->init = mylog_init;
 callback->uninit = mylog_uninit;
 return 0;
}
/// main.c

#include <dlfcn.h>
#include <stdio.h>
#include <string.h>

#include "log.h"

static const char* mylog = "./libmylog.so";
static const char* commonlog = "./libcommonlog.so";

int main(int argc, const char* argv[]) {
 if (argc != 2) {
   printf("<%s> libmylog/libcommonlog\n", argv[0]);
   return -1;
}

 const char* lib = NULL;
 void* arg = NULL;
 if (!strcmp(argv[1], "libmylog")) {
   lib = mylog;
   arg = "main";
} else if (!strcmp(argv[1], "libcommonlog")) {
   lib = commonlog;
   arg = NULL;
} else {
   printf("not supported %s\n", argv[1]);
   return -1;
}

 void* handle = dlopen(lib, RTLD_LAZY);
 if (!handle) {
   printf("failed to load %s, %s\n", lib, dlerror());
   return -1;
}

 entry_func entry = dlsym(handle, "log_entry");
 if (!entry) {
   printf("failed to find log_entry, %s\n", dlerror());
   return -1;
}

 op_callback callback;
 if (entry(&callback)) {
   printf("faield to init callbakc\n");
   return -1;
}
 logger* log;
 callback.init(&log, arg);
 log->log_info(log, "test msg");

 callback.uninit(log);

 return 0;
}
@Thinkpad:~/debug$ gcc common_log.c -fPIC -shared -o libcommonlog.so
@Thinkpad:~/debug$ gcc mylog.c -fPIC -shared -o libmylog.so
@Thinkpad:~/debug$ gcc log.h main.c -o log -ldl
@Thinkpad:~/debug$ ./log libcommonlog
[info] test msg
@Thinkpad:~/debug$ ./log libmylog
[main] [info] test msg

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值