亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

基于C語(yǔ)言的庫(kù)封裝發(fā)布技術(shù)詳解

 更新時(shí)間:2021年08月24日 14:35:19   作者:丁勁犇  
在編程的過程中,使用已經(jīng)封裝好的庫(kù)函數(shù)是十分方便的,也是十分高效的,這篇文章主要給大家介紹了關(guān)于C語(yǔ)言庫(kù)的封裝和使用的相關(guān)資料,需要的朋友可以參考下

每年實(shí)驗(yàn)課,總有同學(xué)問我,如何生成DLL、如何導(dǎo)出類,如何不花很多時(shí)間精力,就設(shè)計(jì)出一個(gè)給別人用的爽的功能庫(kù)呢?結(jié)合這些年的實(shí)踐,我們今天就來聊一聊動(dòng)態(tài)鏈接庫(kù)的封裝發(fā)布。您也可以直接跳到文章最后,去github查看C++/C混合庫(kù)的經(jīng)典案例——Ettus uhd

要讓自己的庫(kù)好用,又通用,該怎么辦?重要的事情說前面:

  • 不要導(dǎo)出類、不要導(dǎo)出變量,僅使用C基礎(chǔ)數(shù)據(jù)類型。
  • 面向?qū)ο髮?shí)現(xiàn)功能真香,實(shí)現(xiàn)接口真要命。
  • 用最棒的語(yǔ)言實(shí)現(xiàn)功能,遵循C語(yǔ)言標(biāo)準(zhǔn)實(shí)現(xiàn)接口。
  • 非密集吞吐的接口,可以使用json整體交互。密集吞吐,用內(nèi)存。

做到了這幾點(diǎn),即使用戶從VC2010換成了python,庫(kù)都不用改。究其原因,C++的類在二進(jìn)制結(jié)構(gòu)上是缺乏定義的,一個(gè)返回值用了std::string,或者參數(shù)用了CTime的方法,從VC2010導(dǎo)出的DLL到了VC2017就不一定能用,更別提其他編譯器和語(yǔ)言了。

1. C動(dòng)態(tài)鏈接庫(kù)是一種即成標(biāo)準(zhǔn)

C語(yǔ)言是一門古老的語(yǔ)言。從六七十年代開始,在Unix/Linux操作系統(tǒng)上,C語(yǔ)言實(shí)現(xiàn)了大量的庫(kù),幾乎涵蓋了當(dāng)代科學(xué)涉及的所有領(lǐng)域。從基礎(chǔ)的XML操作,到復(fù)雜的數(shù)學(xué)算法,都能找到對(duì)應(yīng)的C庫(kù)。C語(yǔ)言的動(dòng)態(tài)鏈接庫(kù)承載了太多的智力遺產(chǎn),以至于后來的大部分語(yǔ)言都自覺的加入了享用既有C語(yǔ)言動(dòng)態(tài)鏈接庫(kù)的能力。

這種情況使得符合C語(yǔ)言習(xí)慣的動(dòng)態(tài)鏈接庫(kù)接口1成為了一種即成實(shí)事,不同的語(yǔ)言之間,使用C動(dòng)態(tài)鏈接庫(kù)的標(biāo)準(zhǔn)交互。盡管這種接口是面向過程的,而可用的參數(shù)類型少的可憐,但其簡(jiǎn)單、直接,又有大量的歷史資源,使得后來的CORBA、COM也無法取代這種底層的接口方式2。

這里有幾個(gè)概念需要明確:

  • C接口的動(dòng)態(tài)鏈接庫(kù)的通用性,一般只和操作系統(tǒng)、運(yùn)行時(shí)(32位還是64位)有關(guān),和具體的編譯器、語(yǔ)言無關(guān)。
  • 很多現(xiàn)代編程語(yǔ)言能調(diào)用C接口的動(dòng)態(tài)鏈接庫(kù)。
  • 部分現(xiàn)代編程語(yǔ)言能生成C接口的動(dòng)態(tài)鏈接庫(kù)。

