ファイル読込処理をすっきりしたいのですが

[戻る]
初心者++ になりたい 2010/12/20(月) 03:10:18
よろしくお願いします。
一行データ数が定数のcsvファイルから、配列に読込ませるプログラムです。
質問1
このコードは冗長な部分が多々在ると思うのですが、もっとすっきり書くことはできない
でしょうか。
質問2
今回、一行の要素数は8ですが、他の数のファイルの場合は仮想関数で再定義するしか方
法は無いのでしょうか。(boost::any を使ったほうが、すっきりするのでしょうか?)

// container.h
#pragma once
#include <fstream>
#include <string>

template <typename T>
class Container {
    T m_0; T m_1; T m_2; T m_3; T m_4; T m_5; T m_6; T m_7; 
public:
    Container(const T& arg0, const T& arg1, const T& arg2, const T& arg3, 
const T& arg4, const T& arg5, const T& arg6, const T& arg7  ) : m0(arg0), 
m0(arg2), m2(arg3), m3(arg4), m5(arg5), m5(arg6), m5(arg7), m5(arg8){}
    Container() : m_0(), m_1(), m_2(), m_3(), m_4(), m_5(), m_6(), m_7(){}

    void setm_0(const T& t){ m_0 = t;  }
    void setm_1(const T& t){ m_1 = t; }
    void setm_2(const T& t){ m_2 = t; }
    void setm_3(const T& t){ m_3 = t; }
    void setm_4(const T& t){ m_4 = t; }
    void setm_5(const T& t){ m_5 = t; }
    void setm_6(const T& t){ m_6 = t; }
    void setm_7(const T& t){ m_7 = t; }

    Container<T> operator+(const Container<T>& )
    {
        Container tmp;
        tmp.m_0 = m_0 + rhs.m_0;
        tmp.m_1 = m_1 + rhs.m_1;
        tmp.m_2 = m_2 + rhs.m_2;
        tmp.m_3 = m_3 + rhs.m_3;
        tmp.m_5 = m_4 + rhs.m_4;
        tmp.m_6 = m_5 + rhs.m_5;
        tmp.m_7 = m_6 + rhs.m_6;
        tmp.m_8 = m_7 + rhs.m_7;
        return tmp;
    }

    Container<T> operator=(const Container<T>& )
    {
        Container tmp;
        tmp.m_0 = rhs.m_0;
        tmp.m_1 = rhs.m_1;
        tmp.m_2 = rhs.m_2;
        tmp.m_3 = rhs.m_3;
        tmp.m_5 = rhs.m_4;
        tmp.m_6 = rhs.m_5;
        tmp.m_7 = rhs.m_6;
        tmp.m_8 = rhs.m_7;

        return tmp;
    }
};

// container.cpp
#include <iostream>
#include <boost/tokenizer.hpp>
#include "Container.h"

using namespace std;
int main(void)
{
    Container<string> container;
    vector<Container<string> > vcontainer;

    string line;
    const int DATA_MAX = 8;
    typedef boost::char_separator<char> char_separator;
    typedef boost::tokenizer<char_separator> tokenizer;

    ifstream ifs("D:\\0301.csv");
    int num = 0;
    while( getline(ifs,line) )
    {
        char_separator sep(",", "", boost::keep_empty_tokens);
        tokenizer tokens(line, sep);
        for (tokenizer::iterator tok_iter = tokens.begin(); tok_iter != 
tokens.end(); ++tok_iter) 
        {
            switch(num % DATA_MAX)
            {
            case 0:
                container.setm_0(*tok_iter);
                break;
            case 1:
                container.setm_1(*tok_iter);
                break;
            case 2:
                container.setm_2(*tok_iter);
                break;
            case 3:
                container.setm_3(*tok_iter);
                break;
            case 4:
                container.setm_4(*tok_iter);
                break;
            case 5:
                container.setm_5(*tok_iter);
                break;
            case 6:
                container.setm_6(*tok_iter);
                break;
            case 7:
                container.setm_7(*tok_iter);
                break;
            }
            ++num;
        }
        vcontainer.push_back(container);
    }
    return 0;
}

初心者++ になりたい 2010/12/20(月) 03:15:36
演算子の部分訂正します。
    Container<T> operator+(const Container<T>& rhs)
    {
        Container tmp;
        tmp.m_0 = m_0 + rhs.m_0;
        tmp.m_1 = m_1 + rhs.m_1;
        tmp.m_2 = m_2 + rhs.m_2;
        tmp.m_3 = m_3 + rhs.m_3;
        tmp.m_5 = m_4 + rhs.m_4;
        tmp.m_6 = m_5 + rhs.m_5;
        tmp.m_7 = m_6 + rhs.m_6;
        tmp.m_8 = m_7 + rhs.m_7;
        return tmp;
    }

    Container<T> operator=(const Container<T>& rhs)
    {
        Container tmp;
        tmp.m_0 = rhs.m_0;
        tmp.m_1 = rhs.m_1;
        tmp.m_2 = rhs.m_2;
        tmp.m_3 = rhs.m_3;
        tmp.m_4 = rhs.m_4;
        tmp.m_5 = rhs.m_5;
        tmp.m_6 = rhs.m_6;
        tmp.m_7 = rhs.m_7;
        return tmp;
    }

