template 関数をDLL にしたいのですが – プログラミング – Home

template 関数をDLL にした...
 
通知
すべてクリア

[解決済] template 関数をDLL にしたいのですが


Sum
 Sum
(@Sum)
ゲスト
結合: 13年前
投稿: 6
Topic starter  

よろしくおねがいします。
環境 Windows 7 Visual Studio 2010 pro

DLL からtemplate 関数を導出するのに次のテストコードを試しているのですが。
下記エラーが発生します、ご教示お願いします。

main.obj : error LNK2019: 未解決の外部シンボル public: void __thiscall
NCPrint::CPrint::operator()<class std::basic_string<char,struct
std::char_traits<char>,class std::allocator<char> > >(class
std::basic_string<char,struct std::char_traits<char>,class std::allocator<char>
> const &) (??$?RV?$basic_string@DU?$char_traits@D@std@@V?
$allocator@D@2@@std@@@CPrint@NCPrint@@QAEXABV?$basic_string@DU?
$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) が関数 class NCPrint::CPrint
__cdecl std::_For_each<class std::basic_string<char,struct
std::char_traits<char>,class std::allocator<char> > *,class NCPrint::CPrint>
(class std::basic_string<char,struct std::char_traits<char>,class
std::allocator<char> > *,class std::basic_string<char,struct
std::char_traits<char>,class std::allocator<char> > *,class NCPrint::CPrint)
(??$_For_each@PAV?$basic_string@DU?$char_traits@D@std@@V?
$allocator@D@2@@std@@VCPrint@NCPrint@@@std@@YA?AVCPrint@NCPrint@@PAV?
$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@0@0V12@@Z) で参照されまし
た。

// DLL ファイル PrintDll.h
#ifndef CPRINT_H_
#define CPRINT_H_

#include <ostream>

namespace NCPrint {
class __declspec(dllexport) CPrint
{
public:
/** @brief Constructor */
explicit CPrint(std::ostream& lhs);

/** @brief Operator overload */
template <typename T> inline void operator()(const T& lhs);

private:
std::ostream& os;
};
}
#endif // end of CPRINT_H_

// DLL ファイル PrintDll.cpp
#include PrintDll.h

namespace NCPrint
{
CPrint::CPrint(std::ostream& lhs) : os(lhs) {}

template <typename T> inline void CPrint::operator()(const T& lhs)
{
os << lhs << endl;
}
}

// EXE ファイル main.cpp
#include <iostream>
#include <algorithm>
#include <vector>
#include PrintDll.h

using namespace std;

int main()
{
vector<string> Day;
Day.push_back(Monday);
Day.push_back(Tuseday);
Day.push_back(Wednesday);
Day.push_back(Thurday);
Day.push_back(Friday);
Day.push_back(Saturday);
Day.push_back(Sunday);

for_each(Day.begin(), Day.end(), NCPrint::CPrint(cout) );

return 0;
}


引用未解決
トピックタグ
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

×template 関数
○関数 template
関数の鋳型 (function template) は「関数ではない」ことは理解できているかな?

PrintDll.cpp 中の operator() は「まだ関数ぢゃない」ため公開不可能。

そもそも inline にしたいんだったら DLL 化することを考えちゃだめだし、
どう書いたらよいかは、どうしたいか次第。


返信引用
Sum
 Sum
(@Sum)
ゲスト
結合: 13年前
投稿: 6
Topic starter  

tetrapod さん。お世話になります。

鋳型をDLLにはできません、当然ですね、メモリを確保した状態で中身はないのですからね、勉強不足でした。
inline はごめんなさい消し忘れです。すみませんでした・・・orz

それから、DLLでoperatorのオーバーロードは無理なんですか・・・ということはfor_eachとかtransformとか、アルゴリズム系の
実装はできないということですね?・・・(´・ω・~) ショボーン.です。

想像するに、仮想関数もDLLにするのは面倒臭い感じかな・・・・?
オブジェクト指向型言語とDLLは相性悪いね、結論ですが
そもそも、ダイナミック リンク ライブラリィはオブジェクト指向だとか
templateなどは考えず、手続き型言語で導出できる関数を扱えば間違いない?
この認識でいいですか?


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

