template 関数の使い方について

[戻る]
オーウェル 2012/07/07(土) 20:01:27
よろしくおねがいします。
v コンテナの各要素の値の大きさで順番を求めその結果を、rv コンテナに取得するプロ
グラムなのですが、行き詰っています。
問題1    関数template の変数out に結果は取得できているのですが、rv コンテナにデー
タを取得することができません、どこがおかしいのでしょうか。
問題2    この関数をポインターや配列でも自由に使えるようにするには、どのようにした
ら良いでしょうか。

#include <iostream>        // use std::cout std::endl
#include <vector>

template<class Iterator, class OutputIterator>
void Rank(Iterator first, Iterator last, OutputIterator out)
{
    typedef
        typename std::iterator_traits<Iterator>::value_type
        value_type;

    value_type rank = value_type();

    for(Iterator p = first; p != last; ++p) {
        iterator_traits<Iterator>::value_type lhs = *p;
        rank = 1;
        for(Iterator q = first; q != last; ++q) {
            iterator_traits<Iterator>::value_type rhs = *q;
            if(rhs > lhs) {
                rank+=1;
            }
        }
        out.push_back( rank );
        ++rank;
    }
}

using namespace std;

int main()
{
    vector<int> v, rv;

    v.push_back(6);
    v.push_back(4);
    v.push_back(5);
    v.push_back(10);
    v.push_back(2);
    v.push_back(8);
    v.push_back(3);
    v.push_back(9);
    v.push_back(1);
    v.push_back(7);

    Rank(v.begin(), v.end(), rv);

    vector<int>::iterator it = rv.begin();

    while(it != rv.end())
    {
        cout << *it << endl;
    }
}

επιστημη [E-Mail] [HomePage] 2012/07/07(土) 20:27:49
こんなもんかしら

#include <iostream>
#include <vector>
#include <iterator>

template<class Iterator, class OutputIterator>
OutputIterator Rank(Iterator first, Iterator last, OutputIterator out) {
  typedef typename std::iterator_traits<Iterator>::value_type value_type;

  value_type rank = value_type();
  for(Iterator p = first; p != last; ++p) {
    value_type lhs = *p;
    int rank = 1;
    for(Iterator q = first; q != last; ++q) {
      value_type rhs = *q;
      if ( rhs > lhs) {
        ++rank;
      }
    }
    *out++ = rank;
  }
  return out;
}

using namespace std;

int main() {
  vector<int> v;

  v.push_back(6);
  v.push_back(4);
  v.push_back(5);
  v.push_back(10);
  v.push_back(2);
  v.push_back(8);
  v.push_back(3);
  v.push_back(9);
  v.push_back(1);
  v.push_back(7);

  vector<int> rv;
  Rank(v.begin(), v.end(), back_inserter(rv));
  for ( vector<int>::iterator it = rv.begin(); it != rv.end(); ++it ) {
    cout << *it << ' ';
  }
  cout << endl;

  int ra[100];
  int* la = Rank(v.begin(), v.end(), ra);
  for ( int* it = ra; it != la; ++it ) {
    cout << *it << ' ';
  }
  cout << endl;

}

オーウェル 2012/07/12(木) 22:00:15
[[解決]]
επιστημη さんありがとうございます。
問題1は、back_inserter() 使うと、値がセットできるんですね。
問題2は、ポインタですね。
ところで、もう一つお聞きしたいのですが、templateはDLLにできるのでしょうか?
templateは鋳型なので、鋳型を作っても実体がなければ無理のような気もするのです
が・・・・
例えば、この関数なんかでもDLL化できるのでしょうか?
本来の質問とは意図が違い恐縮ですがよろしくおねがいします。

επιστημη [E-Mail] [HomePage] 2012/07/12(木) 22:12:25
> ところで、もう一つお聞きしたいのですが、templateはDLLにできるのでしょうか?
> templateは鋳型なので、鋳型を作っても実体がなければ無理のような気もするのです
が・・・・

templateに食わす型が確定していれば、できます。
たとえば std::string は std::basic_string<char> ですからライブラリになります。

型が決まってればもはやtemplateでなくなっちゃってますからね。

επιστημη [E-Mail] [HomePage] 2012/07/12(木) 22:16:03
> back_inserter() 使うと、値がセットできるんですね。

あるいは出来上がりの大きさNがあらかじめわかっているなら:

  vector<int> rv(N); // 要素数Nでこしらえておく
  Rank(v.begin(), v.end(), rv.begin());

オーウェル 2012/07/17(火) 10:00:41
お世話になります、Template からのDLL 化の方法がよくわかりました、ありがとうござ
います。
ところで、例えば導出する関数プロトタイプをこのように定義をします。
int __stdcall Rank(std::vector<int>::iterator, std::vector<int>::iterator, 
vector<int>::iterator)

実装を
Rank(v.begin(), v.end(), back_inserter(rv));
こうした場合
「error C2664: 'Rank' : 3 番目の引数を 'std::back_insert_iterator<_Container>' 
から 'std::_Vector_iterator<_Myvec>' に変換できません。(新しい機能 ; ヘルプを参
照)」
型が違うと怒られます、この部分の書き方を教えて頂けないでしょうか。

tetrapod 2012/07/17(火) 16:38:12
back_inserter は vector::iterator ではないから当然だ。
どうすればいいかは、どうしたいか次第。

俺としては、当該 Rank() を DLL にする、という選択肢がありえない。
DLL 化によって template を使ううまみが消滅しているので。

