テンプレートの継承 後日談 – 固定ページ 2 – プログラミング – Home

テンプレートの継承 後日談
 
通知
すべてクリア

[解決済] テンプレートの継承 後日談

固定ページ 2 / 2

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

だからー。
横につながりがあって、共通に vector にひと括りに入れられるサンプル出したぢゃん。
派生クラス側で実装を強制するようになってるぢゃん。
どーしたいのよ?派生元が template でないとだめなの?同じことぢゃん。
GetSize() を追加したいなら先のサンプルに自分で追加してみればいい。

実行時にひと括りに扱える = 共通な基底クラスを持つ = 派生で実装する
コンパイル時にひと括りに扱える = template で実装する
ってのはごくごく普通な考え方でしょう。

やりたいのは「実行時の共通化」それとも「コンパイル時の共通化」のどっち?
ソレを決めるのが設計。それはあなたにしかできない。

template 中の <typename T> や <int N> に対して具体的にある型や値を入れると、
そこで初めてメタクラスがインスタンス化されて「具体的なクラス」になるワケよ。
で、変数は「具体的なクラス」で作らないとコンパイラは困っちゃうの。
template <class T> std::map<int, base<T>* > data;
では data を具象化=コンパイルすることができないの。
std::map<int, base<double>* > data; みたいに具体的に指定して初めて
「変数」を作ることができる。


返信引用
てんてく
 てんてく
(@てんてく)
ゲスト
結合: 20年前
投稿: 92
Topic starter  

>tetrapodさん
すみません、説明抜けてましたがstructのほうは共有バッファみたいなもので
ヘッダファイルとして提供されるので勝手に変える事が出来ないんです。
「実行時の共通化」それとも「コンパイル時の共通化」のどっち?
どちらかと言われれば・・・・「コンパイル時」を重視します。

>インスタンス化
知ってるつもりです。ただ<***>が不定な変数を作れる書き方があるのかと思っただけです。
話をややこしくしてしまって申し訳ないです


返信引用
REE
 REE
(@REE)
ゲスト
結合: 23年前
投稿: 240
 

ちなみに SendDataの引数はどのように宣言されているのでしょうか?
(または、宣言する予定)


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

何がしたいのか文面からさーっぱり読み取れないのでアレげです。
自分自身で何がしたいのか整理できていますか?

template でコンパイル時多態すると、クラスや関数が多態しただけ作られます。
ソレでよいのかどうか。その必要があるのか。そもそも template が必要なのかどうか。

2005/12/15(木) 10:07:58 の記入を読む限り template を使う必然が無いのです。
struct base {
virtual ~base(){}
virtual size_t getsize() const = 0;
virtual void* get()=0;
virtual const void* get() const = 0;
};
class XY : public base {
TYPE_XY xy;
public:
virtual size_t get_size() const { return sizeof xy; }
void* get() { return &xy; }
const void* get() const { return &xy; }
};
class XYZ : public base {
TYPE_XYZ xyz;
public:
virtual size_t get_size() const { return sizeof xyz; }
void* get() { return &xyz; }
const void* get() const { return &xyz; }
};
で必要十分。

そして、派生側クラスの実装がそっくりで、なんか無駄っぽいような気がしたら、
そのとき初めて template を使うことを考えればいい。
template<class T> class derived : public base {
T data;
public:
virtual size_t get_size() const { return sizeof data; }
void* get() { return &data; }
const void* get() const { return &data; }
};
んで使うときは
base* p=new derived<TYPE_XY>;
base* q=new derived<TYPE_XYZ>;
とか
std::vector<base*> v;
v.push_back(new derived<TYPE_XY>);
v.push_back(new derived<TYPE_XYZ>);
for (std::vector<base*>::iterator it=v.begin(); it!=v.end(); ++it)
SendData((*it)->get(), (*it)->get_size());
とか。

SendData() を template にすると複数個の SendData() ができちゃうよ。
それがお望み?


返信引用
てんてく
 てんてく
(@てんてく)
ゲスト
結合: 20年前
投稿: 92
Topic starter  

SendData(int 送信先ID、int コマンド、void* データ、size_t サイズ)
です。これを

SendMassageA_to_All()
{
for(.....)
{
struct X = it->second->Get(); // structは送信先IDによって内容が違う
it->second->convert(&X); // エンディアン変換が必要
SendData(it->first, MessageA, &X, sizeof X);
}
}
送信用structのインスタンスが必要だったりするんで迷っていました。
さっきふと気づいたんですが用が済んだら再変換したほうがいいですかね?


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 4年前
投稿: 776
 

> it->second->convert(&X); // エンディアン変換が必要

エンディアン変換って、second のメンバである必要がありますか?
何か他の処理もされてます?

でなければ、例えばこんな感じとか。

void* pX = it->second->Get(); // structは送信先IDによって内容が違う
foo::convert(pX, it->second->Size()); // エンディアン変換が必要
SendData(it->first, MessageA, pX, it->second->Size());


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 4年前
投稿: 776
 

