如何使用CppUnit进行单元测试

http://www.vckbase.com/document/viewdoc/?id=1762

一、前言

  测试驱动开发(TDD)是以测试作为开发过程的中心,它坚持,在编写实际代码之前,先写好基于产品代码的测试代码。开发过程的目标就是首先使测试能够通过,然后再优化设计结构。测试驱动开发式是极限编程的重要组成部分。XUnit,一个基于测试驱动开发的测试框架,它为我们在开发过程中使用测试驱动开发提供了一个方便的工具,使我们得以快速的进行单元测试。XUnit的成员有很多,如JUnit,PythonUnit等。今天给大家介绍的CppUnit即是XUnit家族中的一员,它是一个专门面向C++的测试框架。

本文不对CppUnit源码做详细的介绍,而只是对CppUnit的应用作一些介绍。你将看到:

  1. CppUnit源代码的各个组成部分;
  2. 怎样设置你的开发环境以能够使用CppUnit;
  3. 怎样为你的产品代码添加测试代码(实际上应该反过来,为测试代码添加产品代码。在TDD中,先有测试代码后有产品代码),并通过CppUnit来进行测试;

本文叙述背景为:CppUnit1.12.0, Visual C++ 6.0, WindowsXP。文中叙述有误之处,敬请批评指正。

一. CppUnit的安装
从http://sourceforge.net/projects/cppunit CppUnit的源码包. CppUnit是开源产品 , 当前最高版本为1.12.0. (在上面的链接所指向的页面上选择 Development Snapshot ). 
下载后,将源码包解压缩到本地硬盘,例如解压到E:/ cppunit-1.12.0。笔者把文件夹名称中的版本号去掉,即源码包解压缩到E:/cppunit。下载解压后,你将看到如下文件夹:

 picture1

主要的文件夹有:

  • doc: CppUnit的说明文档。另外,代码的根目录,还有三个说明文档,分别是INSTALL,INSTALL-unix,INSTALL-WIN32.txt;
  • examples: CpppUnit提供的例子,也是对CppUnit自身的测试,通过它可以学习如何使用CppUnit测试框架进行开发;
  • include: CppUnit头文件;
  • src: CppUnit源代码目录;
  • config:配置文件;
  • contrib:contribution,其他人贡献的外围代码;
  • lib:存放编译好的库;
  • src:源文件,以及编译库的project等;

  接下来进行编译工作。 在src/目录下, 将CppUnitLibraries.dsw工程文件用vc 打开。执行build/batch build,编译成功的话,生成的库文件将被拷贝到lib目录下。中途或者会有些project编译失败,一般不用管它,我们重点看的是cppunit和TestRunner 这两个project的debug和release版本。
  编译通过以后, 在lib/目录下,会生成若干lib,和dll文件, 都以cppunit开头. cppunitd表示debug版, cppunit表示release版。
CppUnit为我们提供了两套框架库,一个为静态的lib,一个为动态的dll。cppunit project:静态lib;cppunit_dll project:动态dll和lib。在开发中我们可以根据实际情况作出选择。
你也可以根据需要选择所需的项目进行编译,其中项目cppunit为静态库,cppunit_dll为动态库,生成的库文件为:

  • cppunit.lib:静态库release版;
  • cppunitd.lib:静态库debug版;
  • cppunit_dll.lib:动态库release版;
  • cppunitd_dll.lib:动态库debug版;

  另外一个需要关注的project是TestRunner,它输出一个dll,提供了一个基于GUI 方式的测试环境,在CppUnit下, 可以选择控制台方式和GUI方式两种表现方案。两种方案分别如下图所示:

picture2

 picture3

我们选择GUI方式,所以我们也需要编译这个project,输出位置亦为lib文件夹。
要使用CppUnit,还得设置好头文件和库文件路径,以VC6为例,选择Tools/Options/Directories,在Include files和Library files中分别添加%CppUnitPath%/include和%CppUnitPath%/lib,其中%CppUnitPath%表示CppUnit所在路径。本文这里分别填的是E:/CPPUNIT/INCLUDE和E:/CPPUNIT/LIB。

picture4

二、概念

