最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言實現(xiàn)信號槽的項目實踐

 更新時間:2024年04月02日 10:20:37   作者:明月清風舊  
信號槽是觀察者模式的一種實現(xiàn),一個信號就是一個能夠被觀察的事件,本文主要介紹了C語言實現(xiàn)信號槽的項目實踐模具有一定的參考價值,感興趣的可以了解一下

一.前言

前段時間使用qt做了個項目,了解到信號槽這種機制,在解耦合和程序設計的方便程度上體現(xiàn)出極度的優(yōu)雅與從容.

現(xiàn)在回到單片機程序開發(fā)上來,感受到之前的單片機程序寫的還不夠優(yōu)雅,使用函數(shù)指針能夠實現(xiàn)解耦合,但需要設置回調函數(shù),而且不同的模塊設計的回調函數(shù)不盡相同,最大的痛點是實現(xiàn)異步調用非常復雜,與優(yōu)雅二字基本是毫無關聯(lián)了.

網上查閱了一遍,發(fā)現(xiàn)別人設計的信號槽要么只支持同步調用,要么實現(xiàn)過程復雜,代碼量巨大,要么代碼量巨大還只支持同步調用,要么居然還要自己去實現(xiàn)信號,信號不是給個函數(shù)聲明就行了嗎?還有的是閉源代碼,好像是需要購買?

介于以上原因,本人打算自己實現(xiàn)信號槽機制,應用于單片機編程.實現(xiàn)的完整代碼不到300行,支持同步異步混合調用,一對多多對一多對多等.當然,本文程序也有局限性,就是只支持單片機.因為只測試了單片機.

二.平臺及運行環(huán)境

CPUSTM32F407
內存128K+64K
操作系統(tǒng)rt-thread
編譯器keil
moc腳本python

對單片機的配置還是有一定的要求,內存太小的話能支持的信號和槽的數(shù)量就會有限制,使用體驗上就會捉襟見肘;由于信號槽調用比回調函數(shù)速度慢,在信號槽數(shù)量多的情況下如果CPU算力不夠可能會影響業(yè)務邏輯.本文代碼不會限制信號和槽的數(shù)量,只要內存夠.
由于異步調用需要操作系統(tǒng)支持,本文程序基于rt-thread,如需支持其他操作系統(tǒng),需要做相應修改

三.代碼實現(xiàn)

1.定義相關數(shù)據(jù)類型

在 signal.h 文件中聲明數(shù)據(jù)類型如下:

// 聲明信號和發(fā)送宏
#define signal void
#define emit

// 槽列表
typedef struct _slot_list{
  struct _slot_list *next;// 下一個槽
  void *fun;// 槽函數(shù)
  void *mb;// 槽函數(shù)運行的線程
  void *obj;// 槽函數(shù)所在類
}slot_list;

// 信號
typedef struct{
  const char *name;// 信號名
  const char *file;// 信號所在文件
  void *signal_;// 信號
  slot_list *head;// 槽列表
}signal_def;

// 消息
typedef struct{
  void *fun;// 要執(zhí)行的函數(shù)
  void *src;// 消息源
  int param_num;// 參數(shù)個數(shù)
  uint32_t param[0];// 參數(shù)列表
}slot_msg_def;

// 異步調用
typedef struct{
  void *mb;
  int run;
}slot_run_def;

// 線程
typedef void * sig_thread;

2.信號定義相關

為了使系統(tǒng)知道程序中有哪些信號,還需要在信號的定義上下功夫,既要使程序能正常執(zhí)行,又需要使用戶在編程使又只需要聲明信號,而不需要去實現(xiàn).本文程序仿照qt中的做法,使用moc腳本來自動生成信號的實現(xiàn).
在 signal.h 中聲明宏:

// 這個宏用于聲明信號,聲明了之后系統(tǒng)就能找到這個信號
// 這個宏用戶編程時不需要使用
#define signal_export(name_) \
  const static char __sig_##name_##_name[] SECTION(".rodata.sigstr") = #name_; \
  const static char __sig_##name_##_file[] SECTION(".rodata.sigstr") = __FILE__; \
  RT_USED static signal_def _signal_##name_ SECTION("signalstruct")= \
  {\
    .name=__sig_##name_##_name,\
    .file=__sig_##name_##_file,\
    .signal_=name_,\
    .head=0,\
  };