無論你使用什么語(yǔ)言開發(fā)功能,只要提供了符合C語(yǔ)言動(dòng)態(tài)鏈接庫(kù)結(jié)構(gòu)的接口,許多其他語(yǔ)言就可以使用你的功能。因此,完全可以用C++語(yǔ)言實(shí)現(xiàn)一個(gè)C接口的庫(kù),在里面盡情使用STL。

C/C++

2. 用C++制作C的庫(kù)

用C++做C的庫(kù),關(guān)鍵是用好句柄。

什么是“句柄(Handle)”?這是個(gè)翻譯問題。你可以理解為“把手”或者“提手”更合適。句柄很多時(shí)候是一個(gè)整數(shù),用于標(biāo)記一堆運(yùn)行時(shí)資源,實(shí)現(xiàn)操作動(dòng)態(tài)庫(kù)功能的目的。

對(duì)一個(gè)復(fù)雜的功能來說,需要很多運(yùn)行時(shí)的參數(shù)來支撐。比如FFT,就需要有一個(gè)內(nèi)存區(qū)域記錄蝶形運(yùn)算的單元,以及指向各層單元的索引。對(duì)通信中的糾錯(cuò)譯碼,需要一些內(nèi)存區(qū)域記憶寄存器,以及當(dāng)前的狀態(tài)。所有上述這些狀態(tài),都可以用一個(gè)struct 包裹起來,形成一個(gè)“箱子”。這個(gè)箱子對(duì)用戶是透明的,只需要把箱子的把手(Handle)交給用戶手上,用戶在需要的時(shí)候,交回箱子并執(zhí)行任務(wù)。

在這里插入圖片描述

不難想像,可以同時(shí)申請(qǐng)多個(gè)箱子,交給不同的線程去執(zhí)行。庫(kù)的設(shè)計(jì)者要確保Handle標(biāo)記的參數(shù)包之間是獨(dú)立的、線程安全的。

同時(shí),句柄本身可以復(fù)刻面向?qū)ο蟮牟糠止δ堋H绻袶andle作為this指針看待,則C++類可以直接導(dǎo)出為C的函數(shù)。只是首個(gè)參數(shù)要傳入Handle即可。

2.1 使用void * 作為句柄

舉個(gè)例子,假設(shè)手頭有一個(gè)實(shí)現(xiàn)字符串查找的類,需要向外發(fā)布功能。但這個(gè)類是C++的,類似:

//關(guān)鍵詞查找器類
class Findfoo
{
public:
	Findfoo(const std::string & task = "foo");
	~Findfoo();
public:
	void setTask(const std::string & task);
	const std::string &  task() const;
	//在rawStr里查找關(guān)鍵詞
	long long Find(const std::string & rawStr);
private:
	//用于匹配的關(guān)鍵詞
	std::string m_task = "foo";
};
Findfoo::Findfoo(const std::string & task)
	:m_task(task)
{}
Findfoo::~Findfoo()
{}
void Findfoo::setTask(const std::string & task)
{
	m_task = task;
}
const std::string &  Findfoo::task() const
{
	return m_task;
}
long long Findfoo::Find(const std::string & rawStr)
{
	return rawStr.find(m_task);
}

此時(shí),可以設(shè)置以下接口,把C++的類變成C的方法。一旦變?yōu)镃的方法,外部就無需知道該類的存在。

//創(chuàng)建一個(gè)查找器,返回句柄。提供的是關(guān)鍵詞。
void * ff_init_task(const char * task)
{
	Findfoo * f = new Findfoo(task);
	return (void *) f;
}
//重設(shè)關(guān)鍵詞
void ff_reset_task(void * h, const char * task)
{
	Findfoo * f = (Findfoo *)(h);
	assert(f);
	f->setTask(task);
}
//獲取當(dāng)前關(guān)鍵詞
const char * ff_get_task(void * h)
{
	Findfoo * f = (Findfoo *)(h);
	assert(f);
	return f->task().c_str();
}
//用關(guān)鍵詞查找rawStr
long long ff_find(void * h, const char * rawStr)
{
	Findfoo * f = (Findfoo *)(h);
	assert(f);
	return f->Find(rawStr);
}
//刪除當(dāng)前查找器
void ff_fini_task(void * h)
{
	Findfoo * f = (Findfoo *)(h);
	if (f)
		delete f;
}