vector が list に変わったり
int が double に変わったり
back_inserter が front_inserter に変わったり
する可能性はあるの?無いの?
可能性が 1ppm でもあるなら template のまま。

επιστημη [E-Mail] [HomePage] 2012/07/17(火) 21:20:19
おのずとこんな↓カンジになんでねぇかと。

vector<int> rv(v.size());
Rank(v.begin(), v.end(), rv.begin());

オーウェル 2012/07/19(木) 21:50:47
tetrapod さん
>する可能性はあるの?無いの?
無いです、自分自身の考えを整理すると、templateのパターン数あるパターンの中の
一パターンをDLL化したいということになります。
επιστημηさん 
>Rank(v.begin(), v.end(), rv.begin());
これやってみたんですが、DLLの側でrv にデータがセットされるのですが、呼び出し側
(exe)のコンテナに値が入って行きません。
と試行錯誤して調べていると、DLLとSTLって相性が非常に悪いようですね、Microsoftさん
なんとかして欲しいところです・・・・orz
stlなんて使わずに、コテコテのC言語のmalloc、freeで実装する方が手っ取り早いのか
な。

επιστημη [E-Mail] [HomePage] 2012/07/19(木) 22:21:15
あー、DLL側でメモリ・アロケートするだろからね。
だったらexe側にcallbackしてはどうでしょね。

int __stdcall Rank(std::vector<int>::iterator, std::vector<int>::iterator, 
  void (*func)(int,void*), void* arg) {
    ...
    func(rank,arg);
    ...
}

とかやっといて:

vector<int> rv;
Rank(v.begin(), v.end(), 
    [](int r, void* arg) { reinterpret_cast<vector<int>*>(arg)->push_back(r);},
    &rv);

tetrapod 2012/07/20(金) 06:51:44
単に技術的に可能かどうかの検証のため DLL 化した
=速度やわかりやすさなどどうでもいい、って香りがしてきた (爆)

最優先されるのが DLL にすること、であるなら
> コテコテのC言語のmalloc、freeで実装する方が手っ取り早い
に1票。
メモリ操作で EXE/DLL 境界をまたぐとまずい点も解消できそうだし
異コンパイラを使ったり、異コンパイルオプションを使ったりもできるし。

tetrapod 2012/07/20(金) 14:13:07
技術的興味のためだけにサンプル作ってみた。実用性は0と判断する。
VS2005 ではそれなりに動くが cygwin gcc 4.5.3 ではうまく動かないのを確認済み。
将来の読者の参考にでもなれば幸い。

----rank.hpp---- EXE/DLL 共通の公開ヘッダ
/// 宣言だけ行う
template<typename iit, typename oit>
void __stdcall rank(iit b, iit e, oit o);

#if !defined(DLLDECL)
#define DLLDECL __declspec(dllimport)
#endif

/// 明示的インスタンス化実施
/// 定義なし template を dllimport してよい根拠が無いので
/// たまたま VS2005 では意図通りだが g++ ではだめっぽい
template void DLLDECL __stdcall rank(std::vector<int>::iterator,
  std::vector<int>::iterator, std::vector<int>::iterator);
template void DLLDECL __stdcall rank(std::vector<int>::iterator,
  std::vector<int>::iterator, std::back_insert_iterator<std::vector<int> >);
template void DLLDECL __stdcall rank(std::vector<int>::iterator,
  std::vector<int>::iterator, int*);

----rank.cpp---- rank.dll のソース
#include <vector>
#include <iterator>
#define DLLDECL __declspec(dllexport)
#include "rank.hpp"

template<typename iit, typename oit>
void __stdcall rank(iit b, iit e, oit o) {
    typedef typename std::iterator_traits<iit>::value_type val_type;
    for (iit i=b; i!=e; ++i) {
        // 元コードの意図がつかみにくいので、勝手に
        // 入力イテレータの value_type と同一型を出力すると判断する
        // 出力イテレータから value_type を類推すると back_inserter が使えない
        val_type rank=val_type(); // 当該型の0を得る
        ++rank; // 当該型の1を得る
        for (iit j=b; j!=e; ++j) {
            if (*j<*i) ++rank; // operator< を使うように修正
        }
        *o=rank; ++o;
    }
}
/// 明示インスタンス化指示されたものだけ dllexport される


----rankexe.cpp---- EXE のソース
#include <vector>
#include <iterator>
#include <iostream>
#include "rank.hpp"

void dump(const std::vector<int>& v) {
    std::vector<int>::const_iterator i;
    for (i=v.begin(); i!=v.end(); ++i) {
    std::cout << *i << ' ';
    }
    std::cout << std::endl;
}

int main() {
    std::vector<int> v;
    std::vector<int> w;
    v.push_back(1);
    v.push_back(-3);
    v.push_back(2);
    v.push_back(-1);
    dump(v);
    // inserter が EXE 側にあれば問題ない
    rank(v.begin(), v.end(), std::back_inserter(w));
    dump(w);
    int x[4];
    rank(v.begin(), v.end(), x);
    for (int i=0; i<4; ++i) std::cout << x[i] << ' ';
    std::cout << std::endl;
    return 0;
}
-----
cl -LD -EHsc rank.cpp
cl -EHsc rankexe.cpp -link rank.lib

当然ながら DLL で公開していないものは使えないので
const vector<int>& y=v;
rank(y.begin(), y.end(), x);
のように const 1つついただけでリンクエラー発生。

古いスレッドには追加発言できません。