信號聲明示例:
用戶編程時只需要在需要使用信號的頭文件中,使用 signal 關鍵字聲明信號原型即可,moc腳本會自動生成信號的實現(xiàn),保證c語言程序結構正常.如下:

#include "signal.h"

signal pmcu_test_signal(int *a,int b);

信號實現(xiàn)示例:
在此展示一下moc腳本生成的信號實現(xiàn)長什么樣,這部分用戶使用過程中無需關心:

#include "stdlib.h"
#include "signal.h"

void pmcu_test_signal(int *a,int b)
{
  uint32_t *param=malloc(sizeof(uint32_t)*2);
  param[0]=(uint32_t)a;
  param[1]=(uint32_t)b;
  _signal_emit(pmcu_test_signal,param,2);
  free(param);
}
signal_export(pmcu_test_signal);

3.相關函數(shù)實現(xiàn)

signal.c 文件實現(xiàn)如下:
其中 param_check(s) 是斷言 參數(shù)s是否為0的宏

#include "signal.h"
#include "board.h"
#include "stdlib.h"
#include "stdio.h"

typedef struct{
  void *mutex;
}self_def;

static self_def g_self;

// 在系統(tǒng)啟動時初始化信號量系統(tǒng)
int signal_init(void)
{
  self_def *s=&g_self;
  if(s->mutex==0)
  {
    s->mutex=rt_mutex_create("signal_",RT_IPC_FLAG_FIFO);
  }
  return 0;
}

// 4字節(jié)拷貝
static void cpy4byte(uint32_t *dst,uint32_t *src,int num_4byte)
{
  for(int i=0;i<num_4byte;i++)
  {
    dst[i]=src[i];
  }
}

// 調用這個宏執(zhí)行槽函數(shù)
#define SLOT_FUN_RUN(fun,param) \
  ((slot_fun_def)(fun))(param[0],param[1],param[2],\
    param[3],param[4],param[5],param[6],param[7])

// 槽函數(shù)定義
typedef void (*slot_fun_def)(uint32_t a,uint32_t b,uint32_t c,uint32_t d,uint32_t e,uint32_t f,uint32_t g,uint32_t h);

// 異步調用線程
static void slot_run(void *t)
{
  param_check(t);
  slot_run_def *s=t;
  int msg_size=sizeof(slot_msg_def)+sizeof(uint32_t)*8;
  slot_msg_def *msg=malloc(msg_size);
  while(s->run)
  {
    rt_mq_recv(s->mb,msg,msg_size,RT_WAITING_FOREVER);
    SLOT_FUN_RUN(msg->fun,msg->param);
  }
  free(msg);
}

// 創(chuàng)建一個線程
sig_thread thread_creat(int pro)
{
  static int count=0;
  char name[20]={0};
  slot_run_def *run=malloc(sizeof(slot_run_def));
  run->run=1;
  sprintf(name,"sig_mq#%d",count);
  run->mb=rt_mq_create(name,(sizeof(slot_msg_def)+sizeof(uint32_t)*8),20,RT_IPC_FLAG_FIFO);
  sprintf(name,"sig_t#%d",count);
  rt_thread_t rt_t=rt_thread_create(name,slot_run,run,2048,pro,20);
  rt_thread_startup(rt_t);
  count++;
  return run->mb;
}

void thread_delete(sig_thread t)
{
  // 刪除線程需要刪除與此線程相關的所有信號槽
  // 刪除消息隊列
  param_check(0);
}

// 連接信號和槽參數(shù)分別為:信號,槽線程,槽所在類,槽函數(shù)
int connect(void *signal_,sig_thread t,void *slot_obj,void *slot)
{
  self_def *s=&g_self;
  signal_def *sig;
  sig=signal_find(signal_);
  if(sig==0) return -1;
  rt_mutex_take(s->mutex,RT_WAITING_FOREVER);
  slot_list *slo=malloc(sizeof(slot_list));
  param_check(slo);
  slo->fun=slot;
  slo->mb=t;
  slo->next=0;
  slo->obj=slot_obj;
  if(sig->head==0)
  {
    sig->head=slo;
  }
  else{
    slot_list *next=sig->head;
    while(next->next!=0)
    {
      next=next->next;
    }
    next->next=slo;
  }
  rt_mutex_release(s->mutex);
  return 0;
}

