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

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

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

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

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

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

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

1. C動態(tài)鏈接庫是一種即成標準

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

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

這里有幾個概念需要明確:

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

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

C/C++

2. 用C++制作C的庫

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

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

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

在這里插入圖片描述

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

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

2.1 使用void * 作為句柄

舉個例子,假設(shè)手頭有一個實現(xiàn)字符串查找的類,需要向外發(fā)布功能。但這個類是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è)置以下接口,把C++的類變成C的方法。一旦變?yōu)镃的方法,外部就無需知道該類的存在。

//創(chuàng)建一個查找器,返回句柄。提供的是關(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);
}
//獲取當前關(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);
}
//刪除當前查找器
void ff_fini_task(void * h)
{
	Findfoo * f = (Findfoo *)(h);
	if (f)
		delete f;
}

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

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

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

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

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

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

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

具體:

建立一個頭文件,叫做findfoo_global.h。這個頭文件對Linux和windows平臺定義一些宏,用于聲明函數(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

實現(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. 使用庫

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

#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;
}

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

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

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

可以去Github的工程頁簽出項目查看,也可以跟蹤代碼查看其原理。

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

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

2.COM和C接口DLL其實不是一個范疇的東西,這里放在一起,有點粗暴。

總結(jié)

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

相關(guān)文章

  • C語言楊輝三角兩種實現(xiàn)方法

    C語言楊輝三角兩種實現(xiàn)方法

    大家好,本篇文章主要講的是C語言楊輝三角兩種實現(xiàn)方法,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • OpenCV實現(xiàn)圖像角點檢測

    OpenCV實現(xiàn)圖像角點檢測

    這篇文章主要為大家詳細介紹了OpenCV實現(xiàn)圖像角點檢測,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • C語言中main函數(shù)與命令行參數(shù)詳細講解

    C語言中main函數(shù)與命令行參數(shù)詳細講解

    這篇文章主要為大家詳細介紹了C語言main()函數(shù)與命令行參數(shù)問題,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-04-04
  • QT+OpenGL實現(xiàn)簡單圖形的繪制

    QT+OpenGL實現(xiàn)簡單圖形的繪制

    這篇文章主要為大家詳細介紹了如何利用QT和OpenGL實現(xiàn)簡單圖形的繪制,文中的示例代碼講解詳細,具有一定的借鑒價值,需要的可以參考一下
    2022-12-12
  • c++臨時對象導(dǎo)致的生命周期問題

    c++臨時對象導(dǎo)致的生命周期問題

    對象的生命周期是c++中非常重要的概念,它直接決定了你的程序是否正確以及是否存在安全問題,這篇文章主要介紹了c++臨時對象導(dǎo)致的生命周期問題 ,需要的朋友可以參考下
    2024-07-07
  • C語言實現(xiàn)簡單的三子棋游戲源碼

    C語言實現(xiàn)簡單的三子棋游戲源碼

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)簡單的三子棋游戲源碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 深入理解memmove()與memcpy()的區(qū)別以及實現(xiàn)方法

    深入理解memmove()與memcpy()的區(qū)別以及實現(xiàn)方法

    本篇文章是對memmove()與memcpy()的區(qū)別以及實現(xiàn)方法進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C++求解二叉樹的下一個結(jié)點問題

    C++求解二叉樹的下一個結(jié)點問題

    本文將通過C++求解以下問題:給定一個二叉樹其中的一個結(jié)點,請找出中序遍歷順序的下一個結(jié)點并且返回。文中示例代碼講解詳細,感興趣的可以了解一下
    2022-04-04
  • C++實現(xiàn)乒乓球比分判定

    C++實現(xiàn)乒乓球比分判定

    這篇文章主要為大家詳細介紹了C++實現(xiàn)乒乓球比分判定,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • C++實現(xiàn)LeetCode(171.求Excel表列序號)

    C++實現(xiàn)LeetCode(171.求Excel表列序號)

    這篇文章主要介紹了C++實現(xiàn)LeetCode(171.求Excel表列序號),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08

最新評論

安达市| 凤翔县| 玛沁县| 盖州市| 屏南县| 武鸣县| 大埔县| 连山| 武清区| 乌兰县| 玛纳斯县| 肃南| 泸定县| 武陟县| 灵武市| 麻江县| 安义县| 广昌县| 沿河| 高邮市| 阳城县| 肇东市| 连平县| 襄城县| 比如县| 南岸区| 神池县| 贵南县| 云安县| 阳春市| 抚松县| 中江县| 九江县| 凤凰县| 台江县| 丹棱县| 杨浦区| 湖口县| 城口县| 佳木斯市| 清涧县|