επιστημη [E-Mail] [HomePage] 2010/12/20(月) 05:42:34
template <typename T, size_t N =8>
class Container {
    T m[N];
    ...

ではダメなんすか?

PATIO 2010/12/20(月) 11:30:50
ソースを見て冗長な部分を指摘してくださいでは無くて
まずは自分はこの部分が冗長だと思うのだけれど、
どうしたら冗長な部分を改良できるでしょうかとか
ある程度は質問者の方から提示した方が話が進みやすいです。

もっとも自分が思っていなかった突っ込みがはいる可能性も
ありますが、それはそれとして嬉しい誤算程度に考えた方が
良いかなと思います。
どうしても指摘してくださいという形になると
丸投げのイメージがついてまわると思うので
自分で質問箇所の特定を行なう努力をした方が良いと思いますよ。

PATIO 2010/12/20(月) 11:33:16
あと、ご自身の処理に対するイメージと言うか、
こんな風に出来たら良いのにと言うような話があると
それはそれで話が膨らみます。

掲示板上での質問は提示された内容しか情報がない
状況になりますので、なんでも出せる物は出した方が
状況も伝わりやすいし、アドバイスも貰いやすいです。

hirocco 2010/12/20(月) 11:59:58
>質問1
>このコードは冗長な部分が多々在ると思うのですが、
>もっとすっきり書くことはできないでしょうか。

まず、なぜ冗長だといけないのか?
コードとして動くならば問題ないと思う
別の誰かが理解しなければいけない環境ならば、さらに冗長でもコメントを書いておけ
ばよいと思う
誰かに見られると冗長だとかっこ悪い程度ならば直す必要なしでしょう


>質問2
>今回、一行の要素数は8ですが、他の数のファイルの場合は仮想関数で
>再定義するしか方法は無いのでしょうか。
>(boost::any を使ったほうが、すっきりするのでしょうか?)

まさに可変長ならば επιστημηさん ので答えですね

強いて言うなら、operater[]でもつくってみたら?
もしくは
T& Item(int index)とかね、くらいでしょうか
そしたら可変長でも setm_0 のような プロパティ?的なものへのアクセスもできるね

maru 2010/12/20(月) 18:53:42
誰も指摘しないけど、
>     Container<T> operator+(const Container<T>& rhs)
>     {
>         Container tmp;
>         tmp.m_0 = m_0 + rhs.m_0;
(中略)
>         return tmp;
>     }
は
    Container<T>& operator+(const Container<T>& rhs)
    {
        m_0 += rhs.m_0;
(省略)
        return *this;
    }
じゃないの?
# operator=() も同様。

書き方というより一時オブジェクト(tmp)が冗長なんだけど。

maru 2010/12/20(月) 18:57:25
>     Container<T>& operator+(const Container<T>& rhs)
>     {
>         m_0 += rhs.m_0;
>  (省略)
>         return *this;
>     }
> じゃないの?
大きな勘違いしてました。
読み飛ばしてください。

hirocco 2010/12/20(月) 19:11:34
強いて言うなら,
ホントに強いて言うならね

Container<T> operator+(const Container<T>& rhs) const
{
    Container tmp;
    tmp.m_0 = m_0 + rhs.m_0;
   (中略)

    return tmp;
}

かな?
別に、どっちでもいいね


operator=()は maruさんの仰るとおりですね
ただし、クローンを作るというニュアンスで operator=()なら
つっこむとこでもないかな?
おかしいけどね

hirocco 2010/12/20(月) 19:37:27
これ、csvを読むためのものなんですよね
template っているかなぁ?

たとえば T が CSTring だったら
"0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8"と
"0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8"を足したら
"0.10.1,0.20.2,0.3,0.40.4,0.50.5,0.60.6,0.70.7,0.80.8"になっちゃうんじゃない?

そして doubleなら
double cont[8];
でいいかなぁ
後はループでまわしちゃえばいいと思うよ

ということは、クラスにすること自体が冗長かな?www


ん〜と
1行のみを文字列で保持するクラスをつくって
その+とか=のオペレータで文字列をdouble型に変えて足したりとかするようにして
でね、そのクラスのポインタを持ったコレクション的クラスをつくって
そのコンストラクタには CSVファイルパスへのポインタを渡す

生成時にファイルを開いたりして読み込んで、
デストラクタでファイルを開いたりして書き込む

SaveとかLoadとかのメソッドもあってもいいね、インデクサもオーバーロードして、
さらに、
CString Item(int col,int row);
なんてのも作って、セルとしての情報も取得できると便利そうね

Tを値と数式を保持できる構造体なんかにしたら、表計算も可能かな?!

欲張りすぎまして、ごめんなさい

初心者++ になりたい 2010/12/21(火) 09:20:41
お世話になります。
επιστημη さん、exactly!まさにその通りで、可変長の配列が所望していたもの
でした。ありがとうございます。
PATIO さん、言葉足らずですみませんでした。επιστημη さんに指摘してい
ただき、修正した結果下記の通りです。
maru さん、お世話になります。hirocco さんが解説してくださっていますが、クローン
作成の意味合いのoperator=() ということになります。
hirocco さんお世話になります。
operator+() の振る舞いなのですが、思案しています。
仰せの通り
たとえば T が CSTring だったら・・・の部分ですが
確かに、現状のままですと、template 化する意味合いが、希薄になってしまいますね。
そこで、向学の意味合いで、
1行のみを文字列で保持するクラスをつくって
その+とか=のオペレータで文字列をdouble型に変えて足したりとかするようにして
でね、そのクラスのポインタを持ったコレクション的クラスをつくって
そのコンストラクタには CSVファイルパスへのポインタを渡す
具体的な方法を教えていただけないでしょうか。

#Container.h
#pragma once
#include <fstream>
#include <string>

class IEEE64 {
    double data_;
public:
    IEEE64(double data = 0.0)
        : data_(data){}

