ACE_Export与自定义导出符号(结贴)

时间:2022-05-29 23:17:02

     不得不佩服ACE的跨平台强大之处

     如果你需要在Windows下创建dll项目供其他项目使用。

     你创建dll项目的代码要导出才能被外部访问,这个是由于Windows的动态链接库默认访问级别为私有导致的,所以只有导出的接口才会被外部访问。

当你打算导出的时候只需要使用ACE自带的导出宏即可:

使用ACE导出符号头文件

头文件应该像这个样子:

#ifndef CONFIGLOADER_H
#define CONFIGLOADER_H

#include "ace/Log_Msg.h"


class ACE_Export ConfigLoader
{
public:
ConfigLoader(void);
~ConfigLoader(void);
void load_from_file(void);
void load_from_db(void);
};

#endif


源文件正常,无需任何变动:

/************************************************************************/
/* 配置加载类 */
/************************************************************************/
#include "ConfigLoader.h"


ConfigLoader::ConfigLoader(void)
{
}


ConfigLoader::~ConfigLoader(void)
{
}

void ConfigLoader::load_from_db(void)
{
ACE_DEBUG((LM_DEBUG,"ConfigLoader::load_from_db(void) run"));
}

void ConfigLoader::load_from_file(void)
{
ACE_DEBUG((LM_DEBUG,"ConfigLoader::load_from_file(void) run"));

}

但是我发现当有继承关系的时候,尤其是基类是抽象基类但也要导出的时候,比如有virtual函数的时候这种方式就不行了,要用下面的这种方式(单独指定导出某个具体的函数,而不是整个类):

#ifndef COMMUNICATER_H
#define COMMUNICATER_H

#include "ace/ACE_export.h"

class Communicater
{
public:
ACE_Export Communicater(void);
ACE_Export virtual void run_collect() = 0;
ACE_Export virtual ~Communicater(void);
};

#endif

ACE的等价代码

#ifndef COMMUNICATER_H
#define COMMUNICATER_H

//#include "ace/ACE_export.h"

#ifdef COMMUNICATER_EXPORTS
#define COMMUNICATER_API __declspec(dllexport)
#else
#define COMMUNICATER_API __declspec(dllimport)
#endif

class Communicater
{
public:
COMMUNICATER_API Communicater(void);
COMMUNICATER_API virtual void run_collect() = 0;
COMMUNICATER_API virtual ~Communicater(void);
};

#endif



反正就是要对每个接口单个导出,而不是导出类,不知道为何


即可编译生成dll,供其他项目使用,多棒!

具体操作参考:http://blog.csdn.net/calmreason/article/details/6989390中官方网站教程(主要是VC++工程的“项目引用”功能 )

 

 

自己实现导出符号

实现功能: 源文件可以在Windows应用程序中使用(直接包含源文件即可) 源文件可以在Windows生成动态链接库程序中使用(需在预编译的时候定义宏SCP_EXPORTS) 源文件可以在Linux应用程序中使用(直接包含源文件即可) 源文件可以在Linux生成动态链接库程序使用(直接包含源文件即可)

由于ACE的导出符号多少针对ACE特定的要求,而且针对不同平台其导出宏我们不好确定是哪一个,所以,为了简单起见,自己也模仿定义了自己的导出符号:

头文件

SCP_Export.h头文件

#ifndef SCP_EXPORT_H
#define SCP_EXPORT_H

#ifdef WIN32 //for windows
#ifdef SCP_EXPORTS //for windows generate dll,you have to define SCP_EXPORTS in your preprocessor
#define SCP_Export __declspec(dllexport)
#else
#define SCP_Export //for windows as source file
#endif
#else //for Linux
#ifdef SCP_EXPORTS //for Linux as source file
#define SCP_Export
#else
#define SCP_Export //for Linux generate dll
#endif
#endif

#endif

使用自定义导出符号:

无论你是想以源文件方式使用自己的类,还是打算将自己的类放到dll里面,你都只需要做两件事(1)(2)

(1)可以在自己的类的头文件中包含SCP_Export.h头文件

(2)在有可能会被导出的地方加上SCP_Export声明

(3)只有一种情况:windows下,你打算把自己的类放到dll中,这时候你需要在preprocessor里加上SCP_EXPORTS预编译符号,表示你的类在dll里面是可以被外部的程序调用的。

在普通类使用导出符号示例:

CollectorInfor.h

/************************************************************************/
/* 采集器实体类 */
/************************************************************************/
#ifndef COLLECTORINFOR_H
#define COLLECTORINFOR_H

#include <string>
using namespace std;

#include "ace/INET_Addr.h"

#ifndef SCP_EXPORT_H
#include "SCP_Export.h"
#endif

class SCP_Export CollectorInfor
{
public:
CollectorInfor(void);
~CollectorInfor(void);
u_short get_port_number (void) const;
const char *get_host_name (void) const;
const string to_string(void) const;
private:
ACE_INET_Addr local_addr;
};

#endif


包含虚函数的基类使用导出符号的示例:

#ifndef COMMUNICATER_H
#define COMMUNICATER_H

#include <vector>
using namespace std;

