創建動態鏈接庫工程和測試工程 windows下vs2013,和ubuntu下QtCreator以及codeblocks使用體驗

1、背景

因爲很多情況下,我們是不給別人或者甲方爸爸源碼的,所以就要將源碼編譯成動態或者靜態鏈接庫來調用。在windows下面開發,當然用vs是最方便的了,雖然在linux下有了vs code,但是小白表示還沒有摸索明白各種環境設置是怎麼回事,不如windows下面的vs來的可視化好。所以,先看看windows下開發C++的一個超級小白簡單的例子,如何建立鏈接庫工程,生成動態鏈接庫,然後如何建立測試這個鏈接庫的測試工程;以及如果不在同一解決方案下建立測試工程,如何從外部測試這個動態鏈接庫。

因爲也需要開發Ubuntu下面的東西,所以也比較了下。在Ubuntu下分別利用qt,codeblocks完成類似工作。下面看到dummy的代碼煩請見笑了,這只是一條簡單的體驗紀錄。。。

2、Windows下-----vs創建share lib並測試

2.1、創建鏈接庫工程

(1)創建

打開vs2013,選擇“New Project”,在彈出的對話框中選擇“Win32 project”或者“Win32 console application”。注意不要選則“Static(Windows Store apps)” 或者“DLL(Windows Store apps)”。

選擇好工程的路徑,取名字,默認勾選“Create directory for solution”, 點擊OK。

接下來的對話框,選擇“Next”。

設置嚮導接下來注意啦!在Application Setting這一頁中,Application type這裏選擇“DLL”。至於下面Additional options,我當時選了“export symbols”,沒選這個好像也沒有少了啥(myDll)。。。,如果選擇“empty project”就是一個完全空的工程,不包含任何文件。其它保持默認,點擊“Finish”。

創建的庫工程叫做simpleDLL2,結構如下所示:

其中,vs自動創建的文件用紅框標註了。

stdafx.h,stdafx.cpp-----(來自百度百科):英文全稱爲:Standard Application Framework Extensions(標準應用程序框架的擴展)。頭文件預編譯,就是把一個工程(Project)中使用的一些MFC標準頭文件(如Windows.H、Afxwin.H)預先編譯,以後該工程編譯時,不再編譯這部分頭文件,僅僅使用預編譯的結果。這樣可以加快編譯速度,節省時間。

因爲我不用MFC,所以這個應該是可以刪掉的。

targetver.h-----定義程序運行的環境。

simpleDll2.h, simpleDll2.cpp-----vs爲工程創建的,這裏可以理解爲接口代碼寫在這裏。後面貼代碼分析。

dllmain.cpp 裏面放了dll的入口函數。目前不知道這個具體有啥用,網上查了有的說是起到初始化作用。反正我沒有用到,不知道刪了這個cpp可以不?反正我把入口函數全部註釋掉,編譯後測試仍然正確。

interior.h,interior.cpp----我自己創建的新的類,希望將一些內部函數放在這裏,不希望別人看到源碼。

(2)結構及源碼

下面具體說說這個project的結構。我們希望將所有接口函數的實現放在simpleDll2.cpp中,這個裏面的接口函數調用其它cpp比如interior.cpp中的函數實現具體的功能。當編譯完成時,我們可以將接口的cpp和h文件以及lib和dll給用戶,而interior源碼不給,保留了自己的核心技術。

首先放上simpleDll2.h:

// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the SIMPLEDLL2_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// SIMPLEDLL2_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported. 
//上面這麼多話,意思就是如果你想當可輸出接口函數或者類或者結構體啥的,前面加上SIMPLEDLL2_API
#ifdef SIMPLEDLL2_EXPORTS
#define SIMPLEDLL2_API __declspec(dllexport)
#else
#define SIMPLEDLL2_API __declspec(dllimport)
#endif

// This class is exported from the simpleDll2.dll
class SIMPLEDLL2_API CsimpleDll2 {
public:
	CsimpleDll2(void);
	// TODO: add your methods here.
	int extfun1(int x, int y); //在這個可輸出類裏定義了一個接口函數
};

extern SIMPLEDLL2_API int nsimpleDll2;
SIMPLEDLL2_API int fnsimpleDll2(void);
extern SIMPLEDLL2_API int extFun2(int x, int y, int z);  //定義了一個接口函數

然後是:simpleDll2.cpp。看見沒,其實vs人家給出了可輸出的變量、函數、類的例子。

// simpleDll2.cpp : Defines the exported functions for the DLL application.

#include "stdafx.h"
#include "simpleDll2.h"
#include "interior.h"

// This is an example of an exported variable
SIMPLEDLL2_API int nsimpleDll2=0;

// This is an example of an exported function.
SIMPLEDLL2_API int fnsimpleDll2(void)
{
	return 42;
}

// This is the constructor of a class that has been exported.
// see simpleDll2.h for the class definition
CsimpleDll2::CsimpleDll2()
{
	return;
}

int CsimpleDll2::extfun1(int x, int y)
{
	return x + y;
}