// 信號和槽斷開連接,參數(shù)同上
int disconnect(void *signal_,sig_thread t,void *slot_obj,void *slot)
{
  self_def *s=&g_self;
  signal_def *sig;
  sig=signal_find(signal_);
  if(sig==0) return -1;
  rt_mutex_take(s->mutex,RT_WAITING_FOREVER);
  slot_list *next=sig->head;
  slot_list *prev=0;
  while(next!=0)
  {
    if(next->fun==slot&&next->mb==t&&next->obj==slot_obj)
    {
      if(prev) prev->next=next->next;
      else sig->head=next->next;
      free(next);
    }
    prev=next;
    next=next->next;
  }
  rt_mutex_release(s->mutex);
  return -1;
}

// 查找信號結構體
signal_def *signal_find(void *signal_)
{
  extern const int signalstruct$$Base;
  extern const int signalstruct$$Limit;
  signal_def *start=(signal_def *)&signalstruct$$Base;
  signal_def *end=(signal_def *)&signalstruct$$Limit;
  for(signal_def *t=start;t<end;t++)
  {
    if(t->signal_==signal_)
    {
      return t;
    }
  }
  return 0;
}

// 發(fā)送信號,這個函數(shù)用戶無需關心
// 用戶編程發(fā)送信號使用 emit signal_fun(); 語法即可
int _signal_emit(void *signal_,uint32_t *param,int param_num)
{
  self_def *s=&g_self;
  signal_def *sig=signal_find(signal_);
  if(sig==0) return -1;
  if(param_num>7) return -2;
  int size=sizeof(slot_msg_def)+sizeof(uint32_t)*(8);
  slot_msg_def *m=malloc(size);
  rt_mutex_take(s->mutex,RT_WAITING_FOREVER);
  slot_list *h=sig->head;
  m->param_num=param_num;
  m->src=signal_;
  while(h)
  {
    m->fun=h->fun;
    if(h->obj)
    {
      cpy4byte(m->param+1,param,param_num);
      m->param[0]=(uint32_t)h->obj;
    }else{
      cpy4byte(m->param,param,param_num);
    }
    if(h->mb){
      rt_mq_send(h->mb,m,size);
    }else{
      SLOT_FUN_RUN(m->fun,m->param);
    }
    h=h->next;
  }
  rt_mutex_release(s->mutex);
  free(m);
  return 0;
}

signal.h 文件中添加函數(shù)聲明:

sig_thread thread_creat(int pro);
void thread_delete(sig_thread t);
signal_def *signal_find(void *signal_);
int connect(void *signal_,sig_thread t,void *slot_obj,void *slot);
int disconnect(void *signal_,sig_thread t,void *slot_obj,void *slot);
int signal_init(void);

// 這個函數(shù)用戶無需關心
int _signal_emit(void *signal_,uint32_t *param,int param_num);

四.moc腳本實現(xiàn)

moc腳本的工作就是定義信號函數(shù),在每次編譯之前必須調用moc腳本,否則編譯報錯.完整代碼如下:

import os

MOD_FILE="mod_signals.c"
MOD_PATH="./source/task/"

def find_file(path:str,fix:str):
    file_list=[]
    for file_name in os.listdir(path):
        if file_name.endswith(fix):
            file_list.append(os.path.join(path, file_name))
    return file_list

def find_signal_def(file):
    list_signal=[]
    with open(file) as f:
        list_str=f.readlines()
        for i in list_str:
            if(i[0:6]=="signal"):
                list_signal.append(i)
    return list_signal

# 截取變量名
def split_par_name(par_str:str):
    ret_str=""
    if(par_str.count('*')>0):
        ret_str=par_str.split("*")[1].strip()
    else:
        ret_str=par_str.split(" ")[1]
    return ret_str