如此操作,用戶可以完全不知道存在Findfoo類,只用一個(gè)void *指針作為操作類的指示。

上面的例子僅有1個(gè)類作為演示。實(shí)際開發(fā)中,一個(gè)工作可能由好幾個(gè)類的實(shí)例共同協(xié)作完成??梢杂靡粋€(gè)std::map<long long, XXX>來管理各個(gè)實(shí)例,也可以把實(shí)例全部放在一個(gè)struct中。如果用std::map,切記多線程下的mutex一致性保護(hù),防止用戶同時(shí)在多個(gè)線程init好幾組功能實(shí)例,導(dǎo)致std::map崩潰。

從性能角度,建議采用struct來承載所有運(yùn)行時(shí),而后返回指向該struct的指針。

2.2 導(dǎo)出這些方法

上述函數(shù),因?yàn)槭荂++函數(shù),編譯器會(huì)對(duì)其進(jìn)行改名,把參數(shù)也放進(jìn)去,以便支持多態(tài)(同一個(gè)函數(shù)名,不同參數(shù))。要導(dǎo)出為C的函數(shù),就不允許編譯器改名字。要用“extern ‘C‘”進(jìn)行包裝,以便導(dǎo)出這些方法時(shí),函數(shù)名不變。

同時(shí),在Windows下,函數(shù)存在多個(gè)參數(shù)時(shí),棧內(nèi)的參數(shù)順序也有從左開始還是從右壓棧的區(qū)別。要做到最大的適應(yīng)性,需要指定 stdcall開關(guān)。

最后,我們不想為生成庫(kù)的工程、用戶的工程準(zhǔn)備兩套頭文件,故而需要一些瑣碎的宏定義,以區(qū)分當(dāng)前編譯的是DLL本身,還是使用DLL的用戶工程。

具體:

建立一個(gè)頭文件,叫做findfoo_global.h。這個(gè)頭文件對(duì)Linux和windows平臺(tái)定義一些宏,用于聲明函數(shù)時(shí),指定導(dǎo)出(構(gòu)造DLL本身)和導(dǎo)入(使用DLL)

#ifndef FINDFOO_GLOBAL_H
#define FINDFOO_GLOBAL_H
#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#  define Q_DECL_EXPORT __declspec(dllexport)
#  define Q_DECL_IMPORT __declspec(dllimport)
#  define FOOCALL __stdcall
#else
#  define Q_DECL_EXPORT     __attribute__((visibility("default")))
#  define Q_DECL_IMPORT     __attribute__((visibility("default")))
#  define FOOCALL
#endif
#if defined(FINDFOO_LIBRARY)
#  define FINDFOO_EXPORT Q_DECL_EXPORT
#else
#  define FINDFOO_EXPORT Q_DECL_IMPORT
#endif
//句柄就是void *
#define FFHANDLE void *
#endif // FINDFOO_GLOBAL_H

在DLL的工程中,要定義FINDFOO_LIBRARY宏,這樣,就開啟了導(dǎo)出開關(guān)。

建立頭文件findfoo.h

#ifndef FINDFOO_H
#define FINDFOO_H
#include "findfoo_global.h"
#ifdef __cplusplus
extern "C"{
#endif
FINDFOO_EXPORT FFHANDLE		FOOCALL		ff_init_task	(const char * task);
FINDFOO_EXPORT void			FOOCALL		ff_reset_task	(FFHANDLE h	, const char * task);
FINDFOO_EXPORT const char * FOOCALL		ff_get_task		(FFHANDLE h	);
FINDFOO_EXPORT long long	FOOCALL		ff_find			(FFHANDLE h	, const char * rawStr);
FINDFOO_EXPORT void			FOOCALL		ff_fini_task	(FFHANDLE h	);
#ifdef __cplusplus
}
#endif
#endif // FINDFOO_H