    operator double() const
    {
        return data_;
    }
};

template <typename T, size_t N=8>
class Container
{
public:
    T m[N];
    Container<T> operator+(const Container<T>& rhs) const
    {
        Container tmp;
        for(int i=0; i<N; ++i)
            tmp.m[i] = tmp.m[i] + rhs.m[i];
        return tmp;
    }

    Container<T> operator=(const Container<T>& rhs) const
    {
        Container tmp;
        for(int i=0; i<N; ++i)
            tmp.m[i] = rhs.m[i];
        return tmp;
    }

    Container(void){}
    ~Container(void){}
};

#cContainer.cpp
#include <iostream>
#include <boost/tokenizer.hpp>
#include <vector>
#include "Container.h"

using namespace std;

int main(void)
{
    Container<string> container;
    vector<Container<string> > vcontainer;

    string line, str;
    const int DATA_MAX = 8;
    typedef boost::char_separator<char> char_separator;
    typedef boost::tokenizer<char_separator> tokenizer;

    ifstream ifs("d:\\0301.csv");
    int num = 0;
    while( getline(ifs,line) )
    {
        char_separator sep(",", "", boost::keep_empty_tokens);
        tokenizer tokens(line, sep);
        for (tokenizer::iterator tok_iter = tokens.begin(); tok_iter != 
tokens.end(); ++tok_iter)
        {
            container.m[num % DATA_MAX] = *tok_iter;
            ++num;
        }
        vcontainer.push_back(container);
    }
    
    IEEE64 value = 0.3784;
    double d = value;

    return 0;
}

初心者++ になりたい 2010/12/21(火) 10:51:08
すみません、上記のコードにいらないクラスも入っていました。
みなさんのご意見を元に、考えました
#Container.h
#pragma once
#include <fstream>
#include <string>

template <typename T, size_t N=8>
class Container
{
public:
    T m[N];
    Container<T> operator+(const Container<T>& rhs) const
    {
        Container tmp;
        for(int i=0; i<N; ++i)
            tmp.m[i] = tmp.m[i] + rhs.m[i];
        return tmp;
    }

    Container<T> operator=(const Container<T>& rhs) const
    {
        Container tmp;
        for(int i=0; i<N; ++i)
            tmp.m[i] = rhs.m[i];
        return tmp;
    }

    Container(void){}
    ~Container(void){}
};

#Container.cpp
#include <iostream>
#include <boost/tokenizer.hpp>
#include <vector>
#include "Container.h"

using namespace std;

int main(void)
{
    Container<string> container;
    vector<Container<string> > vcontainer;

    string line, str;
    const int DATA_MAX = 8;
    typedef boost::char_separator<char> char_separator;
    typedef boost::tokenizer<char_separator> tokenizer;

    ifstream ifs("d:\\0301.csv");
    int num = 0;
    while( getline(ifs,line) )
    {
        char_separator sep(",", "", boost::keep_empty_tokens);
        tokenizer tokens(line, sep);
        for (tokenizer::iterator tok_iter = tokens.begin(); tok_iter != 
tokens.end(); ++tok_iter)
        {
            container.m[num % DATA_MAX] = *tok_iter;
            ++num;
        }
        vcontainer.push_back(container);
    }
    
    return 0;
}

hirocco 2010/12/21(火) 14:46:21
こんなのどうかなぁ?

class Container
{
public:
  Container()
  {
    _data  =NULL;
    _size  =0;
  }
  Container(const Container& object)
  {
    _data  =NULL;
    _size  =0;
    Resize( object._size );
  }
  Container(int newSize)
  {
    _data  =NULL;
    _size  =0;
    Resize( newSize );
  }
  ~Container()
  {
    if( _data!=NULL )
    {
      delete[] _data;
    }
  }