在使用之前,我们有必要认识一下CppUnit中的主要类,当然你也可以先看后面的例子,遇到问题再回过头来看这一节。
CppUnit核心内容主要包括一些关键类:

Test:所有测试对象的基类。

CppUnit采用树形结构来组织管理测试对象(类似于目录树,如下图所示),因此这里采用了组合设计模式(Composite Pattern),Test的两个直接子类TestLeaf和TestComposite分别表示“测试树”中的叶节点和非叶节点,其中TestComposite主要起组织管理的作用,就像目录树中的文件夹,而TestLeaf才是最终具有执行能力的测试对象,就像目录树中的文件。

picture3

Test最重要的一个公共接口为:

virtual void run(TestResult *result) = 0;

其作用为执行测试对象,将结果提交给result。
在实际应用中,我们一般不会直接使用Test、TestComposite以及TestLeaf,除非我们要重新定制某些机制。

TestFixture:用于维护一组测试用例的上下文环境。

  在实际应用中,我们经常会开发一组测试用例来对某个类的接口加以测试,而这些测试用例很可能具有相同的初始化和清理代码。为此,CppUnit引入TestFixture来实现这一机制。
TestFixture具有以下两个接口,分别用于处理测试环境的初始化与清理工作:

virtual void setUp(); 
virtual void tearDown(); TestCase:测试用例,从名字上就可以看出来,它便是单元测试的执行对象。

  TestCase从Test和TestFixture多继承而来,通过把Test::run制定成模板函数(Template Method)而将两个父类的操作融合在一起,run函数的伪定义如下:

// 伪代码 
void TestCase::run(TestResult* result)
{result->startTest(this); // 通知result测试开始if( result->protect(this, &TestCase::setUp) ) // 调用setUp,初始化环境result->protect(this, &TestCase::runTest); // 执行runTest,即真正的测试代码result->protect(this, &TestCase::tearDown); // 调用tearDown,清理环境result->endTest(this); // 通知result测试结束
} 

这里要提到的是函数runTest,它是TestCase定义的一个接口,原型如下:

virtual void runTest(); 

用户需从TestCase派生出子类并实现runTest以开发自己所需的测试用例。
另外还要提到的就是TestResult的protect方法,其作用是对执行函数(实际上是函数对象)的错误信息(包括断言和异常等)进行捕获,从而实现对测试结果的统计。

TestSuit:测试包,按照树形结构管理测试用例

TestSuit是TestComposite的一个实现,它采用vector来管理子测试对象(Test),从而形成递归的树形结构。

TestFactory:测试工厂

这是一个辅助类,通过借助一系列宏定义让测试用例的组织管理变得自动化。参见后面的例子。

TestRunner:用于执行测试用例

TestRunner将待执行的测试对象管理起来,然后供用户调用。其接口为:

virtual void addTest( Test *test ); 
virtual void run( TestResult &controller, const std::string &testPath = "" ); 

  这也是一个辅助类,需注意的是,通过addTest添加到TestRunner中的测试对象必须是通过new动态创建的,用户不能删除这个对象,因为TestRunner将自行管理测试对象的生命期。

三、CppUnit 的使用

  以上工作完成以后,就可以正式使用CppUnit了,由于单元测试是TDD(测试驱动开发)的利器,一般人会先写测试代码,然后再写产品代码,不过笔者认为先写产品代码框架后再写测试代码,然后通过慢慢补充产品代码以使得能通过测试的方法会好些。不管先写谁只要写得舒服安全就可以。本文决定先写测试代码。
  前面我们提到过,CppUnit最小的测试单位是TestCase,多个相关TestCase组成一个TestSuite。要添加测试代码最简单的方法就是利用CppUnit为我们提供的几个宏来进行(当然还有其他的手工加入方法,但均是殊途同归,大家可以查阅CppUnit头文件中的演示代码)。这几个宏是:

CPPUNIT_TEST_SUITE() 开始创建一个TestSuite;
CPPUNIT_TEST() 添加TestCase;
CPPUNIT_TEST_SUITE_END() 结束创建TestSuite;
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION() 添加一个TestSuite到一个指定的TestFactoryRegistry工厂。

