ページ 1 / 1
C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月09日(月) 16:23
by 宮 みや
以下のコードのように FlagMgr クラスのメンバ変数 m_flagList に
テンプレートを使っていろんな型を追加したいのですが、
どのように作ったらよいものか、見当がつきません。
アドバイス、お願いします。
当方、C++勉強中の身です。
コード:
// Flag --------------------
template <typename _T>
class Flag{
_T m_flag;
public:
_T getFlag() { return m_flag; }
void setFlag(_T t) const { m_flag = t; }
};
// FlagMgr --------------------
class FlagMgr {
std::list<Flag<int>> m_flagList; // int だけではなく String とかもリストに追加したい
public:
void addList(Flag<std::string> flag)
{
//m_flagList.push_back(flag); // エラー。ここにいろんな型を追加したい
}
};
Re: C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月09日(月) 17:01
by usao
とりあえず Flag<int> と Flag< std::string > は「別の型」なので,そういう扱い方はできません.
(その点は大丈夫でしょうか?)
なので,このコードをコンパイル通すために頑張るのではなく,代替的な方法を考える方が良いと思います.
具体的に何を実現したいのかが明確になれば代替案を(誰かが)示せるかもしれません.
(たとえば,テンプレートではなく union とか void* とかでどうにかするとか)
以下のような情報があると良いかもしれません.
>いろんな型
これの種類って固定でしょうか? それとも本当に”任意の型”を考えておられるのでしょうか?
あと,仮にそのリストが無事にできたとして,
FlagMgrはそのリストをどうやって使うことを想定されているのでしょうか?
そのm_flagListを使う側のコード(もちろんコンパイルは通らないでしょうけど雰囲気で)みたいなのが
示されると雰囲気がつかめて良いかもしれません.
Re: C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月09日(月) 21:39
by tk-xleader
type-erasure法でも使えば…
コード:
// Flag --------------------
class Flag{
class FlagBase{
public:
virtual ~FlagBase(){}
};
template<typename _T>
class FlagInternal : public FlagBase{
_T m_value;
public:
FlagInternal(_T t):m_value(t){}
_T getFlagValue(){ return m_value;}
};
FlagBase* m_flag;
public:
template<typename _T> getFlag() { return dynamic_cast<FlagInternal<_T>*>(m_flag)->getFlagValue(); }
template<typename _T> void setFlag(_T t) const { delete m_flag; m_flag = new FlagInternal(t); }
~Flag(){delete m_flag;}
};
// FlagMgr --------------------
class FlagMgr {
std::list<Flag> m_flagList; // int だけではなく String とかもリストに追加したい
public:
void addList(Flag flag)
{
m_flagList.push_back(flag); // エラー。ここにいろんな型を追加したい
}
};
これで、とりあえずはFlagにはどんな型の変数でも詰め込むことが出来ますが…
Re: C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月09日(月) 21:52
by tk-xleader
一応上記の方法でstd::listに任意の型の変数を持ったFlagを詰め込むことが出来ることは出来るんですが、その後の使い道を考えると、あまりうまくないというか何と言うか…
それと、C++のコードの作法としては非常に良くないので、そのあたりも注意していただきたいです(あくまで、サンプルコードということで…)
Re: C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月10日(火) 00:34
by KORYUOH
C++11でならばtupleがあったのでそれを考えてみては?
Re: C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月10日(火) 13:22
by 宮 みや
usao さんへ。
返信ありがとうございます。
別の型だけど、テンプレートを使っていろいろな型を入れられるような処理を作れるかな?
と思い勉強の一環として試していました。
事の発端として、脱出ゲームをC++で作ったらどうなるかな?というところから
フラグをリスト(実際には map?)で管理しようかなという考えに至って、
いろいろな型のフラグをひとつのリストに詰め込めないものかなとごちゃごちゃ試す内に
質問のコードのようなものが出来上がりました。
フラグ管理を実装するにあたって、実際にはただ、いくつか変数を持っておくだけだったり
list<int>でも十分対応できるのですが、テンプレートの勉強も含めて試していた次第です。
現状、アドバイスをいただきまして、unionの型としてなんとなくそれらしい形にはなったのですが、
もっとテンプレートや継承といったC++らしい形でプログラムできたらいいなと思っています。
オブジェクト指向の書き方がなかなか身に付かず、いろいろと苦戦してます(汗
使い方としては Flagクラスをリストから取り出して、ゲッター・セッターで読み書きができればいいと
思っています。その際、intやfloat、std::string、自分でつくったクラス、配列など自由に入れられたら
便利なのかな?と考えたのですが、脱出ゲームのフラグ管理の設計としては必要ないですかね?
イメージは Objective-C の NSArray に NSString (文字列クラス) や NSNumber (数値クラス) など
いろんな型を入れる事ができるので、そんなことを C++ でやってみたかったです。
そこでさらに、NSString、NSNumber等をテンプレートにできるかな?という試みだったのですが、
全然技術力が追いつかなかったです (--;)
union は知らなかったのですが、これなら形に出来るかなと一応コードを書いてみて
ひとまず実装できるまでにはなりました。アドバイスありがとうございます^^
まだ、こうした方が良いよとか、なにかあれば教えていただけるとありがたいです。
あとlist から vector に変えてみました。
コード:
// 実装用フラグデータ --------------------
struct sFlagData {
static const int CDATA_MAX = 8; // 文字列の最大数
// フラグデータ
union uFlagData {
int iData;
bool bData;
float fData;
char cData[CDATA_MAX];
};
// フラグタイプ
enum eFlagDataType {
eFLAG_DATA_TYPE_INT,
eFLAG_DATA_TYPE_BOOL,
eFLAG_DATA_TYPE_FLOAT,
eFLAG_DATA_TYPE_CHAR_8, // 7文字まで可
};
// データ
uFlagData uData; // フラグデータ
eFlagDataType eType; // フラグタイプ
// コンストラクタ
sFlagData(){}
sFlagData(int val) : eType(eFLAG_DATA_TYPE_INT) { this->uData.iData = val; }
sFlagData(bool val) : eType(eFLAG_DATA_TYPE_BOOL) { this->uData.bData = val; }
sFlagData(float val) : eType(eFLAG_DATA_TYPE_FLOAT) { this->uData.fData = val; }
sFlagData(const char *val) : eType(eFLAG_DATA_TYPE_CHAR_8)
{
assert(strlen(val) < CDATA_MAX); // エラーチェック (オーバーフロー)
strcpy(this->uData.cData, val); // 文字列代入
}
sFlagData(uFlagData val, eFlagDataType eType) : eType(eType) { uData = val; }
};
// FlagMgr --------------------
class FlagMgr {
std::vector<sFlagData> m_sDataList;
public:
void addVectorData(sFlagData data) { m_sDataList.push_back(data); };
std::vector<sFlagData> getFlagData() { return m_sDataList; };
};
Re: C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月10日(火) 13:36
by 宮 みや
tk-xleader さんへ。
コードありがとうございます。
type-erasure法というものがあるのですね。
折りをみて試してみます^^
それと、あまりうまみがないことや
C++のコードの作法としては非常に良くないというのは
どういうところでそう思われたのでしょうか?
ほんとに当てずっぽうなんですが、キャストするのが良くないとか?
KORYUOH さんへ
tuple ですか。まだ調べてないのですが、近いうちに検索してみます。
情報ありがとうございます。
Re: C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月10日(火) 14:11
by usao
>脱出ゲームのフラグ管理
という目的に対して良いかどうかは別として,勉強目的でいろいろこねてみるのはよいと思います.
(フラグ管理なら,単にboolの配列的なものさえあればいいんじゃないかな?とか思いますが)
なんでも放り込んでおけるlistなりvectorなりについては,
それがどうにかこうにか実現できたとしても,それを使う側のコードがわりと苦しいことになるんじゃないかなー…と.
具体的には,”そのlistのN番目の要素が実際には何型のデータを保持しているのか?” をどうにかして
そのlistの中身を利用する側の箇所で判断できないと,データを取り出せないわけです.
例えば今回enumで書かれた例で言えば,
「eTypeという変数を見て,共用体になってるデータをどう読めばいいか」
をデータを読む側(FlagMgrのメソッド内かな)が判断し,適切に( if elseif ... なり switch なりで )処理分岐する必要があるのですよね?
(要するに,M種類のデータが扱われているとしたら,M種類のGetterがあり,
格納されているデータ値を参照するには実際のデータに適合するGetterを正しく選択して用いねばならないという形.)
これって,大変です.→うまみが少ない
Re: C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月10日(火) 14:52
by h2so5
Objective-Cの場合はメッセージ式によってメソッドの動的結合が可能なので、クラスが分からなくても汎用型に対してメソッドを呼び出すことが可能です。
しかし、C++の場合はクラスがわからないとメソッドが呼び出せないため、type-erasureによる汎用型を使ってしまうとダウンキャストの必要性が生じます。
宮 みやさんのアプローチはObjective-Cではあまり問題にならないかもしれませんが、C++ではデメリットが大きいと思われます。
Re: C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月10日(火) 23:39
by tk-xleader
「うまみがない」というのは、この場面においてtype-erasure法を使うことが果たして適切かどうかといえば、そうではないように思われるということです。type-erasureを使うのが効果的な場面は、共通のインターフェイスを持った、継承関係にない複数の型の処理をまとめるときでしょうね。
今回のように、ただフラグをまとめたいというだけで、関係のない型を無理矢理std::listに挿入するような使い方はまずしないですね。
「C++のコードの作法としては非常に良くない」というのは、type-erasure法に関係するところではなくて、サンプルコード全体を見たときに、C++としてはお行儀の悪いコードになっているということです。例えば、FlagBaseクラスはそのインスタンスを外部から直接作り出すことはしてはいけないのですが、デフォルト/コピーコンストラクタが実装されていないためコンパイラの自動実装がなされてしまい、それがpublicになるので、インスタンスを直接生成できてしまうという問題があります。
他にも、ポインタ型に対するdynamic_castの評価値をノーチェックで利用している(dynamic_castはポインタ型を対象としたキャストに失敗した場合、NULLに評価される。)とか、newで生成したオブジェクトを生ポインタで扱っている(可能な限りスマートポインタを使用すべき)こと。もっと基本的なことを言えば、const&を適切に扱っていないために、テンプレート仮引数_Tのコピーコストに関わらず、Setterを呼ぶたびにインスタンスのコピーが生成されてしまうといった点が挙げられるでしょうね。
Re: C++ テンプレートのいろんな型を list に追加する方法
Posted: 2013年9月11日(水) 19:35
by 宮 みや
usao さん
以降、型を追加するたびに if elseif 等で判別していく
処理を追加していくのは煩わしいですね。
あれから受け取る側で型判定して、という処理も書いてみましたが確かに大変でした。
呼び出すたびに型判定するのもコードが長ったらしくなって嫌ですね。
こうまで、しなければならないなら
脱出ゲームのフラグ管理にはboolの配列で十分かな?と思いました。
[hr]
h2so5 さん
なるほどです。
Objective-C と C++ ではソフトを作るにあたって多少考え方が変わってきそうですね。
Objective-C では NSArray で簡単にいろいろな型を入れられたので
同じように出来るかなと思ってました。
[hr]
tk-xleader さん
記述していただいたコードを試している最中です。
おかげさまで別の型をリストに詰め込むことができました。
デバッグしてて気づいたのですが、
m_flagListに別の型を追加していくごとに、今まで追加した _T m_value の型が
最後に追加した型に変換されて判断されてしまうんですね。
dynamic_cast で 型違いのため NULL が返され、実行エラーになってしまう
バグに苦しんでるところです。
型を消してしまうので、こういった問題が含まれているんですね。
他にもいろいろ注意点を教えていただいてありがとうございます。
[hr]
まだプログラムは値 (_T) を取得するメソッドでエラーを起こす状態ですが、
ひとまずトピックの「テンプレートのいろんな型を list に追加する」という
目的は達成できたので「解決」を押しておきます。