def def_signal_fun(line:str):
    # 刪除多余空格
    line=' '.join(line.split())
    # print(line)
    list_str=line.split('(')
    fun_name=list_str[0].split(' ')[1]
    param_str=list_str[1].split(')')[0]
    params=[]
    # 有","則至少有兩個參數(shù)否則可能有一個參數(shù),可能沒有
    if(param_str.count(',')>0):
        params=param_str.split(',')
        for i in range(len(params)):
            params[i]=params[i].strip()
    else:
        t_str=param_str.strip()
        if(len(t_str)>0)and(t_str!="void"):
            params.append(t_str)
    # print(fun_name,params)
    params_num=len(params)
    fun_str=""
    fun_str+="void "+fun_name+"("
    for i in range(params_num):
        fun_str+=params[i]
        if(i<len(params)-1):
            fun_str+=","
    fun_str+=")\n{\n"
    fun_str+="  uint32_t *param=malloc(sizeof(uint32_t)*wppm3vysvbp);\n".format(d=params_num)
    for i in range(params_num):
        fun_str+="  param[{d1}]=(uint32_t){d2};\n".format(d1=i,d2=split_par_name(params[i]))
    fun_str+="  _signal_emit({d1},param,{d2});\n".format(d1=fun_name,d2=params_num)
    fun_str+="  free(param);\n}\n"
    fun_str+="signal_export(wppm3vysvbp);\r\n\r\n\r\n\r\n".format(d=fun_name)
    # print(fun_str)
    return fun_str

def ergodic_signal_fun(path):
    fun_str=""
    list_file=find_file(path,".h")
    for i in list_file:
        list_signal=find_signal_def(i)
        for j in list_signal:
            fun_str+=def_signal_fun(j)
    return fun_str

def mod_fine_creat(file_path):
    with open(file_path,"w+") as f:
        f.write("#include \"stdlib.h\"\n")
        f.write("#include \"signal.h\"\r\n\r\n\r\n\r\n")
        f.write(ergodic_signal_fun(MOD_PATH))

mod_fine_creat(MOD_PATH+MOD_FILE)
print("mod_file creat success.\n")

使用moc腳本:
keil中做如下設置,可在每次編譯前自動調用moc腳本:

自動調用moc腳本

五.使用

1.測試程序編寫

編寫 main.c 如下:

// 使用信號槽功能需要包含此文件
#include "signal.h"
// 此文件聲明了pmcu_test_signal 信號,如前文所述
#include "prot_mcu.h"

// 定義槽函數(shù)
void slot_test(int *a,int b)
{
  int int_array[20]={0};
  int_array[0]=a[0];
  int_array[1]=a[1];
  int_array[2]=a[2];
  int_array[3]=a[3];
  int_array[4]=a[4];
  int_array[5]=b;
}
// 定義槽函數(shù),假設a是函數(shù)所在類
void slot_test2(int *a,int *b,int c)
{
  int int_array[20]={0};
  int_array[0]=a[0];
  int_array[1]=a[1];
  int_array[2]=a[2];
  int_array[3]=a[3];
  int_array[4]=a[4];
  int_array[5]=c;
}

int main()
{
  // 定義數(shù)據(jù),異步調用時需保證數(shù)據(jù)在函數(shù)執(zhí)行時還有效
  int int_array[20]={4,3,5,2,1};
  // 創(chuàng)建異步調用線程,如不需要異步調用,則無需創(chuàng)建線程
  sig_thread t=thread_creat(20);
  // 連接信號和槽,異步調用,這個槽將在線程t中運行
  connect(pmcu_test_signal,t,0,slot_test);
  // 同步調用,這個槽將在調用信號的線程中運行
  connect(pmcu_test_signal,0,0,slot_test);
  // 異步調用,假設int_array是槽函數(shù)所在類
  connect(pmcu_test_signal,t,int_array,slot_test2);
  while(1)
  {
    rt_thread_mdelay(1000);
    // 發(fā)送信號,emit不是必須的,但出于可讀性要求,還是加上比較好
    emit pmcu_test_signal(int_array,8);
  }
  
}

程序中 pmcu_test_signal 信號連接了3個槽,因此發(fā)送信號時,每個槽都會執(zhí)行一遍,其中沒有所屬類的槽函數(shù) slot_test 會執(zhí)行兩遍,一次在main函數(shù)線程,一次在異步線程; slot_test2函數(shù)有所屬類,其第一個參數(shù)就是所屬類的指針,執(zhí)行在異步線程.

2.執(zhí)行結果

進入調試界面:

調試1

通過調試信息分析:
此時槽 slot_test 在異步線程中執(zhí)行,并且參數(shù)傳入正常