感兴趣的朋友可以在HelperMacros.h看看这几个宏的声明,本文在此不做详述。

假定我们要实现一个类,类名暂且取做CPlus,它的功能主要是实现两个数相加(多简单的一个类啊,这也要测试吗?不要紧,我们只是了解怎样加入测试代码来测试它就行了,所以越简单越好)。 假定这个类要实现的相加的方法是:

int Add(int nNum1, int nNum2);

  OK,那我们先来写测试这个方法的代码吧。TDD 可是先写测试代码,后写产品代码(CPlus)的哦!先写的测试代码往往是不能运行或编译的,我们的目标是在写好测试代码后写产品代码,使之编译通过,然后再进行重构。这就是Kent Beck说的“red/green/refactor”。所以,上面的类名和方法应该还只是在你的心里,还只是你的idea而已。
  根据测试驱动的原理,我们需要先建立一个单元测试框架。我们在VC中为测试代码建立一个project。通常,测试代码和被测试对象(产品代码)是处于不同的project中的。这样就不会让你的产品代码被测试代码所“污染 ”。
由于在CppUnit下, 可以选择控制台方式和UI方式两种表现方案,我们选择UI方式。在本例中,我们将建立一个基于GUI 方式的测试环境。因此我们建立一个基于对话框的Project。假设名为UnitTest。
建立了UnitTest project之后,我们首先配置这个工程。
首先在project中打开RTTI开关,具体位置在菜单Project/Settings/C++/C++ Language。如下图所示设置:

picture6

  由于CppUnit所用的动态运行期库均为多线程动态库,因此你的单元测试程序也得使用相应设置,否则会发生冲突。于是我们在Project/Settings/C++/Code Generation中进行如下设置:
  在Use run-time library一栏中,针对debug和release分别设置为‘Debug Multithreaded DLL’和‘Multithreaded DLL’。如下图所示:

picture7

picture8

  最后别忘了在project中link正确的lib。包括本例采用的cppunit.lib和cppunitd.lib静态库以及用于GUI方式的TestRunner.dll对应的lib。具体位置在Project/Settings/Link/General
  在‘Object/library modules’中,针对debug和release分别加入cppunitd.lib testrunnerd.lib和cppunit.lib TestRunner.lib。如下图所示:

picture9

picture10

  最后,由于TestRunner.dll为我们提供了基于GUI的测试环境。为了让我们的测试程序能正确的调用它,TestRunner.dll必须位于你的测试程序的路径下。所以把/lib目录下的testrunnerd.dll和TestRunner.dll文件分别拷贝到UnitTest priject的程序debug和release版本输出目录中。如下图所示:

picture11

(这是release版本)只要放在一起就可以了。
配置工作终于完成,下面开始写测试框架。

在CppUnit中, 是以TestCase为最小的测试单位, 若干TestCase组成一个TestSuite。所以我们要先建立一个TestCase。
在UnitTest project中新建一个类, 命名为CPlusTestCase, 让其从CppUnit::TestCase派生。为其新增一个方法,假设为 void testAdd(); 我们将在这个函数中写入我们的一些测试代码(还记得我们要测试的构想中的CPlus::Add(…)吗)。代码如下:切记要包含头文件

#include <cppunit/TestCase.h>
class CPlusTestCase : public CppUnit::TestCase
{
public:CPlusTestCase ();virtual ~ CPlusTestCase ();void testAdd();
};

接下来, 我们要对我们的CPlusTestCase进行声明。声明用到了三个宏.

    CPPUNIT_TEST_SUITE();CPPUNIT_TEST();CPPUNIT_TEST_SUITE_END();

第一个宏声明一个测试包(suite),第二个宏声明(添加)一个测试用例. 现在我们的CPlusTestCase类看上去象这样:切记要包含头文件,否则无法识别这些宏。

#include <cppunit/TestCase.h>
#include <CppUnit/extensions/HelperMacros.h>class CPlusTestCase : public CppUnit::TestCase
{CPPUNIT_TEST_SUITE(CPlusTestCase);CPPUNIT_TEST(testAdd);CPPUNIT_TEST_SUITE_END();
public:CPlusTestCase ();virtual ~ CPlusTestCase ();void testAdd();
};