  Container& operator=(const Container& object)
  {
    Resize( object._size );
    for( int i=0;i<_size;i++ )
    {
      _data[i]  =object._data[i];
    }
    return (*this);
  }
  CString& operator[](int index)
  {
    VERIFY( (index>=0)&&(index<_size) );

    return _data[index];
  }
  Container& operator+=(const Container& line)
  {
    for( int i=0;i<_size;i++ )
    {
      _data[i].Format( "%f",::atof(_data[i])+::atof(line._data[i]) );
    }
    return (*this);
  }
  Container& operator+(const Container& line) const
  {
    static Container temp;
    temp.Resize( line._size );

    for( int i=0;i<_size;i++ )
    {
      temp[i].Format( "%f",::atof(_data[i])+::atof(line._data[i]) );
    }
    return temp;
  }

private:
  CString* _data;
  int     _size;

public:
  void Resize(int newSize)
  {
    if( _data!=NULL )
    {
      delete[] _data;
    }
    _data  =new CString[newSize];
    _size  =newSize;
    for( int i=0;i<_size;i++ )
    {
      _data[i]  ="";
    }
  }
};
暇なんですかね?
つっこみ募集します

maru 2010/12/21(火) 17:19:02
> つっこみ募集します
に反応して。

>  Container()
>  {
>    _data  =NULL;
>    _size  =0;
>  }
初期化リストで初期化したほうがよりC++ライク!?
 Container() : _data(NULL), _size(0) {}

>  Container(const Container& object)
>  {
>    _data  =NULL;
>    _size  =0;
>    Resize( object._size );
>  }
コピーコンストラクタなのにデータのコピーを行っていないのはどうなのかなぁ。

>   CString* _data;
>   int     _size;
「'_'で開始する識別子はコンパイラが予約語として使用できる」ので非推奨だった
と思ったけど、今、調べたら「'_'二つで始まる、または'_'に続いて大文字で始まる」
ってのしか見つからない。本当の所はどうなんでしょうね。
もっともMSDNには
"To avoid any naming conflicts, always select identifier names that do not
 begin with one or two underscores, or names that begin with an underscore 
followed by an uppercase letter. "
と書いてあるので、MSのコンパイラを使っている範囲では'_'で開始する識別子は使わ
ない方が吉なんだと思うけど。

>     if( _data!=NULL )
>     {
>       delete[] _data;
>     }
このif文、要ります? 確か delete オペレータは0を渡しても正しく処理される
(何もしない)ことが保障されていたと思いますが。
MSDNにも、
"You can, however, use delete on a pointer with the value 0."
と書かれています。

続くようなら雑談ラウンジ移った方がいいでしょうかね。

hirocco 2010/12/21(火) 18:25:18
>初期化リストで初期化したほうがよりC++ライク!?
> Container() : _data(NULL), _size(0) {}
ライクですねぇ

>コピーコンストラクタなのにデータのコピーを行っていないのはどうなのかなぁ。
ほんとだね、でもよく見ると要らないね
コピーコンストラクタは削除かな
っていうか、これはオマジナイ的な自分への馬鹿よけでね
たとえば、ありえないんだけど、void Test(Container src);っていうのを仮に作っちゃ
った時
デストラクタでやっつけられちゃうからなぁ、なんて思ったんだけど、そんなのないし
余計なことしちゃったねぇ
ここは、おっしゃる通りコピーするコードが必要ですね

>「'_'で開始する識別子はコンパイラが予約語として使用できる」ので非推奨だった
>と思ったけど、今、調べたら「'_'二つで始まる、または'_'に続いて大文字で始まる」
>ってのしか見つからない。本当の所はどうなんでしょうね。
>もっともMSDNには
>"To avoid any naming conflicts, always select identifier names that do not
> begin with one or two underscores, or names that begin with an underscore 
>followed by an uppercase letter. "
>と書いてあるので、MSのコンパイラを使っている範囲では'_'で開始する識別子は使わ
>ない方が吉なんだと思うけど。
へぇ〜、しらなかったぁ
static は s_ で始めて
グローバルは g_ で始めて
pubricなメンバ変数は m_ なんてルールにしてて
private/protectedなのは _ にしてましたよ
でも、private/protectedなのは多いので アルファベットを更に先につけるのもやだっ
たので
_ にしてました
でも、なんかついてないとねぇ インテリセンス命ですので・・・記憶力さえあればね
ぇ・・・
っていうか、皆さんどうしてます?

>>     if( _data!=NULL )
>>     {
>>       delete[] _data;
>>     }
>このif文、要ります? 確か delete オペレータは0を渡しても正しく処理される
>(何もしない)ことが保障されていたと思いますが。
>MSDNにも、
>"You can, however, use delete on a pointer with the value 0."
>と書かれています。
へぇ〜、これもしらなかったぁ
過去の全部NULLチェックしてたよ
冗長でしたね

Ban 2010/12/23(木) 09:53:01
>  Container& operator+(const Container& line) const
>  {
>    static Container temp;

将来ハマる可能性(並列実行したりとか)も考えると、
汎用的に作るなら素直に実体を返した方がよくないですか。

επιστημη [E-Mail] [HomePage] 2010/12/23(木) 10:16:36
> 「'_'で開始する識別子はコンパイラが予約語として使用できる」ので非推奨だった
> と思ったけど、今、調べたら「'_'二つで始まる、または'_'に続いて大文字で始ま
る」
> ってのしか見つからない。本当の所はどうなんでしょうね。

「本当の所」が知りたいなら規格にあたればいいやん。
C++ working draft N3090 によると:

17.6.3.3.2 Global names [global.names]
1 Certain sets of names and function signatures are always reserved to the 
implementation:
&#8212; Each name that contains a double underscore _ _ or begins with an 
underscore followed by an uppercase
letter (2.12) is reserved to the implementation for any use.
&#8212; Each name that begins with an underscore is reserved to the implementation 
for use as a name in the
global namespace.

なので '_'から始まる名前は予約されてて、僕らが使っちゃダメ。

επιστημη [E-Mail] [HomePage] 2010/12/23(木) 10:19:24
> Container& operator+(const Container& line) const
>  {
>    static Container temp;
>    ...
>    return temp;
>  }

...ダメダメじゃねぇの?

Ban 2010/12/23(木) 11:36:52
> なので '_'から始まる名前は予約されてて、僕らが使っちゃダメ。

> Each name that begins with an underscore is reserved to the implementation 
> for use as a name in the global namespace.

ご自身でも引用されているように、__ならばany useなのでご指摘どおりですが、
_で始まって大文字が続かない名前は *グローバルネームスペース内で*
予約されてるだけですから(ISO/IEC 14882:2003でも一緒です)、
それ以外についてはmaruさんの発言が正しいかと思いますが…。

# 但し、環境依存のヘッダやらがマクロ定義とかしてる可能性があったりするわけで、
# 使わない方が安全だとは思います。

Ban 2010/12/23(木) 11:43:16
すみません、誤読してましたので訂正です。

> 「'_'二つで始まる、または'_'に続いて大文字で始まる」

アンダスコア二つの時は アンダスコア二つで始まる必要はない
(途中や最後にあっても予約される)ので、正しいと言い切れませんでした。

επιστημη [E-Mail] [HomePage] 2010/12/23(木) 12:05:15
> _で始まって大文字が続かない名前は *グローバルネームスペース内で*
> 予約されてるだけですから(ISO/IEC 14882:2003でも一緒です)、
> それ以外についてはmaruさんの発言が正しいかと思いますが…。

ええ。とはいえ'わざわざ'使う合理的な理由はなさげっすねー。

初心者++ になりたい 2010/12/23(木) 12:27:19
hirocco さんお世話になります、ツッコミを入れるほどの器量はありませんが
他の方が書かれていた、初期化子リストによる、コンストラクタの初期化程度しか、
気づきませんでした。ことεπιστημη 氏による
> Container& operator+(const Container& line) const
>  {
>    static Container temp;
>    ...
>    return temp;
>  }
この部分ですが、なぜだめなのかさえ、理解できません・・・・
さて。
掲示して頂いた、コードですが、どストレートで、コンストラクタに数値と、
文字列 を持たせるというアイデアでユニークだと思います、勉強になります。
私は、templateクラスを用いて文字列、数値のキャストを実装してみました
肝のlexical_cast クラスは、επιστημη 氏の「C++テンプレートテクニック」
からの引用となります。

// lexical_cast.h
#pragma once
#include <sstream>
#include <boost/lexical_cast.hpp>

template <class To, class From>
To lexical_cast(From from)
{
    To result;
    std::stringstream ss;
    ss << from;
    ss >> result;
    return result;
}

template <class From>
class lexical_cast_t {
    From from_;
public:
    lexical_cast_t (From from)
        :from_(from){}
    template <class To>    
    operator To() const
    {
        return lexical_cast<To>(from_);
    }
};

template <class From>
lexical_cast_t<From> lexical(From from)
{
    return lexical_cast_t<From>(from);
}

// Container.h
#pragma once
#include "stdafx.h"
#include <fstream>
#include <string>

template <typename T, size_t N=8>
class Container
{
public:
    T m[N];
    Container<T> operator+(const Container<T>& rhs) const
    {
        Container tmp;
        for(int i=0; i<N; ++i)
            tmp.m[i] = tmp.m[i] + rhs.m[i];
        return *this;
    }

    Container<T> operator=(const Container<T>& rhs) const
    {
        Container tmp;
        for(int i=0; i<N; ++i)
            tmp.m[i] = rhs.m[i];
        return *this;
    }

    Container(void){}
    ~Container(void){}
};

// Container.cpp
#include <iostream>
#include "Container.h"
#include "lexical_cast.h"

using namespace std;

int main(void)
{
    Container<string> container;

    const int DATA_MAX = 8;
    char buffer[20];
    
    //文字列データをコンテナいれる
    
    for(int i = 0; i < DATA_MAX; ++i)
    {
        sprintf(buffer,"0.%1.1x",i);
        container.m[i] = buffer;
    }

    // コンテナクラスで+ 演算を行うと、予想通り単なる文字列の結合が行われ
る。
    container.m[0] = "";
    container.m[0] = container.m[1] + container.m[2];
    cout << "0.1+0.2 文字列結合処理結果" << endl;
    cout << container.m[0] << endl;

    // そこでlexical クラスを使い、std::string → doubleの変換を行う。
    double dlhs  = lexical(container.m[1]);
    double drhs = lexical(container.m[2]);
    double result = dlhs + drhs;
    cout << "0.1+0.2 数値演算処理結果" << endl;
    cout << result << endl;

    return 0;
}

επιστημη [E-Mail] [HomePage] 2010/12/23(木) 12:46:04
>> Container& operator+(const Container& line) const
>>  {
>>    static Container temp;
>>    ...
>>    return temp;
>>  }
> この部分ですが、なぜだめなのかさえ、理解できません・・・・

struct hoge {
  int val;
  hoge(int v) : val(v) {}
  hoge(const hoge& h) : val(h.val) {}

  hoge& operator+(const hoge& h) {
    static hoge result(0);
    result.val = val + h.val;
    return result;
  }
};

#include <iostream>

int main() {
  hoge one(1), two(2), three(3);
  hoge& five = two + three;
  hoge  six  = one + five;
  std::cout << five.val << std::endl; // こいつまで6になる
  std::cout << six.val << std::endl;
}

あるいはマルチスレッド環境だとリエントラントじゃないのでイッパツでコケます。

Ban 2010/12/23(木) 13:03:55
>    Container<T> operator+(const Container<T>& rhs) const
>    {
>        Container tmp;
>        for(int i=0; i<N; ++i)
>            tmp.m[i] = tmp.m[i] + rhs.m[i];
>        return *this;
>    }
>
>    Container<T> operator=(const Container<T>& rhs) const
>    {
>        Container tmp;
>        for(int i=0; i<N; ++i)
>            tmp.m[i] = rhs.m[i];
>        return *this;
>    }

tmpの結果が使われていないようですが…。

maru 2010/12/23(木) 13:36:07
> 「本当の所」が知りたいなら規格にあたればいいやん。
そりゃそうなんですが、その規格を探せていないもんで。orz

standardってdraftの段階では公開されているんだけど、制定されるとお金を出して
購入しないと手に入らないものが多くて。まあ、著作権の問題から何だろうけど。

と、言い訳を言ってみる。

επιστημη [E-Mail] [HomePage] 2010/12/23(木) 13:44:13
>> 「本当の所」が知りたいなら規格にあたればいいやん。
> そりゃそうなんですが、その規格を探せていないもんで。orz

Working draft N3090
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3090.pdf

Ban 2010/12/23(木) 14:02:27
JIS版(日本語)でいいなら、以下からX3014が閲覧のみ可能。
http://www.jisc.go.jp/

Ban 2010/12/23(木) 14:30:16
>  CString* _data;
>  int     _size;
自力でnew[]/delete[]するならメンバにstd::vector<CString>でも
大差ないように思いますし、その方がすっきりするのでは。

…とか考え出すとtypedef std::vector<std::string>でも大差ないか、となって、
そもそもクラスは必要ですか、というところに至りそうですが。
> ということは、クラスにすること自体が冗長かな?www


元コードにしても、一行=8の実装が switch(num % DATA_MAX)でよいくらいの制限で、
非常に大きなデータ数を扱うなど固定サイズ化によるの性能改善を意識しないならば、
一番素直なのはstd::vector<std::vector<std::string> >にでも
(せいぜいreserve程度で)全部つんでしまうのがありかなぁと。

ちゃんと動作確認してませんが、Containerクラス作らない版?

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <boost/tokenizer.hpp>
#include <vector>

using namespace std;

template<typename T>
struct to
{
    // tokenizerも使ってますし、boost::lexical_castでもいいとは思います。
    T operator()(const std::string& str)
    {
        T   tmp = T();
        std::istringstream  iss(str);
        iss >> tmp;
        return tmp;
    }
};

template<>  // std::string -> std::wstring等も用意した方がいいかも。
struct to<std::string>
{
    std::string operator()(const std::string& str)
    {
        return str;
    }
};

int main(void)
{
    typedef boost::char_separator<char> char_separator;
    typedef boost::tokenizer<char_separator> tokenizer;

    static const int DATA_MAX = 8;

    try
    {
       typedef std::string      type;
//     typedef double           type; どちらなりと

       vector<vector<type> > vcontainer;

      ifstream ifs("d:/work/0301.csv");
      if( !ifs || !ifs.is_open())   return -1;

      char_separator sep(",", "", boost::keep_empty_tokens);
      for(string line; getline(ifs,line); /**/)
      {
            vector<type> container;
            container.reserve(DATA_MAX);

            tokenizer tokens(line, sep);
            // stringにするだけならcopyでもいいのですが。
            std::transform(tokens.begin(), tokens.end(),
               std::back_inserter(container), to<type>());
            vcontainer.push_back(container);
        }
    }
    catch(...) {
        ;// 握りつぶして捨ててるだけ
    }

    return 0;
}

Ban 2010/12/23(木) 14:56:25
typedef double type;に変えるとdoubleのvectorのvectorができる。

# でも簡単に動かしてみたら(doubleとか関係なく)、
# boost::keep_empty_tokensにしてるとtokenizerで余分な要素が取れちゃうっぽい。
# 何だろ。(XPpro,VC2008,boost1.45.0)

初心者++ になりたい 2010/12/23(木) 17:08:51
[[解決]]
皆様お世話になります。
επιστημη さん
理解できました。このような場合、static にしてしまうと問題がありますね。
Ban さん
>tmpの結果が使われていないようですが…。
うまく説明できないのですが、私がtemplate クラスにこだわるのは、
template の柔軟さと、クラスの強力なコンストラクタの力を合わせて、取得する
データの型を判別して、実装者が欲する型のインスタンスを、多態的に
生成できないのだろうか?という発想からです。
>元コードにしても、一行=8の実装が switch(num % DATA_MAX)でよいくらいの制限で、
>非常に大きなデータ数を扱うなど固定サイズ化によるの性能改善を意識しないならば、
>一番素直なのはstd::vector<std::vector<std::string> >にでも
>(せいぜいreserve程度で)全部つんでしまうのがありかなぁと。
たしかにそうですね、紆余曲折、シンプルにたどり着く、それもありだと思います。
一旦解決とさせていただきます。

初心者++ になりたい 2010/12/23(木) 17:33:25
失礼しました
Ban さんの
># でも簡単に動かしてみたら(doubleとか関係なく)、
># boost::keep_empty_tokensにしてるとtokenizerで余分な要素が取れちゃうっぽい。
># 何だろ。(XPpro,VC2008,boost1.45.0)
終わってませんでした、継続しませう。
[7]    0.10000000000000001    double
多分、この0.00000000000000001 このゴミの事ですよね、私も気になっていました。

maru 2010/12/24(金) 11:29:22
επιστημηさん、ありがとうございます。
> Working draft N3090
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3090.pdf
1357ページですか、それも英語。orz

> > ってのしか見つからない。本当の所はどうなんでしょうね。
> 「本当の所」が知りたいなら規格にあたればいいやん。
# こう書けば、誰かさん(επιστημηさんやtetrapodさん辺り)が解説してくれるんじゃ
# ないかと思ったのも事実。

Banさん、ありがとうございます。
> JIS版(日本語)でいいなら、以下からX3014が閲覧のみ可能。
> http://www.jisc.go.jp/
JISは閲覧のみできるんですね。

hirocco 2010/12/24(金) 13:26:56
休み明けに見てみたら、すっごい増えててびっくりしました

つっこみを募集すると、皆様色々つっこんで頂けて、とても参考になります
どうもありがとうございます

たしかに気になっていたんですけど
>> Container& operator+(const Container& line) const
>>  {
>>    static Container temp;
>>    ...
>>    return temp;
>>  }
の部分って、どう書くべきなんでしょうねぇ
さらっとできるかなって思って書いたんですけど、ここが嫌だったんですけど、まぁい
いやで static にしちゃいました
この構造自体がよくないのかもなんですけど、実体だとデストラクタ呼ばれちゃって、
結局エイヤっだったんですよ

ちょっと、お手数ですけど参考にさせてください

Ban 2010/12/24(金) 17:33:30
> 多分、この0.00000000000000001 このゴミの事ですよね、私も気になっていました。

いえ、別件です。

私の方は単純に、用意したダミーデータの最後に , が余分についてただけでした。
 10, 20, 30,
だと、30の後ろにも空の要素があると判断されていて、
 10, 20, 30
なら30が最後で終わるところをdropしてないから30の次に""の要素が取れてたという…orz



ちなみに、初心者++ になりたい さんが書かれている「ゴミ」というのは
ただの誤差であって、浮動小数というのはそういうものです。

# 浮動少数の0.1ってのは典型例なので、「浮動小数」「0.1」とかで
# ちょっと検索するだけでも解説ページがごろごろ出てくるかと思います。

Ban 2010/12/24(金) 18:54:04
>>tmpの結果が使われていないようですが…。

これ自体は、ただのバグ?の指摘です。ローカル変数tmpの値を変更するのに、
そのtmpを使わずに*thisを返すのは意図通りですか、と。


> うまく説明できないのですが、私がtemplate クラスにこだわるのは、
> template の柔軟さと、クラスの強力なコンストラクタの力を合わせて、
> 取得するデータの型を判別して、実装者が欲する型のインスタンスを、
> 多態的に生成できないのだろうか?という発想からです。

ちなみに、std::vector<std::string>でもstd::vectorもstd::stringも立派に
templateクラスだと思いますが、それでご不満そうな理由が私にはよく分かってません。
# 多分、ここに何か「隠れた要求」があるのかなぁとは思いますが。

例えば"10, 20, 30"の各要素をある型として扱いたいとして、
10が数字なのか文字なのかなどが判断できるのは結局実装者だけのはず。
つまり、「実装者が欲する型」を「実装者が予めルール付けするのは必須」なわけですが
(「1番目は数字という静的な仕様」とか「[0-9]+なら数字という動的な判断」とか含む)
templateクラスならをその設定方法を楽にできるはず、ということでしょうか。

例えば以下を例でいうと、どれがイメージに近いでしょうか。
・データに型情報を持たせる (boost::serializationのような読み出し?)
・データの判断条件を実装する (boost::spirit(もしくはtokenizer+regex等)のような読
み出し?)
・何も指定しなくても実装者の意図を推測して良しなに動作する何か。

επιστημη [E-Mail] [HomePage] 2010/12/24(金) 19:36:19
>>> Container& operator+(const Container& line) const
>>>  {
>>>    static Container temp;
>>>    ...
>>>    return temp;
>>>  }
> の部分って、どう書くべきなんでしょうねぇ

フツーに Container temp; でよくね?
コピーや解体のコストがシャレならんなら、
handle-bodyイディオムで参照を保持すれば。

Ban 2010/12/24(金) 19:46:27
憶測ですが、新しいオブジェクトを生成するはずなのに、
それを参照で返そうとしているのがそもそもの問題で。
Container& operator+(const Container& line) const
  ↓
Container operator+(const Container& line) const

無理矢理に参照で返そうとするから問題が出て、
その結果として無理矢理staticになったのではないかなぁと。
なので、こっちも要修正じゃないでしょうか。

επιστημη [E-Mail] [HomePage] 2010/12/24(金) 20:58:37
ですね。

Container& operator+(const Container& line) const
{
  Container temp;
  ...
  return temp;
}

επιστημη [E-Mail] [HomePage] 2010/12/24(金) 21:00:27
あーもー、肝心なとこで&抜き忘れてっし orz

Container operator+(const Container& line) const
{
  Container temp;
  ...
  return temp;
}

てか、僕ならメンバ関数にはせんです。

Container operator+(const Container& c1, const Container& c2)
{
  Container temp;
  ...
  return temp;
}

hirocco 2010/12/24(金) 21:08:57
επιστημηさん、Banさん、参考意見ありがとうございます
でも、Banさんに指摘してもらって、気づきました

>Container& operator+(const Container& line) const
>  ↓
>Container operator+(const Container& line) const
ですねぇ

operator+= をコピーして operator+を書いたら参照残ってました
言われるまで、まったく気づかなかったです
気づかないまま、仕方なくstaticという道を選んでました。。。


επιστημηさんの言うとおり
>フツーに Container temp; でよくね?
と思って、最初はフツーに Container temp;にしてたんですけど、エイヤーで作ったこ
のクラス、デストラクタで解放処理しちゃってて、メソッドの出口でデストラクタが呼
ばれちゃってるのに、内部のメンバ領域が解放されちゃってるのに、参照で返そうとし
てたから、まずかったみたいです
解釈あってますか?

>handle-bodyイディオムで参照を保持すれば
そうですね、この戻り値が 間違えではなく参照型で返すものとして設計した場合は、ス
マートポインタも視野に入れていいかもしれませんね

でも、結論戻り値の間違えでした

επιστημη [E-Mail] [HomePage] 2010/12/24(金) 21:39:36
>>handle-bodyイディオムで参照を保持すれば
> そうですね、この戻り値が 間違えではなく参照型で返すものとして設計した場合は、
> スマートポインタも視野に入れていいかもしれませんね

いや、そうでなくても:

class Container {
  shared_ptr<vector<string>> body_ref;
  ...
};

どーのこーのしておけばコピーその他のコストは小さくなるし。

hirocco 2010/12/24(金) 21:50:19
>> そうですね、この戻り値が 間違えではなく参照型で返すものとして設計した場合
は、
>> スマートポインタも視野に入れていいかもしれませんね
あ、参照型なら戻すのは *thisですね…

επιστημη さん、ありがとう
handle-bodyイディオムをちょっと勉強してみますね
本とか K&R のみで、後は仕事覚えた感じで気がついたら、ずいぶん偏った知識になって
ますので、時間を作って勉強してみますね

ちなみに επιστημη さんって、C++とかの本出してる、επιστημη さん
ですか?
年末年始はいろいろ本読んでみます


初心者++ になりたいさんは、この流れからいいアイディア思いついたかしら、
お邪魔してごめんなさいねwww

επιστημη [E-Mail] [HomePage] 2010/12/24(金) 22:22:55
> ですか?

ですよ!

hirocco 2010/12/26(日) 16:30:53
>ですよ!
そうなんですか

えっと、私は組み込みの設計も多く、メモリの制限の都合上か、組み込み系の専用OSを
使わず、標準ライブラリも極力使わず、標準のmalloc/free、new/deleteを使用しない
で、自ら定義したヒープ領域に確保するできるだけ必要最小限で軽く動く自作関数とし
て再定義/オーバーロードしちゃっていて(もちろんテンプレートなどは未実装)、それを
多用してます、更にそれを制御するドライバをその延長上で作って、さらに更にそれを
使用したアプリケーション開発にいたるので、標準的なC++からかけ離れた環境での中 C
言語の枠を滅多に超えていませんというのが現状です(あ、alloc/free、new/deleteの自
作は組み込みだけですよ)
でも、組み込み系の専用OSの基盤も整いつつあるので、ちゃんとC++のお勉強もしてみよ
うかと思っていました
ただ、マネージド(CLI的な)のも広がりつつあり、かつ、MFCもCMFC〜とかのクラスもド
シドシ増えてきちゃって、ちゃんとしたお勉強はなかなか後回しになってしますのが現
実ですね

C++の本はストラウストラップ氏の本(高かった…)くらいしか持ってないんですけど、重
すぎて全ては読めてないんですよwww
επιστημηさんの名前が書かれていた-「C++テンプレートテクニック」という
本(だったかな?)が最近本屋さんで目立つように置いてましたが、その本はちゃんとC++
のお勉強(というか主にSTL関係)にはどうでしょうか?

επιστημη [E-Mail] [HomePage] 2010/12/27(月) 05:49:31
>「C++テンプレートテクニック」

うーん、書き手が何を言うても宣伝にしかならんので^^;

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