質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

2回答

801閲覧

サイズの異なるvecterをメンバにもつ構造体をループで同じ処理をしたい

kosetei.dn

総合スコア0

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2023/03/16 09:13

編集2023/03/16 14:44

実現したいこと

  • サイズの異なるvecterをメンバにもつ構造体をループで同じ処理をしたい

前提

異なるサイズのVectorをメンバに持つ構造体に対して同じ処理をしたい場面があります。
現在は以下のようにひたすら書いているんですが、ループを使って一括で書く方法はないでしょうか。

問題

該当の構造体はフレームワークで提供されるもので変更できません。

該当のソースコード

C++

1enum 2{ 3 TCU, 4 ALE, 5 TMP1, 6 TMP2, 7 TMP3, 8 TMP4, 9 PRS, 10 11TAGCOUNT 12}; 13 14int32_t respSize[TAGCOUNT] {13 ,13 ,6 ,3 ,3 ,3 ,2 }; 15int32_t respData[TAGCOUNT] {5 ,3 ,4 ,35 ,4 ,2 ,1 }; 16 17struct RESP 18{ 19 vector<int32_t> TCU; // 要素数:13 20 vector<int32_t> ALE; // 要素数:13 21 vector<int32_t> TMP1; // 要素数: 6 22 vector<int32_t> TMP2; // 要素数: 3 23 vector<int32_t> TMP3; // 要素数: 3 24 vector<int32_t> TMP4; // 要素数: 3 25 vector<int32_t> PRS; // 要素数: 2 26}; 27 28main() 29{ 30 RESP resp; 31 resp.TCU.assign(respSize[TCU], respDate[TCU]); 32 resp.ALE.assign(respSize[ALE], respDate[ALE]); 33 resp.TMP1.assign(respSize[TMP1], respDate[TMP]); 34 resp.TMP2.assign(respSize[TMP2], respDate[TMP2]); 35 resp.TMP3.assign(respSize[TMP3], respDate[TMP3]); 36};

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

episteme

2023/03/16 09:27

assing ってなんですか?
kosetei.dn

2023/03/16 09:45 編集

誤字です。assignです。修正しました
int32_t

2023/03/16 10:03

RESPのデータメンバは質問文では7個ですが、実際も7個ですか?
fana

2023/03/16 10:15

とりあえず同名の再定義でコンパイル通らないと思うんだけど…
matukeso

2023/03/16 10:24

auto *p=&resp.TCU; for(int i=0;i!=5;++i) p[i].assign( size[i], data[i]); みたいな事がやりたい? (この例は多分UB踏んでると思うけど。踏んでるよね?)
dameo

2023/03/16 11:45

回答する気はないですが、C++の仕様バージョンは書いた方がいいかもですね。
dameo

2023/03/16 13:23 編集

なんか進んでなかったので続きを少し。。。 クラスメンバポインタ https://learn.microsoft.com/ja-jp/cpp/cpp/pointers-to-members?view=msvc-170 使うとこんな感じになります(const 対応してないけど)。 vector<int32_t>& respMemberByIndex(RESP& resp, int index) { static vector<int32_t> RESP::* const accessor[RESP_COUNT] = {&RESP::TCU, &RESP::ALE, &RESP::TMP1, &RESP::TMP2, &RESP::TMP3, &RESP::TMP4, &RESP::PRS}; return resp.*accessor[index]; } void func() { RESP resp; for(int i=0;i!=RESP_COUNT;++i)respMemberByIndex(resp, i).assign(respSize[i], respData[i]); ... } (C++バージョンがないので)enumは綺麗に扱えません。
kosetei.dn

2023/03/16 13:23

C++のバージョンはすいません分からないんです。明日会社に行ったら聞いてみます。
kosetei.dn

2023/03/16 13:24

RESPのメンバは可変です。下手すると50とか100とかあります。
dameo

2023/03/16 13:26

可変だとビルド通りませんw
kosetei.dn

2023/03/16 13:28

同名の再定義というのは構造体の宣言の話でしょうか? それともenumと構造体のメンバが同じという意味? 前者ならすいません実際は別の名前で宣言してますので分かりません。 後者は名前は変えてますが、同じ名前でもコンパイル通りました。
dameo

2023/03/16 13:29

TMPが何度も出ていることですよw
kosetei.dn

2023/03/16 13:30

>> auto *p=&resp.TCU; for(int i=0;i!=5;++i) p[i].assign( size[i], data[i]); みたいな事がやりたい? はいその通りです。ですがメンバのサイズが違うのにそれで行けるのかわからなくて。 あとすみません。プログラミング初めてまだ1ヶ月も経ってないのでUBを踏むというのが何かわかりません。
kosetei.dn

2023/03/16 13:31

>>TMPがなん℃も出ている すいません気づきませんでした。実際のメンバ名の前半3文字だけ取ってきていたのでこうなってしまいました。 直します。
kosetei.dn

2023/03/16 13:33

>>可変だとビルド通りません すいません可変というのは自分が書いた後に仕様書が変われば誰かが追加したり、順番が入れ替わったりするかもしれないということです。コンパイルした後にメンバの数が変わることを要求しているわけではありません。
kosetei.dn

2023/03/16 13:35

仕様書が変わって書き換えの必要が生じた際に出来るだけ書き換える場所を少なくしておかないと書き換え忘れが生じる可能性があるため、今回の様な質問をさせていただきました。
dameo

2023/03/16 13:38 編集

依存するクラスのメンバ変数が増えて、その変数を使わなければならないのに、こちらの実装が変わるのは当たり前ですよ。それは可変とは言わないし、書き換え忘れるとかの次元の話ではありません。UBというのはUpper Boundary。
kosetei.dn

2023/03/16 13:42

>>依存するクラスのメンバ変数が増えて、その変数を使わなければならないのに、こちらの実装が変わるのは当たり前 そうなんですが、実際構造体のメンバに同じ処理をなん度も行っていると、どうにかして関数化、ループによる処理をして、書き換える場所を少なくしておかないと、書き換える場所が1000箇所ぐらいになってしまいます。
kosetei.dn

2023/03/16 13:43

>>UBというのはUpper Boundary すいませんUpper Boundaryで検索してもプログラミング用語としての用法が出てこないんですが、何かのツールでしょうか...
dameo

2023/03/16 13:44

普通に1000箇所しないといけないだけですよ。 どうしても嫌なら相手のコードをパースして実行コードとテストコードを自動生成するしかないですね。
dameo

2023/03/16 13:44

英語くらい調べましょう。なんか以前C++でリフレクションしたいといってゴネてた人みたい。。。
kosetei.dn

2023/03/16 13:49

すいません、本当に初心者で分からないことだらけなんです。 Upper Boundaryというのは、ソートされた配列内で、ある値より大きい要素のうち一番左側の位置のこと、で合っていますか?
dameo

2023/03/16 13:49

繰り返します。英語くらい調べましょう。
kosetei.dn

2023/03/16 13:52

>> 相手のコードをパースして実行コードとテストコードを自動生成するしかないですね。 実際それを真剣に考えてますが、そのツールをきちんと共有しないと自分以外の人にとっては保守性が悪いことは変わりませんし、アサインされて1ヶ月も経ってない自分がツール作ったんで以降これを使って下さい、とはなかなか言えないです。
kosetei.dn

2023/03/16 13:57

>> 英語くらい調べましょう。 Upper Boundaryというのは調べた限りプログラミングの専門用語ではないですし、プログラミングに関連して英文を検索しても前述の様な用法しか出て来ませんでした。具体的にどういう検索をすれば、想定する意味が返ってきますか?
dameo

2023/03/16 13:57

あなたの都合は知りませんよ。ツール作るなら作ればいいし、嫌なら何個でも実装するだけ。 変更するならテストだってしないといけないし、変更忘れなんてありえません。 ツールだってしっかり汎用に作ってしっかりテストされてれば1ヶ月だって使いますよ。 あなたの技量の問題です。
dameo

2023/03/16 13:58

英語も訳せないなら向いてないです。
kosetei.dn

2023/03/16 14:03

直訳すれば上側の境界なのは分かりますが、それを踏む、という言葉の意味が分かりません。別の特殊な意味で使われている例を調べた結果前述の様な用法が英文の記事ではヒットするのですが、間違っているのでしょうか。また、こちらの認識が間違っているにも関わらず、ただ自分で調べることを勧めてくることにはどう言った意味があるのでしょうか。
dameo

2023/03/16 14:05

あとは想像力の問題ですね。向いてません。
kosetei.dn

2023/03/16 14:05

>> ツールだってしっかり汎用に作ってしっかりテストされてれば1ヶ月だって使う そうですね、私の技量がそこまで高ければ、そう言ったこともあるかもしれませんが、残念ながら、私はそんなレベルではありません。
kosetei.dn

2023/03/16 14:11

>> あとは想像力の問題ですね。向いてません。 そうですね。想像力は乏しい方かもしれません。残念ながら私には相手が何を言いたいのか察する能力が低い様ですので...
dameo

2023/03/16 14:16

まずは嘘をつかずに働くことをオススメします。
kosetei.dn

2023/03/16 14:18

残念ながら働いているんです。つい最近までニートみたいな状態でしたが、工場に入ったはずが何故か組み込みの開発部署に異動になり必死です。
dameo

2023/03/16 14:26

じゃあこんなところで聞かずに、現場の人とよく相談してください。 とりあえず組み込みだと処理系が特殊なものであることもあるし(OSのないマイコンは特に)、コンパイラだって不具合がないわけではないので、あまり難しいことをすると煙たがられるケースもありますよ。 例えば私のあげたメソッドポインタとかは使わない方が無難だし、C++の新しい機能使うとか言うとキッチリ検証してない限り嫌がられます。 モノによりますが、PCで動くソフトのように考えない方がいいです。
kosetei.dn

2023/03/16 14:33

はい、本当にその通りです。一応VxworksというOS上で動いている様で組み込みと言っても、CPUは IntelのATOM系とかいう話をちらっと聞きました。ですが特殊なことをやるのがNGなのはその通りで、 なので基本的な書き方で今回の様なケースをクリア出来るのではないかと、質問した次第です。 なお当然ですが同僚には質問済みです。c++詳しくないから分からんと言われました。上司はコーディングテクニックの質問に答えてくれるほど暇じゃないと思うので聞きづらいです。 最悪明日の打ち合わせで他の方が如何されてるのかちらっと聞こうとは思ってますが...
dameo

2023/03/16 14:48 編集

VxWorksは大昔使ったことありますよ。制限的な意味だとかなりマシな方だと思いましたが、Linuxとかと比べれば汎用性は低い感じでした。多分どんな現場でもC++で頑張ってる方はいると思うので、改善をしたいのであれば、上司とかに拘らず人脈を増やすのが最善です。現場で師匠的な位置づけの方が見つかるまでは大人しくダルいやり方でやるしかないですね。
kosetei.dn

2023/03/16 14:49

大昔、、、これでも新製品の、超精密品の製造機の設計なんですが、、、 ま、まぁ、信頼性が大事な現場では枯れた技術の方が好まれるって言いますし...
kosetei.dn

2023/03/16 14:55

ちなみに、別に新人だからいいんですが、全部一個づつ書いてあっても、「うわ、こいつ冗長な書き方してんな。」って馬鹿にされることはないですかね? まぁコードレビューとかうち全然できてないみたいですが。。。
dameo

2023/03/16 15:04

組み込み制御系は単純な方が好まれるよ。絶対に間違いがないって一目で分かる方が嬉しい。
matukeso

2023/03/16 18:55

Undefined Behavior 未定義動作
dameo

2023/03/16 19:25

聞いてみるもんですねw まさかそっちだとは思わなかったw (未定義動作の)罠を踏むという意味で踏んでると。日本語難しい。
fana

2023/03/17 01:47 編集

まず,ご自身が危惧している事(:構造体の定義がくっちゃくちゃに変わるかもしれない?)や,現状で「どうなん?」って思ってるようなこと(各メンバに対して同じような処理を延々と書かないとならない?)等々について,その構造体を提供してくる側とよく話し合うべきと思う. 例えば,その構造体の定義が本当に今後変わり得るのだとしても,「あなたが思っているよりもマシな変わり方しかありえない」という想定が可能なのかもしれませんよね. (e.g. 少なくとも既存のメンバの順序を無意味に入れ替えたり,既存メンバの間に新しいのを差し込んだりとかはしないだろ,みたいな)
fana

2023/03/17 02:00

示されたコードだけを見た感じからだと,とりあえず struct REST{ vector<int32_t> Data[TAGCOUNT]; }; のようにしない理由(あえて個々のvectorとして定義する理由)とは何なのか? というあたりからかなぁ.
matukeso

2023/03/17 06:38

>あえて個々のvectorとして定義する理由 「該当の構造体はフレームワークで提供されるもので変更できません。」って書いてある。 他人や上司が作ったんでしょ。
fana

2023/03/17 06:51

だからまず提供側と話せば? って言ってる. その際にこのあたりの話を切り口にすれば良いのでは?と言ってるだけだ. 提供側が実際に話せる範囲にいないならばどうしようもないだろうが.
kosetei.dn

2023/03/17 13:27

誰が作ってるのかさっぱり分かりません。何せ私はまだ同じ部署の10数人の顔を覚えるので必死なので。 ちなみに私が今書いてるのは、過去の製品から流用してきた定義で書いているので、現在要件定義真っ只中の現在開発中の製品の仕様が、確定するのはだいぶ先でしょう。 しかし、とりあえず過去の流用でいいからシュミレーターを作って他のユニットのために動かして欲しい、と言う状態です。実際の製品ではそれぞれのメンバに同じ処理をするなんてそんなにないでしょうから、問題ではないんだと思います。 しかし新製品の仕様が決まるたびにシュミレーターも対応していかなければなりません。結果毎日2、30箇所書き換えが生じます。せめて一つの当たり一箇所の書き換えで済ませたいよぉ、と言うのが私の偽らざる本音です。今日会社に行ったら、メンバの名前は変わるわ、増えるわ減るわ入れ替わるわの地獄でした。全部おんなじような処理なんだから、ボタンひとつで書き換わるようにしたいよぉ。今日は13時間労働だったよぉ。
kosetei.dn

2023/03/17 13:33

>>組み込み制御系は単純な方が好まれるよ。絶対に間違いがないって一目で分かる方が嬉しい。 今日上司に全く同じことを言われました。中学生でも上から読んでいけばわかるように書いて欲しいとのことです。
dameo

2023/03/17 13:39

作ってる人とはそのうち話す機会もあるでしょう。ただ違うチームの実装に対して安易に変更要求をするのはお金が絡むので至難の業かもしれません。ただ同時に開発してるなら上を通せば大して手間ではないでしょう。変更前提のモジュールならテストするので、そこで同時に良くなるなら誰の懐も傷まないということです。別に30箇所くらい大した手間ではないですよ。13時間なら普通に寝れるので何の問題もありませんが、開発終盤で法に触れずに無理できるように残業は抑えましょう。 なお、自分のコードだけならスクリプトで実行コードとテストコードを吐くだけで大丈夫です。瞬殺ですね。
kosetei.dn

2023/03/17 13:39

>> Undefined Behavior 未定義動作 理解しました。確かに仰っているやり方だとベクターの要素数が違ったり、そもそもintではなくdoubleだったり charだったりするので、何が起こるか分かりません。一応ベースはパソコンと同じ様ですし、現状実験機はなにもつながって無いので、大丈夫だとは思いますが、余り試したくはありません。
kosetei.dn

2023/03/17 13:44

>> なお、自分のコードだけならスクリプトで実行コードとテストコードを吐くだけで大丈夫です。瞬殺ですね。 本日はその書き換えスクリプト作成に7時間かかりました。そして完成しませんでした。他に会議4時間あったので実際の仕事としてコード書き換えてたのは、2時間も無いですね。(スクリプト書くより手で書き換えた方が早いんじゃ、、、と10時になったからと会社から追い出されながら思いました。)
dameo

2023/03/17 13:59 編集

未定義動作以前にコンパイルが通らない。Upper Boundaryの解釈は苦肉の策w > スクリプト書くより手で書き換えた方が早い 書き慣れてない処理や言語だとそうなりがち。スクリプトは時間あるときにやればいいです。 あと(自動化しない)手作業の速度アップは地味に大事。
guest

回答2

0

回答ではありません

直接的な回答ではなく、コメントで聞いた内容に基づくスクリプトの骨子を伝えるための記述です。

前提

環境

Ubuntu 20.04 amd64
GCC 9.4.0
python 3.8.5

元のソースコード

質問のと少し違います。

resp.h

c++

1#ifndef __RESP_H__ 2#define __RESP_H__ 3#include <cstdint> 4#include <vector> 5enum { 6 TCU, 7 ALE, 8 TMP1, 9 TMP2, 10 TMP3, 11 TMP4, 12 PRS, 13 TAGCOUNT 14}; 15extern int32_t respSize[TAGCOUNT]; 16extern int32_t respData[TAGCOUNT]; 17struct RESP { 18 std::vector<int32_t> TCU; // 要素数:13 19 std::vector<int32_t> ALE; // 要素数:13 20 std::vector<int32_t> TMP1; // 要素数: 6 21 std::vector<int32_t> TMP2; // 要素数: 3 22 std::vector<int32_t> TMP3; // 要素数: 3 23 std::vector<int32_t> TMP4; // 要素数: 3 24 std::vector<int32_t> PRS; // 要素数: 2 25}; 26#endif

resp.cpp

c++

1#include "resp.h" 2int32_t respSize[TAGCOUNT] = {13 ,13 ,6 ,3 ,3 ,3 ,2 }; 3int32_t respData[TAGCOUNT] = {5 ,3 ,4 ,35 ,4 ,2 ,1 };

main.cpp

c++

1#include "resp.h" 2int main() { 3 RESP resp; 4 resp.TCU.assign(respSize[TCU], respData[TCU]); 5 resp.ALE.assign(respSize[ALE], respData[ALE]); 6 resp.TMP1.assign(respSize[TMP1], respData[TMP1]); 7 resp.TMP2.assign(respSize[TMP2], respData[TMP2]); 8 resp.TMP3.assign(respSize[TMP3], respData[TMP3]); 9 return 0; 10}

スクリプト

コンセプト

RESPクラスを解析するために、ソースコードを直接読むと、正確なパースが難しくなります。なので一度ビルドを通したものから、デバッグ情報を抽出してそのデータをパースする形で考えてみました。elf/dwarf形式のデバッグ情報からRESPの型情報を抜きたいのですが、objdumpは面倒そうで、llvm-dwarfdumpを使うのも仰々しいので、いつも使うツール群のgdbから抜く形にしてみました。

gdbから抜くスクリプト

sh

1g++ -c -g -Wall resp.cpp -o resp.o 2g++ -c -g -Wall main.cpp -o main.o 3g++ -g -Wall main.cpp resp.o -o app 4gdb --quiet app >gdb_ptype_enum.txt <<EOF2 5ptype TAGCOUNT 6EOF2 7gdb --quiet app >gdb_ptype_resp.txt <<EOF2 8ptype RESP 9EOF2

こんなデータが抜けます。
gdb_ptype_enum.txt

plain

1Reading symbols from app... 2(gdb) type = enum : unsigned int {TCU, ALE, TMP1, TMP2, TMP3, TMP4, PRS, TAGCOUNT} 3(gdb) quit

gdb_ptype_resp.txt

plain

1Reading symbols from app... 2(gdb) type = struct RESP { 3 std::vector<int, std::allocator<int> > TCU; 4 std::vector<int, std::allocator<int> > ALE; 5 std::vector<int, std::allocator<int> > TMP1; 6 std::vector<int, std::allocator<int> > TMP2; 7 std::vector<int, std::allocator<int> > TMP3; 8 std::vector<int, std::allocator<int> > TMP4; 9 std::vector<int, std::allocator<int> > PRS; 10} 11(gdb) quit

抽出データを元にC++コードを生成

generate.py

python

1import re, os 2def chop(s): 3 return s.rstrip(os.linesep) 4names = [] 5with open('gdb_ptype_enum.txt', 'rt', encoding='utf-8') as f: 6 next(f); 7 names = re.match('\(gdb\) type = enum : unsigned int \{([^}]*)\}$', chop(next(f))).group(1).split(', ') 8print(f'names(読んだとき)={names}') 9if names[-1:][0] != 'TAGCOUNT': 10 raise 'enumの最後がTAGCOUNTじゃありません' 11names = names[:-1] 12print(f'names(最終)={names}') 13with open('gdb_ptype_resp.txt', 'rt', encoding='utf-8') as f: 14 next(f); 15 re.match('\(gdb\) type = struct RESP \{$', chop(next(f))).group(0) 16 members = [] 17 while True: 18 line = chop(next(f)) 19 if re.match('\}$', line): 20 break 21 m = re.match(' std::vector<int, std::allocator<int> > (\w+);$', line) 22 if m: 23 members.append(m.group(1)) 24print(f'members={members}') 25if len(names) != len(members): 26 raise 'enumの数とRESPのメンバ数が合いません' 27for name in names: 28 if name not in members: 29 raise 'enumの名前に該当するRESPのメンバがありません' 30with open('hoge.h', 'wt', encoding='utf-8') as f: 31 print("""#ifndef __HOGE_H_ 32#define __HOGE_H_ 33#include "resp.h" 34inline std::vector<int32_t>& respMemberByIndex(RESP& resp, int index) { 35 static std::vector<int32_t> RESP::* const accessor[TAGCOUNT] = {""", end='', file=f) 36 print(', '.join([f'&RESP::{name}' for name in names]), end='', file=f) 37 print("""}; 38 return resp.*accessor[index]; 39} 40#endif 41""", file=f)

生成してるコードは私がコメントに書いたメンバポインタを使ったコードですが、オススメはしません。あくまで意図を説明する用です。

生成されるコードは以下のようなコードになります。
hoge.h

c++

1#ifndef __HOGE_H_ 2#define __HOGE_H_ 3#include "resp.h" 4inline std::vector<int32_t>& respMemberByIndex(RESP& resp, int index) { 5 static std::vector<int32_t> RESP::* const accessor[TAGCOUNT] = {&RESP::TCU, &RESP::ALE, &RESP::TMP1, &RESP::TMP2, &RESP::TMP3, &RESP::TMP4, &RESP::PRS}; 6 return resp.*accessor[index]; 7} 8#endif

生成されませんが、このコード用のmainは以下になります。
main_new.cpp

c++

1#include "resp.h" 2#include "hoge.h" 3int main() { 4 RESP resp; 5 for(int i=0;i!=TAGCOUNT;++i)respMemberByIndex(resp, i).assign(respSize[i], respData[i]); 6 return 0; 7}

構造体に多少変更があってもある程度は抽出してくれますし、表記揺れにも多少は強いです。ただし、ビルドツールの変更にはとても敏感なので注意してください。

最後に全生成スクリプト

これらのコードを全部生成して実行するシェルです。

sh

1cat >main.cpp <<EOF 2#include "resp.h" 3int main() { 4 RESP resp; 5 resp.TCU.assign(respSize[TCU], respData[TCU]); 6 resp.ALE.assign(respSize[ALE], respData[ALE]); 7 resp.TMP1.assign(respSize[TMP1], respData[TMP1]); 8 resp.TMP2.assign(respSize[TMP2], respData[TMP2]); 9 resp.TMP3.assign(respSize[TMP3], respData[TMP3]); 10 return 0; 11} 12EOF 13cat >resp.h <<EOF 14#ifndef __RESP_H__ 15#define __RESP_H__ 16#include <cstdint> 17#include <vector> 18enum { 19 TCU, 20 ALE, 21 TMP1, 22 TMP2, 23 TMP3, 24 TMP4, 25 PRS, 26 TAGCOUNT 27}; 28extern int32_t respSize[TAGCOUNT]; 29extern int32_t respData[TAGCOUNT]; 30struct RESP { 31 std::vector<int32_t> TCU; // 要素数:13 32 std::vector<int32_t> ALE; // 要素数:13 33 std::vector<int32_t> TMP1; // 要素数: 6 34 std::vector<int32_t> TMP2; // 要素数: 3 35 std::vector<int32_t> TMP3; // 要素数: 3 36 std::vector<int32_t> TMP4; // 要素数: 3 37 std::vector<int32_t> PRS; // 要素数: 2 38}; 39#endif 40EOF 41cat >resp.cpp <<EOF 42#include "resp.h" 43int32_t respSize[TAGCOUNT] = {13 ,13 ,6 ,3 ,3 ,3 ,2 }; 44int32_t respData[TAGCOUNT] = {5 ,3 ,4 ,35 ,4 ,2 ,1 }; 45EOF 46cat >gcc_gdb_ptype.sh <<EOF 47g++ -c -g -Wall resp.cpp -o resp.o 48g++ -c -g -Wall main.cpp -o main.o 49g++ -g -Wall main.cpp resp.o -o app 50gdb --quiet app >gdb_ptype_enum.txt <<EOF2 51ptype TAGCOUNT 52EOF2 53gdb --quiet app >gdb_ptype_resp.txt <<EOF2 54ptype RESP 55EOF2 56EOF 57cat >generate.py <<EOF 58import re, os 59def chop(s): 60 return s.rstrip(os.linesep) 61names = [] 62with open('gdb_ptype_enum.txt', 'rt', encoding='utf-8') as f: 63 next(f); 64 names = re.match('\\(gdb\\) type = enum : unsigned int \\{([^}]*)\\}\$', chop(next(f))).group(1).split(', ') 65print(f'names(読んだとき)={names}') 66if names[-1:][0] != 'TAGCOUNT': 67 raise 'enumの最後がTAGCOUNTじゃありません' 68names = names[:-1] 69print(f'names(最終)={names}') 70with open('gdb_ptype_resp.txt', 'rt', encoding='utf-8') as f: 71 next(f); 72 re.match('\\(gdb\\) type = struct RESP \\{\$', chop(next(f))).group(0) 73 members = [] 74 while True: 75 line = chop(next(f)) 76 if re.match('\\}\$', line): 77 break 78 m = re.match(' std::vector<int, std::allocator<int> > (\w+);\$', line) 79 if m: 80 members.append(m.group(1)) 81print(f'members={members}') 82if len(names) != len(members): 83 raise 'enumの数とRESPのメンバ数が合いません' 84for name in names: 85 if name not in members: 86 raise 'enumの名前に該当するRESPのメンバがありません' 87with open('hoge.h', 'wt', encoding='utf-8') as f: 88 print("""\ 89#ifndef __HOGE_H_ 90#define __HOGE_H_ 91#include "resp.h" 92inline std::vector<int32_t>& respMemberByIndex(RESP& resp, int index) { 93 static std::vector<int32_t> RESP::* const accessor[TAGCOUNT] = {""", end='', file=f) 94 print(', '.join([f'&RESP::{name}' for name in names]), end='', file=f) 95 print("""\ 96}; 97 return resp.*accessor[index]; 98} 99#endif 100""", file=f) 101EOF 102cat >main_new.cpp <<EOF 103#include "resp.h" 104#include "hoge.h" 105int main() { 106 RESP resp; 107 for(int i=0;i!=TAGCOUNT;++i)respMemberByIndex(resp, i).assign(respSize[i], respData[i]); 108 return 0; 109} 110EOF 111sh gcc_gdb_ptype.sh 112python3 generate.py 113g++ -g -Wall resp.o main_new.cpp -o app_new

投稿2023/03/17 21:00

dameo

総合スコア943

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

よくわからんけど,operator[] とかで vector への参照を返すヘルパでも作ればよいのでしょうか?

C++

1struct RESP 2{ //※質問と同じ個数分書くのは面倒だからここでは2個にした 3 std::vector<int32_t> V1; 4 std::vector<int32_t> V2; 5}; 6 7//ヘルパ 8class Helper 9{ 10private: 11 RESP &m_rResp; 12public: 13 Helper( RESP &resp ) : m_rResp(resp) {} 14 15 std::vector<int32_t> &operator[]( int index ) 16 { 17 switch( index ) 18 { 19 case 0: return m_rResp.V1; break; 20 case 1: return m_rResp.V2; break; 21 default: throw std::out_of_range( "ERR" ); break; 22 } 23 } 24}; 25 26int main() 27{ 28 RESP resp; 29 30 { 31 Helper H{ resp }; 32 33 const int32_t Size[2] = { 10, 3 }; 34 const int32_t Val[2] = { 4, 1 }; 35 for( int i=0; i<2; ++i ) //ループでどうの 36 { 37 H[i].assign( Size[i], Val[i] ); 38 } 39 } 40 41 return 0; 42};

投稿2023/03/16 10:30

編集2023/03/16 10:31
fana

総合スコア11658

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2023/03/16 10:41

例示の Helper の実装はてきとー. 例えば,各 vector を指すポインタの配列を持つとかすれば,switch よりはかっこいい(?)かな?
kosetei.dn

2023/03/16 14:23

フレームワーク的に自分で勝手にクラスを作っていいのか分からないんですが、明日同僚に聞いてOKだったら試してみます。ありがとうございます。
fana

2023/03/17 01:30 編集

質問へのコメント欄の方で,構造体のメンバが増えたり順序が入れ替わったりする的な話をしているけど, そんな(構造体の仕様自体がいつまでたっても確定しない)状態に関する話なのだとしたら,考えるだけ無駄な気もする. ほんとにそんなやり方をしているのであれば, 少なくとも【その構造体の定義を決めている者が】構造体の中身が相応に書き換わろうが利用側コードが地獄見ないようにするための何か を構造体と一緒に提供すべきであって,使う側(:あなたとか)がこのような工夫をひねり出すべきではないと思うよ. ある一瞬の時期だけはどうにか使えたとしても,構造体がまた変わったら全く通用しなくなるのだろうし,不毛というか,馬鹿馬鹿しい話というか…
fana

2023/03/17 01:35

例えば void DoSomething( struct RESP *p ) { /*この関数の責務を果たす実装が必要だが,RESPの定義が今後どうなるのかはさっぱりわからない*/ } みたいな状況なのだろうか? 個人的には想像つかないし,そんなの無理だろ,とすら思う. その状況に対処することを考えるよりも,その状況自体をどうにかすべきであるような…?
fana

2023/03/17 01:40

とりあえず C++ なのであれば「構造体」をやめるべき: 「構造体のメンバ変数を各所でダイレクトにいじくり回すっていうやり方」自体をやめるべき なのではなかろうか. 特にそんな状況なのであれば,RESPの内側の具体的な実装に依存しきったコードを量産すればそりゃ地獄が待ってるよなぁ…と.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問