實(shí)現(xiàn)導(dǎo)出方法 findfoo.cpp

#include "findfoo.h"
#include <assert.h>
#include <string>
class Findfoo
{
public:
	Findfoo(const std::string & task = "foo");
	~Findfoo();
public:
	void setTask(const std::string & task);
	const std::string &  task() const;
	long long Find(const std::string & rawStr);
private:
	std::string m_task = "foo";
};
Findfoo::Findfoo(const std::string & task)
	:m_task(task){}
Findfoo::~Findfoo(){}
void Findfoo::setTask(const std::string & task)
{
	m_task = task;
}
const std::string &  Findfoo::task() const
{
	return m_task;
}
long long Findfoo::Find(const std::string & rawStr)
{
	return rawStr.find(m_task);
}
//-----------
FINDFOO_EXPORT FFHANDLE FOOCALL ff_init_task(const char * task)
{
	Findfoo * f = new Findfoo(task);
	return (FFHANDLE) f;
}
FINDFOO_EXPORT void FOOCALL ff_reset_task(FFHANDLE h, const char * task)
{
	Findfoo * f = reinterpret_cast<Findfoo *>(h);
	assert(f);
	f->setTask(task);
}
FINDFOO_EXPORT const char * FOOCALL ff_get_task(FFHANDLE h)
{
	Findfoo * f = reinterpret_cast<Findfoo *>(h);
	assert(f);
	return f->task().c_str();
}
FINDFOO_EXPORT long long FOOCALL ff_find(FFHANDLE h, const char * rawStr)
{
	Findfoo * f = reinterpret_cast<Findfoo *>(h);
	assert(f);
	return f->Find(rawStr);
}
FINDFOO_EXPORT void FOOCALL ff_fini_task(FFHANDLE h)
{
	Findfoo * f = reinterpret_cast<Findfoo *>(h);
	if (f)
		delete f;
}

3. 使用庫(kù)

一旦導(dǎo)出了上述方法,即可使用庫(kù)。

#include <iostream>
#include <cassert>
#include "findfoo.h"
using namespace std;
int main()
{
	FFHANDLE h = ff_init_task("foobar");
	assert(h);
	cout << "Task string:" << ff_get_task(h) << endl;
	cout << "Input String:";
	std::string strRaw;
	cin >> strRaw;
	cout << ff_find(h,strRaw.c_str());
	//Delete
	ff_fini_task(h);
	h = nullptr;
	return 0;
}

上述是最簡(jiǎn)單的例子。當(dāng)需要處理大量動(dòng)態(tài)內(nèi)存時(shí),需要注意:內(nèi)存誰申請(qǐng),誰釋放。這一點(diǎn)特別容易引起錯(cuò)誤。

4. 經(jīng)典的范例:libuhd

USRP軟件無線電平臺(tái)對(duì)應(yīng)的開源庫(kù)libuhd是用C++ boost開發(fā)的。但是,為了兼容更多的語(yǔ)言,其進(jìn)行了封裝,把各個(gè)類都用句柄抽象出來了,且是標(biāo)準(zhǔn)C的接口。

可以去Github的工程頁(yè)簽出項(xiàng)目查看,也可以跟蹤代碼查看其原理。

這個(gè)項(xiàng)目是把C、C++的聯(lián)合運(yùn)用發(fā)揮的非常棒的例子。

1包括函數(shù)入口點(diǎn)的定位方式、函數(shù)命名方式、參數(shù)傳遞規(guī)則、參數(shù)類型。

2.COM和C接口DLL其實(shí)不是一個(gè)范疇的東西,這里放在一起,有點(diǎn)粗暴。

總結(jié)