通过这几个宏,我们就把CPlusTestCase和testAdd注册到了测试列表当中。

  接下来,我们要注册我们的测试suite. 使用CPPUNIT_TEST_SUITE_NAMED_REGISTRATION()来注册一个测试suite. 这个宏的第二个参数是我们注册的suite的名字. 在这里我们可以用字符串来代替, 但我们用一个静态函数来返回这个suite的名字.

// PlusTestCase.h 
class CPlusTestCase : public CppUnit::TestCase
{CPPUNIT_TEST_SUITE(CPlusTestCase);CPPUNIT_TEST(testAdd);CPPUNIT_TEST_SUITE_END();
public:CPlusTestCase ();virtual ~ CPlusTestCase ();void testAdd();static std::string GetSuiteName();
};// PlusTestCase.cppstd::string CPlusTestCase::GetSuiteName(){return " CPlus ";}

记得要在PlusTestCase.h中包含 #include <string> 
然后在 PlusTestCase.cpp注册我们的suite.

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CPlusTestCase, CPlusTestCase::GetSuiteName());

它将CPlusTestCase这个TestSuite注册到一个指定的TestFactory工厂中。
接下来我们写一个注册函数static CppUnit::Test* GetSuite(), 使其在运行期生成一个Test.

		
// PlusTestCase.h 
class CPlusTestCase : public CppUnit::TestCase
{CPPUNIT_TEST_SUITE(CPlusTestCase);CPPUNIT_TEST(testAdd);CPPUNIT_TEST_SUITE_END();
public:CPlusTestCase ();virtual ~ CPlusTestCase ();void testAdd();static std::string GetSuiteName();static CppUnit::Test* GetSuite();
};// PlusTestCase.cppCppUnit::Test* CPlusTestCase::GetSuite(){CppUnit::TestFactoryRegistry& reg = CppUnit::TestFactoryRegistry::getRegistry (CPlusTestCase::GetSuiteName());return reg.makeTest();}

记住在PlusTestCase.h中包含头文件:

#include <cppunit/extensions/TestFactoryRegistry.h>

最后, 我们为单元测试建立一个UI测试界面.

  由于我们希望这个Project运行后显示的是GUI界面,所以我们需要在App的 InitInstance ()中屏蔽掉原有的对话框,代之以CppUnit的GUI。
我们在CUnitTestApp::InitInstance()函数中,将原先显示主对话框的代码以下面的代码取代:

		
CppUnit::MfcUi::TestRunner runner;runner.addTest(CPlusTestCase::GetSuite());//添加测试runner.run();//show UI
/*	CUnitTestDlg dlg;m_pMainWnd = &dlg;int nResponse = dlg.DoModal();if (nResponse == IDOK){// TODO: Place code here to handle when the dialog is//  dismissed with OK}else if (nResponse == IDCANCEL){// TODO: Place code here to handle when the dialog is//  dismissed with Cancel}
*/

切记必须先在UnitTest.cpp中包含头文件:

#include <cppunit/ui/mfc/TestRunner.h>
#include " PlusTestCase.h "

到此为止, 我们已经建立好一个简单的单元测试框架。测试框架虽然写好了,但是测试代码仍然为空,产品代码也还没有写。下面我们来写测试代码: 
如前所述,在测试类中,我们添加了一个测试方法:

void testAdd();

它测试的对象是前面提到的CPlus类的方法:int Add(int nNum1, int nNum2);(产品代码)我们来看看testAdd()的实现:记得在PlusTestCase.h中包含头文件

#include <cppunit/TestAssert.h>
// PlusTestCase.cpp
void CPlusTestCase::testAdd()
{CPlus plus;int nResult = plus.Add(10, 20); //执行Add操作CPPUNIT_ASSERT_EQUAL(30, nResult); //检查结果是否等于30	
}