#include "ace/INET_Addr.h"
#include "ace/Task.h"

#ifndef COMMUNICATERINFOR_H
#include "CommunicaterInfor.h"
#endif

#ifndef SCP_TYPE_H
#include "scp_type.h"
#endif

#ifndef SCP_EXPORT_H
#include "SCP_Export.h"
#endif

#ifndef MESSAGEQUEUE_H
#include "MessageQueue.h"
#endif

class Communicater
{
public:
//派生类必须实现的方法
SCP_Export virtual void run_collect(void) = 0;
SCP_Export const CommunicaterInfor& get_infor(void);
//派生类必须实现的方法
SCP_Export Communicater(const CommunicaterInfor&);
SCP_Export virtual ~Communicater(void);

protected:
vector<id_type> has_collector_id_;
const CommunicaterInfor& infor_;
};
//派生类必须实现的方法
extern "C" SCP_Export Communicater* CreateCommunicater(CommunicaterInfor&);

#endif


派生类使用导出函数:

Communicater_103.h

#ifndef COMMUNICATER_103_H
#define COMMUNICATER_103_H

#include <string>
using namespace std;

#include "ace/Log_Msg.h"
#include "ace/Event_Handler.h"
#include "ace/Date_Time.h"
#include "ace/WIN32_Proactor.h"
#include "ace/INET_Addr.h"
#include "ace/SOCK_Dgram.h"
#include "ace/Message_Block.h"
#include "ace/OS.h"
#include "ace/Proactor.h"
#include "ace/Task.h"
#include "ace/Asynch_Acceptor.h"

#include "SCP_Export.h"
#ifndef COMMUNICATER_H
#include "Communicater.h"
#endif

#ifndef SCP_TYPE_H
#include "scp_type.h"
#endif

#ifndef CONFIGLOADER_H
#include "ConfigLoader.h"
#endif

#ifndef GLOBAL_H
#include "global.h"
#endif

/*8 链路传输过程
8.1 客户机/服务器模型
系统采用标准TCP/IP 的客户机-服务器模型进行通信。应用服务数据单元(ASDU)报
文使用高可靠性的TCP 数据流传输,主站(监控/远动)作为服务器端在约定的1048(418H)
号TCP 端口上被动侦听等待,子站(保护/测控装置)作为客户端主动发起连接。TCP 传输
的数据保证可靠性但无报文边界,因而需要在接收方进行适当的解粘包处理。
为了适应多主站情况,系统通过辅助UDP 链路实现主站IP 的动态识别。在此辅助UDP
链路中,子站作为服务器端在约定的1032(408H)号UDP 端口被动等待接收,主站作为客
户端主动发送UDP 广播报文。子站接收UDP 报文后获得主站IP 地址信息可用来发起TCP
连接,同时可取得时间信息进行时钟同步。
*/


/**
* @class UDPSender
*
* @brief The class will be created by <main>.
*/
class UDPSender : public ACE_Handler
{
public:
UDPSender (void);
~UDPSender (void);

//FUZZ: disable check_for_lack_ACE_OS
///FUZZ: enable check_for_lack_ACE_OS
int open (const ACE_TCHAR *host, u_short port);

protected:
// These methods are called by the freamwork

/// This is called when asynchronous writes from the dgram socket
/// complete
virtual void handle_write_dgram (const ACE_Asynch_Write_Dgram::Result &result);

private:

/// Network I/O handle
ACE_SOCK_Dgram sock_dgram_;

/// wd (write dgram): for writing to the socket
ACE_Asynch_Write_Dgram wd_;

};

class Communicater_103 : public Communicater ,public ACE_Service_Handler
{
public:
Communicater_103(const CommunicaterInfor&);
~Communicater_103(void);
void run_collect(void);

private:

UDPSender udp_sender;
ACE_Asynch_Read_Stream reader_;
};

extern "C" SCP_Export Communicater* CreateCommunicater(CommunicaterInfor&);

#endif

名称空间中的函数和类的导出:

#ifndef GLOBAL_H
#define GLOBAL_H

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;

#ifndef SCP_EXPORT_H
#include "SCP_Export.h"
#endif

namespace global
{

int SCP_Export string_to_id(const string& dll_name);

string SCP_Export to_hex_string(const string& temp_string);

string SCP_Export long_long_to_string(long long id);

template<typename Result,typename Para>
Result SCP_Export lexical_cast(Para para)
{
stringstream ss;
ss<<para;
Result result;
ss>>result;
return result;
}

template<typename T>
void SCP_Export print(T begin,T end)
{
while(begin != end)
{
cout<<*begin++<<" ";
}
cout<<endl;
};

}

#endif


在编译环境中添加导出符号宏:


 有了以上代码文件的准备,只需要在你用到这些代码的项目中添加导出宏即可使用:右键项目名》属性》Configuration Properties》C/C++》Preprocessor》Preprocessor Definitions》添加宏SCP_EXPORTS

这样你的项目就可以生成一个dll供其他项目使用,在其他项目中只需要包含你定义的头文件,并引用此项目即可,具体见:http://blog.csdn.net/calmreason/article/details/6989390