調試2

此時槽 slot_test 在同步線程中執(zhí)行,并且參數(shù)傳入正常

調試3

此時槽 slot_test2 在異步線程中執(zhí)行,并且參數(shù)傳入正常

六.總結

寥寥幾行就實現(xiàn)了類于類之間的解耦合,異步調用實現(xiàn)更是簡單,基于此模型的編程各模塊只需要關注自己內部的業(yè)務邏輯,在合適的時候發(fā)出信號就行了,至于有沒有模塊需要,哪些模塊需要,就不需要關心了.

到此這篇關于C語言實現(xiàn)信號槽的項目實踐的文章就介紹到這了,更多相關C語言 信號槽內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 解析c語言switch中break語句的具體作用

    解析c語言switch中break語句的具體作用

    以下是對c語言switch中break語句的作用進行了詳細的分析介紹,需要的朋友可以過來參考下
    2013-07-07
  • C++求逆序對的方法

    C++求逆序對的方法

    這篇文章主要介紹了C++求逆序對的方法,包含了字符串常見的操作方法,是非常實用的技巧,需要的朋友可以參考下
    2014-09-09
  • Arduino控制舵機詳解 附代碼

    Arduino控制舵機詳解 附代碼

    rduino是一款便捷靈活、方便上手的開源電子原型平臺,它構建于開放原始碼simple I/O介面版,并且具有使用類似Java、C語言的Processing/Wiring開發(fā)環(huán)境,這篇文章主要介紹了Arduino控制舵機詳解(含代碼),需要的朋友可以參考下
    2023-05-05
  • Visual?Studio2022報錯無法打開源文件?"openssl/conf.h"解決方法

    Visual?Studio2022報錯無法打開源文件?"openssl/conf.h"解決方法

    這篇文章主要介紹了Visual?Studio2022報錯無法打開源文件"openssl/conf.h"解決方式,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • C++數(shù)據(jù)結構之哈希表的實現(xiàn)

    C++數(shù)據(jù)結構之哈希表的實現(xiàn)

    哈希表,即散列表,可以快速地存儲和查詢記錄。這篇文章主要為大家詳細介紹了C++數(shù)據(jù)結構中哈希表的實現(xiàn),感興趣的小伙伴可以了解一下
    2023-03-03
  • C語言實現(xiàn)登錄注冊和忘記密碼功能

    C語言實現(xiàn)登錄注冊和忘記密碼功能

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)登錄、注冊和忘記密碼功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • 舉例說明自定義C++異常處理的實例

    舉例說明自定義C++異常處理的實例

    這篇文章主要介紹了舉例說明自定義C++異常處理的實例的相關資料,這里舉例說明該如何使用C++ 的異常,需要的朋友可以參考下
    2017-10-10
  • C語言庫函數(shù)qsort的使用詳解

    C語言庫函數(shù)qsort的使用詳解

    C語言庫函數(shù)中的qsort的是一個回調函數(shù),回調函數(shù)就是一個通過函數(shù)指針調用的函數(shù),這篇文章主要介紹了C語言庫函數(shù)qsort的使用,需要的朋友可以參考下
    2022-06-06
  • C語言超詳細文件操作基礎下篇

    C語言超詳細文件操作基礎下篇

    這篇文章主要為大家詳細介紹了C語言的文件操作,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C語言編程中生成隨機數(shù)的入門教程

    C語言編程中生成隨機數(shù)的入門教程

    這篇文章主要介紹了C語言編程中生成隨機數(shù)的入門教程,包括利用rand()函數(shù)來編寫隨機數(shù)生成器的示例,要的朋友可以參考下
    2015-12-12

最新評論

五大连池市| 兴宁市| 赤城县| 芒康县| 九龙城区| 宜都市| 禄劝| 宜春市| 宜宾县| 德清县| 恭城| 侯马市| 云梦县| 阳高县| 攀枝花市| 航空| 临武县| 湖南省| 庆阳市| 牙克石市| 嘉定区| 龙岩市| 隆子县| 云浮市| 沙湾县| 黑龙江省| 木兰县| 元氏县| 乐陵市| 巴青县| 湟源县| 怀远县| 安福县| 涞水县| 昌图县| 囊谦县| 永定县| 定日县| 邹城市| 徐闻县| 始兴县|