CPPUNIT_ASSERT_EQUAL是一个判断结果的宏。CppUnit中类似的其它宏请查阅TestAssert.h,本文在此不做详述 。

  另外,我们还可以覆写基类的 setUp()、tearDown()两个函数。这两个函数实际上是一个模板方法,在测试运行之前会调用setUp()以进行一些初始化的工作,测试结束之后又会调用tearDown()来做一些“善后工作” ,比如资源的回收等等。当然,你也可以不覆写这两个函数,因为它们在基类里定义成了空方法,而不是纯虚函数。 
  编写完上面的测试代码后,进行编译。编译肯定通不过,编译器会告诉我们CPlus类没有声明,因为我们还没有实现CPlus类呢!现在的工作就是马上实现CPlus类,让编译通过。现在你应该嗅到一点“测试驱动”(Test Driven Develop)的味道了吧?

在VC中建立一个MFC Extension Dll的Project,在这个Project 中加入类CPlus,它的声明如下:

		
// Plus.h
class AFX_EXT_CLASS CPlus
{
public:CPlus();virtual ~CPlus();
public:int Add(int nNum1, int nNum2);
};

Add在Plus.cpp中实现如下

		
int CPlus::Add(int nNum1, int nNum2)
{return nNum1 + nNum2;//这里可以写一些错误的语句,用来看看错误的结果
}

非常简单,不是吗?现在让前面那个包含测试代码的Project dependent这个Project,并且include 相关头文件 ,Rebuild All,你会发现编译已通过。你体会到了测试代码驱动产品代码了吗?当然我们的这个例子还很简单 ,没有重构这一步骤。

运行我们的测试程序,单击Browse,你就会看到如下图所示的界面:

picture12


选择CPlusTestCase::testAdd后,单击Run,你就会看到如下图所示的界面:

picture13

  这下你应该对前面我们说的TestSuite的名字理解更深了吧。CPlus是一个测试包TestSuite,它的下面包含一个测试用例,这个测试用例下面又包含一个测试方法。

如果我修改CPlus::Add的代码如下:

int CPlus::Add(int nNum1, int nNum2)
{
//	return nNum1 + nNum2;return 2;	
}

重新编译通过,运行程序就会发现:

picture14

GUI显示有一个单元测试不通过,并显示出错的地方和原因,这样就很好的控制Bug了。


四、下面是完整的程序清单