何か誤解がありそう・・

> メモリを確保した状態で中身はないのですからね
メモリを確保? 誰がどこに、という解説がないので意味不明な文書になっている。
C++ の function template はその仕様上
「コンパイル時にコンパイラが関数鋳型の内容をコンパイラの作業メモリに」覚える
「template parameter に具体的型や数値が指定されたら」そこで関数になる
わけだ。

提示ソースコードをコンパイルした状態では、後者の指示がない。
つまり printdll.obj 中に、
template<T> NCPrint::CPrint::operator() という鋳型が生成されることはない。
DLL のロードの際にコンパイルされなおすこともない(ので提示結果のとおり)

printdll.cpp 中で <char> とか <std::string> とか具体的に与えられると
NCPrint::CPrint::operator()<char>
NCPrint::CPrint::operator()<std::string>
のように「関数」に具現化されて、これらは .obj や .dll に入れることができる。

> DLLでoperatorのオーバーロードは無理なんですか・・・
できるよ。だれができないって言ったの?
function template を DLL 化することはできないけど(提示例)
ふつーの operator のように単純な関数であったり
具現化済み function template (は既に関数なので) DLL 化できる。

DLL と EXE でコンパイラのメーカー違いとかバージョン違いとかあると
非互換になる可能性もあるし、俺なら DLL 化するのは慎重に行うな。


返信引用
Sum
 Sum
(@Sum)
ゲスト
結合: 13年前
投稿: 6
Topic starter  

お世話になります。
勘違いがあったようです、ご指摘ありがとうございます。

>printdll.cpp 中で <char> とか <std::string> とか具体的に与えられると
>NCPrint::CPrint::operator()<char>
>NCPrint::CPrint::operator()<std::string>
>のように「関数」に具現化されて、これらは .obj や .dll に入れることができる。
具体的な書き方を教えて頂けないでしょうか。


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

step1:
非 template つまり、通常のメンバ関数なら DLL export/import できる?
できるのなら、

step2:
(step1+)明示的テンプレート具現化すればよいだけだよ。

いきなりソース書くのは回避しておこう。
まずは自分で試してみておくんなまし。


返信引用
仲澤@失業者
(@uncle_kei)
Prominent Member
結合: 5年前
投稿: 828
 

ヒント:
テンプレートを具現(クラス、関数)化する一つの方法としてtypedefが使えます。
MFCのCArrayの例でやると、
typedef CArray< CString> STR_Ary;
STR_Ary StrAry;
StrAry.Add( CString(_T(一番目)));
とか。


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

それは単純に「普通に使ったから具現化された」例であって明示具現化ではない

template<typename T> void testfunc(T t) { ... } // testfunc<T>
があるとき、強制的に testfunc<int> をコンパイル時生成させるには
template void testfunc(int t);
と書けばいい。 ISO/IEC 14882:1998 14.7.2 Explicit instantiation
# template void testfunc<int>(int t); でもいい。が、冗長。

ここで <> が無いことに注意。
template<> void testfunc(long t); は specialization になるので意味が違う。
同 14.7.3 Explicit specialization


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

そもそも論をいうなら
・ template はコンパイル時に解決されるものである
・ DLL はコンパイル済みバイナリである
という点で DLL に template の具現化実体を置こうというのが仕様矛盾している。
俺ならこういう設計は最初から選択肢に入らない。
それでもあえて DLL に template の具現化実体を置くにはどうすればいいか?
という純粋技術的興味の問題として扱うものとして。

あとは DLL の export 側と import 側の問題でしかないので
----test.h----
#if !defined(TEST_T_CALLING_CONVENTION)
#define TEST_T_CALLING_CONVENTION __declspec(dllimport) __stdcall
#endif