SIMPLEDLL2_API int extFun2(int x, int y, int z)
{
	interior a;
	int b = a.intFun(x, y);
	return b + z;
}

說明的是,在simpleDll2.cpp中定義了個可輸出的函數extFun2,用來調用不可輸出的類的函數interior::intFun,其定義如下:

int interior::intFun(int x, int y)
{
	return x + y;
}

實際上,可以定義extFun2只實現intFun的功能,視實際需要而定。

寫完這個簡單示例,就可以編譯了。build完之後,會發現在solution(注意是solution即simpleDll2根目錄,而不是simpleDll2工程目錄下)的Debug路徑下,出現了simpleDll2.lib,simpleDll2.dll。

2.2、創建測試工程

在開發階段,可以在工程裏新建一個測試工程,用於測試鏈接庫的功能和調試。

右鍵點擊工程,選擇add,new project,選擇win32 console application工程,取名testdll。建好後是這樣的:

注意!需要配置環境!因爲和simpledll2工程在一個solution下,所以vs自動創建的結構,一般配置是這樣的:

右鍵單擊testdll工程,打開propoties,在VC++ Directories選項頁面裏,include Directories設置在最前面加上:..\simpleDll2; Library Directories設置在最前面加上:..\Debug; 在Linker --->  input 選項頁Additional Dependencies設置在最前面加上:simpleDll2.lib; 

在testdll.cpp添加:

// testdll.cpp : Defines the entry point for the console application.

#include "stdafx.h"
#include "simpleDll2.h"
#include <iostream>

int _tmain(int argc, _TCHAR* argv[])
{
	int a = 0;
	CsimpleDll2 cs;
	a = cs.extfun1(3, 4);
	std::cout << "a:" << a << std::endl;

	int b = extFun2(1, 2, 3);
	std::cout << "b:" << b << std::endl;

	//int c = intFun(1, 2); ERROR!!
	return 0;
}

測試的結果爲:

a:7
b:6

2.3、封裝好的庫被外部測試

假設我們已經開發完成交付用戶。用戶可以拿着你的庫進行二次開發。現在模擬用戶,看如何二次開發,或者說我們可以測試下是否可以用我們自己的庫二次開發。

首先,搭好結構:我起了一個文件夾叫“dev”,裏面有:

一個一個梳理:

(1)3rdparty文件夾是二次開發用到所有的第三方庫相關的.h和lib文件都放在這裏。注意我們剛纔寫的simpleDll2這裏已經成爲第三方庫了。因爲開發一個軟件需要用到很多第三方庫,所以建立這樣一個文件夾進行統一管理是非常必要的。3rdparty文件夾裏面的結構如下:

include裏面放置simpleDll2.h,這個文件夾裏放置所有頭文件,這樣在工程設置時只用設置這一個路徑就行了。

simpleDll2裏面放置的是simpleDll2.lib。不同的庫就起個文件夾放置,或者也可以統一管理。我這裏是單獨放置的。

(2)回到上次目錄dev文件夾下,進入bin_Debug文件夾,將simpleDll2.dll放置進去。注意一會測試的工程要改生成路徑!

這個bin_Debug文件放置了執行程序(debug版本,release起名叫bin)和所需要的動態鏈接庫,也就是剛纔放置進去的dll們。

(3)proj就是新建的二次開發工程,裏面內容如下:

源碼在其自己的proj子文件夾下。

打開proj工程,右鍵單擊工程名稱,配置環境:

打開propoties,注意:在General選項頁中的Output directories裏面,刪掉原來的內容,改爲:..\..\bin_Debug

其它設置根據工程文件夾的結構,在VC++ Directories選項頁面裏,include Directories設置在最前面加上:..\..\3rdparty\include; Library Directories設置在最前面加上:..\..\3rdparty\simpleDll2; 在Linker --->  input 選項頁Additional Dependencies設置在最前面加上:simpleDll2.lib; 

完成配置。注意..\表示上一級,這個以proj.vcxproj位置爲準。因爲要往上兩級纔到dev根目錄,所以要兩個..\

在proj.cpp中的main函數中添加剛纔同樣的測試代碼,可以得到測試結果。

(4)上面三個文件實際上就夠用了,根本不需要simpleDll2.cpp. 但是因爲有的時候用戶或者有的要求要的,可以建立一個src文件夾,將這個cpp文件放進去,供二次開發參考,實際上裏面也沒有什麼有營養的東西。。。因爲主要的備註都會寫在頭文件裏了。

3、ubuntu16.04下--QtCreator創建庫工程並測試

qt和qtcreator下載的.run版本,安裝的時候輸入:

chmod a+x qt-opensource-linux-x64-5.4.1.run
./qt-opensource-linux-x64-5.4.1.run

發現竟然自動也把qtcreator裝好(沒裝我下載好的,而是裝了3.3.1,難道自動匹配對應的版本就安裝了?選擇的時候也沒有仔細看直接傻瓜安裝了哈哈哈)。

在ubuntu16.04下,建立新工程,選擇New——Library——c++ Library,點擊choose,然後根據嚮導設置路徑,其它設置就用默認的。建立的庫工程叫做qtlib,qt自動創建了qtlib.h和qtlib.cpp,以及一個qtlib_global.h用來指導你定義全局變量。

意思是在qtlib.cpp裏面寫導出函數,發現linux版本並不需要標示哪些是導出函數,直接在qtlib.cpp裏面加入函數就行了,(昨天大神指導說這就是linux牛逼的地方,會把所有輸出函數放在最前,不像windows是通過來尋找標示完成的。)我試了一下。比如加入簡單的:

int SampleAddInt(int i1, int i2)
{
    return i1 + i2;
}

後記插播:其實可以不用在qtlib.h中聲明。如果你擔心使用順序,可以在.h中加聲明。不加聲明也是可以用的。而且不管你加不加聲明,別的工程調用這個函數的時候,都得在調用的函數當前頁面重新聲明一下。。。

然後build下,生成了這麼一堆東西:

我們新建一個測試工程qt console, 叫做testlib, 創建好後我們在這裏調用qtlib中的函數,檢驗我們是否成功導出了上面那個函數。

右鍵testlib工程,選擇添加庫Add Library,彈出的對話框選擇external library,接下來選擇lib file和include file就可以了,注意lib選的時候,只出現了libqtlib.so那個文件,整個過程巨傻瓜巨好用,不由讚歎下。

注意前面說到的,如果用到庫中的哪個函數,還要在這個當前工程中聲明一下纔可以使用。

選好後build,然後debug就可以了,默認的編譯器是gcc(我的是64位和32位的都有),調試器是gdb。

測試完成。

 

4、ubuntu16.04下--codeblocks創建工程

如果覺得qt建立的lib把qt自己東西扯進來的太多,不夠簡潔,那麼可以試試超簡潔風的codeblocks。

安裝codeblocks: 打開終端,依次輸入下面三句就裝好了。

sudo add-apt-repository ppa:damien-moore/codeblocks-stable
sudo apt-get update
sudo apt-get install codeblocks codeblocks-contrib

在終端輸入codeblocks打開程序,新建工程cblib,選擇shared dll,嚮導指引建立cblib工程後,發現工程文件夾目錄下只有.cbp工程文件和一個main.cpp,可謂非常簡潔。

建立的代碼中自帶示例:

// The functions contained in this file are pretty dummy
// and are included only as a placeholder. Nevertheless,
// they *will* get included in the shared library if you
// don't remove them :)
// 
// Obviously, you 'll have to write yourself the super-duper
// functions to include in the resulting library...
// Also, it's not necessary to write every function in this file.
// Feel free to add more files in this project. They will be
// included in the resulting library.

extern "C"
{
    // A function adding two integers and returning the result
    int SampleAddInt(int i1, int i2)
    {
        return i1 + i2;
    }

    // A function doing nothing ;)
    void SampleFunction1()
    {
        // insert code here
    }

    // A function always returning zero
    int SampleFunction2()
    {
        // insert code here
        
        return 0;
    }
}

int SampleAddInt2(int i1, int i2)
{
    return i1 + i2;
}

第二段註釋大概意思就是說,你自己寫的函數可以不止放在這一個cpp裏,也可以自己加入更多的files,他們也會被包含在最終的庫中的。

注意我額外添加了一個函數放在最後。

build好後,只有一個libcblib.so文件和對應的main.o目標文件以及一個layout文件(不知道幹啥的,是不是就是導出函數排序之類的?),真是簡潔。

-------------------------------

下面建立一個新的工程用來測試這個so庫。

建立console工程testlib,也是隻有一個main.cpp文件

設置庫路徑的方式是:右鍵項目,選擇build options,左邊一欄注意選擇testlib即工程名,而不是選擇Debug或者release,剛開始不知道選擇了debug,設置好竟然沒有用,醉了。。。All right, 選擇工程名,右邊選擇Linker settings,左邊的link libraies窗口下面有個add,點擊,選擇好庫,然後add,可以根據需要選擇要相對還是絕對路徑,然後點擊ok完成設置。

我們測試下,在testlib的main.cpp中測試了加了extern c標記的SampleAddInt函數和未加標記的SampleAddInt2函數,發現不加標記的能用,加了標記的會報錯。。。在cblib的main.cpp中,extern C的作用是告訴編譯器,這部分代碼按C語言的格式進行編譯,而不是C++的。是否因爲gcc不編譯這部分?搞不清楚。。。汗一個

注意的是,在前面需要聲明一下函數。

#include <iostream>

using namespace std;

//int SampleAddInt(int i1, int i2);
int SampleAddInt2(int i1, int i2);

int main()
{
 //   cout<<"SampleAddInt(1,3)"<<SampleAddInt(1,3)<<endl;
    cout<<"SampleAddInt2(3,3)"<<SampleAddInt2(3,3)<<endl;
    return 0;
}

超簡潔風的codeblock感覺很棒呢!圈粉!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章