本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • C++如何實(shí)現(xiàn)簡(jiǎn)單的計(jì)時(shí)器詳解

    C++如何實(shí)現(xiàn)簡(jiǎn)單的計(jì)時(shí)器詳解

    因?yàn)樽罱e著無聊就想著要不用C++寫點(diǎn)什么東西,仔細(xì)想了想其實(shí)自己的C++學(xué)的也不怎么好,寫個(gè)簡(jiǎn)單的計(jì)時(shí)器吧!所以下面這篇文章主要介紹了利用C++如何實(shí)現(xiàn)簡(jiǎn)單的計(jì)時(shí)器,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-01-01
  • C++高精度算法的使用場(chǎng)景詳解

    C++高精度算法的使用場(chǎng)景詳解

    在我們進(jìn)行計(jì)算的過程中,經(jīng)常會(huì)遇到幾十位,甚至幾百位的數(shù)字的計(jì)算問題,也有可能會(huì)遇到小數(shù)點(diǎn)后幾十位,幾百位的情況,而我們面對(duì)這樣的情況下,long long  和 double 的數(shù)據(jù)范圍顯然是不夠使用的了。因此這時(shí),我們就需要引入一個(gè)新的算法,叫做高精度算法
    2022-09-09
  • C語(yǔ)言實(shí)現(xiàn)memcpy函數(shù)的使用示例

    C語(yǔ)言實(shí)現(xiàn)memcpy函數(shù)的使用示例

    在C語(yǔ)言中,我們可以自己實(shí)現(xiàn) memcpy 函數(shù)來實(shí)現(xiàn)內(nèi)存數(shù)據(jù)的拷貝操作,本文就來介紹一下C語(yǔ)言實(shí)現(xiàn)memcpy函數(shù)的使用示例,感興趣的可以了解一下
    2023-09-09
  • C語(yǔ)言實(shí)現(xiàn)貪吃蛇游戲(命令行)

    C語(yǔ)言實(shí)現(xiàn)貪吃蛇游戲(命令行)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言命令行實(shí)現(xiàn)貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • 在C++中自定義宏的簡(jiǎn)單方法

    在C++中自定義宏的簡(jiǎn)單方法

    這篇文章主要介紹了在C++中自定義宏的簡(jiǎn)單方法,作者建議使用類似定義函數(shù)一樣的方法來定義宏,需要的朋友可以參考下
    2015-07-07
  • C語(yǔ)言實(shí)現(xiàn)BST二叉排序樹的基本操作

    C語(yǔ)言實(shí)現(xiàn)BST二叉排序樹的基本操作

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)BST二叉排序樹的基本操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 基于C語(yǔ)言實(shí)現(xiàn)的aes256加密算法示例

    基于C語(yǔ)言實(shí)現(xiàn)的aes256加密算法示例

    這篇文章主要介紹了基于C語(yǔ)言實(shí)現(xiàn)的aes256加密算法,結(jié)合具體實(shí)例形式詳細(xì)分析了C語(yǔ)言實(shí)現(xiàn)的aes256加密算法實(shí)現(xiàn)步驟與使用技巧,需要的朋友可以參考下
    2017-02-02
  • C語(yǔ)言中不定參數(shù)?...?的語(yǔ)法以及函數(shù)封裝

    C語(yǔ)言中不定參數(shù)?...?的語(yǔ)法以及函數(shù)封裝

    不定參數(shù)是指函數(shù)可以接收不確定個(gè)數(shù)的參數(shù),下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言中不定參數(shù)?...?的語(yǔ)法以及函數(shù)封裝的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • 隨機(jī)加密程序的實(shí)現(xiàn)方法

    隨機(jī)加密程序的實(shí)現(xiàn)方法

    下面實(shí)例是對(duì)隨機(jī)加密程序的實(shí)現(xiàn)方法。需要的朋友參考下
    2013-05-05
  • C++常用函數(shù)之XML JSON格式轉(zhuǎn)換問題

    C++常用函數(shù)之XML JSON格式轉(zhuǎn)換問題

    XML在Json出現(xiàn)前應(yīng)用很廣泛,靈活性好,應(yīng)用語(yǔ)言也沒有限制,發(fā)展了這么長(zhǎng)時(shí)間后xml標(biāo)準(zhǔn)已經(jīng)很臃腫。這篇文章主要介紹了C++常用函數(shù)之XML JSON格式轉(zhuǎn)換問題,需要的朋友可以參考下
    2020-02-02

最新評(píng)論