// PlusTestCase.h
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <string>
#include <cppunit/TestCase.h>
#include <CppUnit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/TestAssert.h>
class CPlusTestCase : public CppUnit::TestCase  
{//通过这几个宏,我们就把CPlusTestCase和testAdd注册到了测试列表当中.CPPUNIT_TEST_SUITE(CPlusTestCase);		//声明一个测试包CPPUNIT_TEST(testAdd);			//声明一个测试用例CPPUNIT_TEST_SUITE_END();	
public:CPlusTestCase();virtual ~CPlusTestCase();void testAdd(); //测试方法static std::string GetSuiteName();//写一个注册函数, 使其在运行期生成一个Teststatic CppUnit::Test* GetSuite();
};
// PlusTestCase.cpp
#include "stdafx.h"
#include "UnitTest.h"
#include "PlusTestCase.h"
#include "plus.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//注册一个测试suite到一个指定的TestFactory工厂中
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CPlusTestCase, CPlusTestCase::GetSuiteName());
//
// Construction/Destruction
//
CPlusTestCase::CPlusTestCase()
{
}
CPlusTestCase::~CPlusTestCase()
{
}
void CPlusTestCase::testAdd()
{CPlus plus;int nResult = plus.Add(10, 20); //执行Add操作CPPUNIT_ASSERT_EQUAL(30, nResult); //检查结果是否等于30	
}
std::string CPlusTestCase::GetSuiteName()
{return "CPlus";
}
/**	注意:CPlusTestCase::GetSuite()返回一个指向CppUnit::Test的指针.*  这个指针就是整个测试的起点。*  CppUnit::TestFactoryRegistry::getRegistry()根据TestSuite的名字返回TestFactoryRegistry工*  然后调用工厂里的makeTest()对TestSuite进行组装,将建立起一个树状的测试结构。*/
CppUnit::Test* CPlusTestCase::GetSuite()
{CppUnit::TestFactoryRegistry& reg = CppUnit::TestFactoryRegistry::getRegistry(CPlusTestCase::GetSuiteName());return reg.makeTest();
}
// UnitTest.cpp
#include "stdafx.h"
#include "UnitTest.h"
#include <cppunit/ui/mfc/TestRunner.h>
#include "PlusTestCase.h"
…
/
// CUnitTestApp initialization
BOOL CUnitTestApp::InitInstance()
{…CppUnit::MfcUi::TestRunner runner;runner.addTest(CPlusTestCase::GetSuite()); //添加测试	runner.addTest(CMinusTestCase::GetSuite());runner.run(); //show UI
/*	CUnitTestDlg dlg;m_pMainWnd = &dlg;int nResponse = dlg.DoModal();if (nResponse == IDOK){// TODO: Place code here to handle when the dialog is//  dismissed with OK}else if (nResponse == IDCANCEL){// TODO: Place code here to handle when the dialog is//  dismissed with Cancel}
*/return FALSE;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458698.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iOS开发网络篇—Reachability检测网络状态

前言&#xff1a;当应用程序需要访问网络的时候&#xff0c;它首先应该检查设备的网络状态&#xff0c;确认设备的网络环境及连接情况&#xff0c;并针对这些情况提醒用户做出相应的处理。最好能监听设备的网络状态的改变&#xff0c;当设备网络状态连接、断开时&#xff0c;程…

网络七层协议 五层模型 TCP连接 HTTP连接 socket套接字

socket&#xff08;套接字&#xff09;是通信的基石&#xff0c;是支持TCP/IP协议的网络通信的基本操作单元&#xff0c;包含进行网络通信必须的五种信息&#xff1a;连接使用的协议&#xff0c;本地主机的IP地址&#xff0c;本地进程的协议端口&#xff0c;远地主机的IP地址&a…

【VC++技术杂谈005】如何与程控仪器通过GPIB接口进行通信

在工控测试系统中&#xff0c;经常需要使用到各类程控仪器&#xff0c;这些程控仪器通常具有GPIB、LAN、USB等硬件接口&#xff0c;计算机通过这些接口能够与其通信&#xff0c;从而实现自动测量、数据采集、数据分析和数据处理等操作。本文主要介绍如何与程控仪器通过GPIB接口…

标题在上边框中的html(fieldset标签)

<fieldset> <legend>标题</legend> 内容 </fieldset> 转载于:https://www.cnblogs.com/lswbk/p/4952820.html

移除项目中的CocoaPods

在项目中移除CocoaPods cocoaPods虽然很方便&#xff0c;但是我是真心的不喜欢用它&#xff0c;总是出错如果你觉得CocoaPods让你的项目出现了问题&#xff0c;不好用甚至是恶心&#xff0c;想将其从项目中彻底移除&#xff0c;也有方法&#xff1a; 1.删除工程文件夹下的Podf…

popoverController(iPad)

一、设置尺寸 提示&#xff1a;不建议&#xff0c;像下面这样吧popover的宽度和高度写死。 1 //1.新建一个内容控制器2 YYMenuViewController *menuVc[[YYMenuViewController alloc]init];3 4 //2.新建一个popoverController&#xff0c;并设置其内容控制器5 s…

fastQC

Fastqc用途 FastQC aims to provide a simple way to do some quality control checks on raw sequence data coming from high throughput sequencing pipelines. It provides a modular set of analyses which you can use to give a quick impression of whether your data …

C++ Handle(句柄) part1

本文是我学习C&#xff0b;&#xff0b;沉思录第6章的笔记 本文主要讲述了Handle类的概念&#xff0c;定义方法以及写时复制技术。 在前文(Surrogate代理类)的讲解中我们了解到了代理的实现方法. 代理类有很多好处,但是麻烦的是每次都得进行复制.如果该类是经常使用并且member很…

Swift调用Objective C的FrameWork

很多Github的库经过很多年的发展&#xff0c;源码都是OC写的&#xff0c;&#xff0c;所以&#xff0c;用Swift调用OC的库就是开发中难免遇到的的一个问题&#xff0c;本文以AFNetworking为例&#xff0c;讲解如何跨语言调用。 第一步 创建一个空的工程 注意&#xff0c;语言选…

显式(静态)调用: LIB + DLL + .H

1、编程时用ad.h,ad.lib,放在项目当前目录里2、在头文件中加入#include "ad.h"3、在Project Setting–>Link–>Object/library modules加入ad.lib执行时将ad.dll跟你的程序放在同一目录。 就可以直接调用dll中的函数了 当前目录 转载于:https://www.cnblogs.co…

接入支付宝出现交易订单处理失败,请稍后再试(ALI64)的错误

上次在接入支付宝的时候就碰到了交易订单处理失败&#xff0c;请稍后再试&#xff08;ALI64&#xff09;这样的错误&#xff0c;后来经过排查和总结&#xff0c;一般来讲这种问题都是公钥和私钥没有正确配置造成的。支付宝这边为了保证数据在传输时不被篡改&#xff0c;使用了r…

支付宝集成交互流程

交互流程 功能流程 流程说明&#xff08;以Android平台为例&#xff09;&#xff1a; 第4步&#xff1a;调用支付接口&#xff1a;此消息就是本接口所描述的开发包提供的支付对象PayTask&#xff0c;将商户签名后的订单信息传进pay方法唤起支付宝收银台&#xff0c;订单格式具体…

VxLAN基础

转自&#xff1a;http://blog.csdn.net/freezgw1985/article/details/16354897 一 . 为什么需要Vxlan1. vlan的数量限制4096个vlan远不能满足大规模云计算数据中心的需求2. 物理网络基础设施的限制基于IP子网的区域划分限制了需要二层网络连通性的应用负载的部署3. TOR交换机MA…

C语言程序代码优化

我认为一个好的用于科学计算的程序代码应该&#xff1a;算法漂亮精妙&#xff0c;程序简洁易懂&#xff0c;运算快速&#xff0c;节省内存。这里有的地方是矛盾的&#xff0c;比如简洁vs易懂&#xff0c;时间vs空间&#xff0c;找个平衡吧。目前来看时间要比空间宝贵一些。写程…

如何在苹果官网下载旧版本的Xcode 方法

1 在百度里输入“苹果开发者中心“&#xff0c;进入以下页面。点击页面中的“Member Center" 2 出现登录界面。这是需要苹果开发者帐号的&#xff0c;没有帐号的可以选择“Create Apple ID”进行注册。已经注册的选择“Sign In"登录 3 页面跳转后&#xff0c;选择…

【转】android多分辨率适配

前一阶段开发android项目&#xff0c;由于客户要求进行多分辨率适配&#xff0c;能够支持国内主流的分辨率手机。因此经过了几次开发走了很多弯路&#xff0c;目前刚刚领略了android多分辨率适配的一些方法。 先介绍一下所走的弯路&#xff0c;由于android的布局文件存放在res的…

Java线程池介绍

根据摩尔定律&#xff08;Moore’s law&#xff09;&#xff0c;集成电路晶体管的数量差不多每两年就会翻一倍。但是晶体管数量指数级的增长不一定会导致 CPU 性能的指数级增长。处理器制造商花了很多年来提高时钟频率和指令并行。在新一代的处理器上&#xff0c;单线程程序的执…

cocoa pods的安装与我遇到的问题

1.打开终端 终端输入 ruby -v 查看ruby的版本 打印代码&#xff1a; ruby 2.0.0p648 (2015-12-16 revision 53162) [universal.x86_64-darwin15] 2. 更换ruby镜像 终端输入如下命令&#xff08;把Ruby镜像指向taobao&#xff0c;避免被墙&#xff0c;你懂得&#xff09; a.移…

Implicit declaration of function 'NSFileTypeForHFSTypeCode' is invalid in C99

一般出现该问题是因为通过C调用了unix/linux 底层接口&#xff0c;所以需要调整c语言的编译选项&#xff0c;设置方法见下图&#xff1a;(根据实际情况选择相应的编译选项)

DebugView 调试入门

参考链接&#xff1a;http://blog.csdn.net/jiankunking/article/details/44984487 软件下载地址&#xff1a;点击打开链接 debugview 可以捕获程序中由TRACE(debug版本)和OutputDebugString输出的信息。支持Debug、Release模式编译的程序&#xff08;即该软件捕获的是exe直接运…