# tetrapod さんの例と大差なかったですね。


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

> struct X = it->second->Get(); // structは送信先IDによって内容が違う
ができるためには Get() が返す構造体(=全て異なる)の真の型が必要となります。
それでは virtual で派生先クラスに処理を委任する意味がない。

解決策1 virtual void* base::hton() {} を用意すればいい。
解決策2 virtual void base::SendData(int message...) {} を用意すればいい。

いずれにせよこの時点で template の出る幕は無い。
提示されてるソースコードはどう見ても「コンパイル時多態」ではない。
継承派生で実装するのが自然だと思われます。


返信引用
てんてく
 てんてく
(@てんてく)
ゲスト
結合: 20年前
投稿: 92
Topic starter  

class base
{
(ry
};
class baseXY : public base
{
TYPE_XY m_st;
public:
baseXY(){//cout 生成}
~baseXY(){//cout 破棄}
virtual size_t size()const {return sizeof(TYPE_XY);}
virtual void* Get(){return &m_st;}
virtual void* BtoL() { TYPE_XY* p = new TYPE_XY; (ry}
};

class baseXYZ
{
TYPE_XYZ m_st;
(ry
};

typedef std::map<int, boost::shared_ptr<base> > Test;
Test test;
void TestFunc()
{
boost::shared_ptr<baseXY> xy( new baseXY);
boost::shared_ptr<baseXYZ> xyz(new baseXYZ);
boost::shared_ptr<baseXYZ> xyz3(new baseXYZ);

test[0] = xy;
test[1] = xyz;

for( Test::iterator it = test.begin(); it != test.end(); it++)
.... // cout など

test[1] = xyz3; //生成と破棄のテスト
}

アドバイスによってこのような実装になりました。
BtoL() の戻り値を消す必要があるとかGet()の要キャストとかが残りましたが
ほぼ望みどおりになったように思います。(delete はまだしも キャストが面倒)
まだツッコミ所あると思われるので一応【解決】ですが
引き続きお願いします。
今は次期用の実装案を模索している状態でこれらを導入するには間があります故
皆様の知恵をお貸しくださいませ m(_ _)m


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

キャストが出る=設計が悪い
です。キャストしなきゃならんとしたら継承派生関係がおかしいか、
基底クラスの設計が悪く virtual にするべきメンバ関数が不足しているか、
なんかその辺のミスがどこかにありそう、と言い切っちゃいます。

で、先に例に出した base::get() や base::get_size() なんかは設計が原始的杉で
見えなくていいものまで外に見せているというか、むしろ base がやるべきことをしてない
悪い例といっていいかもしれません。

SendData に渡せればよい、ではなく、SendData できるという風に考えるといいです。
struct sendable {
virtual ~sendable() {}
virtual void send(...) = 0;
};
にしておき
class hoge_sendable : public sendable {...};
void hoge_sendable::send(...) {
SendData(..., hton(member_1), network_sizeof (member_1));
SendData(..., hton(member_2), network_sizeof (member_2));
}
みたいに実装すればきれいになります。ヘンな後始末もいらない。
必要な virtual 関数がもっと他にあるなら追加すればいいでしょう。
(その場合 sendable という名前が適切かどうかは要検討ですね)


返信引用
てんてく
 てんてく
(@てんてく)
ゲスト
結合: 20年前
投稿: 92
Topic starter  

size() あたりは送信時にも使いますけどget() はファイルに落とすときくらいしか
ひとまとめの用法を思いつかなかったりもするのでvoid* であることは許容できるのですが実
際は
メンバstructをいぢる時にも使えてもいい気がするので出来れば変えたいです。

sendable は解決策2のことでしょうか?
解決策2については一度検討したのですが、メッセージをdefineしているヘッダと
SendData() を宣言してるヘッダのインクルードがめんどくさいかも、とか妄想してみました。
でもやっぱり後始末し忘れるよりはいいかな・・・・。再検討してみます。

おまけですが send(...)これどうやって使うんでしょうか?
省略記号ってことと例外の最後の受けに使うことくらいか知らないんです。
なにかいいサンプルありますか?


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

いやこの場合は ... は「面倒なので記述省略」の意味で使いました。
send(int 送信先, int メッセージID) とか、好きな引数を適当に補ってください。

OO の基本思想は「自分でできることは自分でやる」です。
つまり「できることは可能な限りメンバ関数で実装する」です。
「非メンバ関数に自分のメンバ変数を公開して勝手に使ってもらう」のはおかしい。

なんかスレタイトルから限りなくずれてきたような気がします。
長くなりすぎてますし、続けたいのなら別スレ作ってください。


返信引用
てんてく
 てんてく
(@てんてく)
ゲスト
結合: 20年前
投稿: 92
Topic starter  

了解です。回答くださった皆様、いろいろとありがとうございました!


返信引用
固定ページ 2 / 2

返信する

投稿者名

投稿者メールアドレス

タイトル *

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