2012年12月24日月曜日

DLL内のテンプレートクラスを呼び出す方法

別DLLで定義したテンプレートクラスをmain関数で呼び出すときにリンカーに怒られました。これを躱すには明示的にテンプレートクラスをインスタンス化するという方法があるみたいです。以下はその例です。

LoggerSingletonクラスはSingletonテンプレートクラスをバインドしています。Singletonクラスは静的メンバ関数を経由してインスタンスにアクセスする機能を提供します。Log.cppの末尾で明示的テンプレートとして定義するとDLL外からも呼び出すことができます。リンクエラーの原因は単純に実装部が公開されていなかったからだけですね。つまり逆に言えば、この方法であれば実装部を隠ぺいすることができたことになり、より疎結合になったと言えます。

エントリポイント
///
/// main.cpp
///
#include <iostream>
#include "Logger.h"

int _tmain(int argc, _TCHAR* argv[])
{
 // インスタンスを取得
 Test::Logger* logger = Test::LoggerSingleton::Instance();

 // 記録
 logger->Record("Test 1");
 logger->Record("Test 2");
 logger->Record("Test 2");

 // 記録を出力
 std::cout << logger->Message();

 return 0;
}

シングルトンパターンを提供するテンプレートクラス
///
/// Singleton.h
///
#ifndef _INC_SINGLETON_H_
#define _INC_SINGLETON_H_

#ifdef _TEST
#define TEST_API __declspec(dllexport)
#else
#define TEST_API __declspec(dllimport)
#endif

namespace Test {

template <typename T>
class TEST_API Singleton
{
private:
 T* _d;

public:
 ~Singleton();
 static T* Instance();

private:
 // インスタンス化をガード
 Singleton();
 Singleton(const Singleton<T>& other);
 Singleton<T>& operator=(const Singleton<T>& other);
};

}
#endif

テンプレートクラスの実装部
///
/// SingletonImp.h
///
#ifndef _INC_SINGLETON_IMP_H_
#define _INC_SINGLETON_IMP_H_

#include "Singleton.h"

namespace Test {

template <typename T>
Singleton<T>::~Singleton()
{
 delete this->_d;
}

template <typename T>
T* Singleton<T>::Instance()
{
 static Singleton<T> singleton;
 return singleton._d;
}

template <typename T>
Singleton<T>::Singleton() : _d(new T)
{
}

}
#endif

テンプレートクラスの明示的インスタンス化
///
/// Logger.h
///
#ifndef _INC_LOGGER_H_
#define _INC_LOGGER_H_

#include <string>
#include <vector>
#include "Singleton.h"

namespace Test {

class TEST_API Logger
{
private:
 typedef std::vector<std::string*> Table;

 Table _table;

public:
 Logger();
 ~Logger();
 void Record(const std::string& msg);
 std::string Message();
};

typedef Test::Singleton<test::logger> LoggerSingleton;

}
#endif

///
/// Log.cpp
///
#include "SingletonImp.h"
#include "Logger.h"

namespace Test {

Logger::Logger()
{
}

Logger::~Logger()
{
 for(Table::iterator it = this->_table.begin(); it != this->_table.end(); ++it)
 {
  // インスタンスを解放
  delete *it;
 }
}

void Logger::Record(const std::string& msg)
{
 // インスタンスをコピーして格納
 this->_table.push_back(new std::string(msg));
}

std::string Logger::Message()
{
 std::string msg;

 // メッセージの作成
 for(Table::iterator it = this->_table.begin(); it != this->_table.end(); ++it)
 {
  msg += *(*it);
  msg += "\n";
 }

 return msg;
}

// 明示的テンプレートのインスタンス化
template Test::Singleton<test::logger> LoggerSingleton;
}

0 件のコメント:

コメントを投稿