struct test_t {
 template<typename T>
 void TEST_T_CALLING_CONVENTION operator() (const T& t);
};
----testdll.cpp----
#define TEST_T_CALLING_CONVENTION __declspec(dllexport) __stdcall
#include test.h
template<typename T> void test_t::operator ()(const T &t) {
 std::cout << t;
}
template void test_t::operator ()(const int&);
----testexe.cpp----
#include test.h
int main() {
 test_t t;
 t(1); // はうまく動く
 // t(1.0); // はコンパイル通るがリンクエラー
}
----
__stdcall をつけてみたものの実際には __thiscall になるような気が・・・


返信引用
仲澤@失業者
(@uncle_kei)
Prominent Member
結合: 5年前
投稿: 828
 

>それは単純に「普通に使ったから具現化された」例であって明示具現化ではない

ややっ、これは失礼m(__)m。
テンプレートの明示的具現化をDLL内で行う方法としてあげたわけではありません。
DLL内では具現化しなければならないのは自明だと考えてました。

> t(1); // はうまく動く

は、ちょっと驚きです。
やってみたんですが、何が悪いのか自分とこのVS2008ではリンクエラー(しくしく)。


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

いやもちろん testdll.lib を追加しなきゃ・・・


返信引用
Sum
 Sum
(@Sum)
ゲスト
結合: 13年前
投稿: 6
Topic starter  

お世話になります。
もう少し考えさせてください。


返信引用
Sum
 Sum
(@Sum)
ゲスト
結合: 13年前
投稿: 6
Topic starter  

>step1:
>非 template つまり、通常のメンバ関数なら DLL export/import できる?
>できるのなら

step 1 ですがこのように実装しましたがどうでしょうか?
駄目があれば指摘してください。

// Proejct name templateTestDll
// Filename PrintDll.h

#ifndef CPRINT_H_
#define CPRINT_H_
#include <iostream>

class __declspec(dllexport) CPrint
{

public:
explicit CPrint(std::ostream& os_) : os(os_){};
void CPrint::operator()(std::string& os);

private:
std::ostream& os;
};
#endif // end of CPRINT_H_

// Proejct name templateTestDll
// Filename PrintDll.cpp

#include PrintDll.h

using namespace std;

void CPrint::operator()(std::string& os)
{
cout << os.c_str() << endl;
}

// Proejct name templateTestExecDll
// Filename main.cpp

#include <algorithm>
#include <vector>
#include PrintDll.h
#pragma comment(lib, templateTestDll.lib)

using namespace std;

int main()
{
vector<string > Day;

Day.push_back(Monday);
Day.push_back(Tuseday);
Day.push_back(Wednesday);
Day.push_back(Thurday);
Day.push_back(Friday);
Day.push_back(Saturday);
Day.push_back(Sunday);

for_each(Day.begin(), Day.end(), CPrint(cout) );

return 0;
}


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

class に __declspec(dllimport) をつけるということは、
全メンバ関数を DLL に import/export するということ(とほぼ同等)だ。
# function template はその原理上 import/export されないし、できない
っていうか 常に dllexport だと DLL 化できていないんだが?

当初議題 09/03 はメンバ関数1個だけを import/export と、俺は解釈したので、
09/05 の実装だと全メンバ関数になり、若干ずれてきている気のせいがするが、
まあそこへんは俺の勝手な解釈なので単に気のせいかも。
全メンバ関数を import/export したいのであれば 09/05 の実装で適切だよ。
(dllimport/dllexport を適切に使い分ける前提で)
(template 化しようとすると話は変わってくる)

# 重箱の隅ツッコミがほしいのなら
# wcout に出力したく (wchar_t ベースに書き換えたく) なったり
# string / basic_ostream の allocator や traits を変えたい要望に対応するには
# CPrint 自体を template ベースにする= DLL 化は無し
# という選択をするしかないだろう

というわけで「結局何がやりたいのか」が読者に伝わっていないのが伸びてる原因。
技術的興味を満たすにはスレッドが長くてもかまわないんだけど。


返信引用
Sum
 Sum
(@Sum)
ゲスト
結合: 13年前
投稿: 6
Topic starter  

ありがとう。


返信引用

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました