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

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

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

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

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

解決済

3回答

2180閲覧

クラスのメンバ変数に対する配列アクセス

YDTM5

総合スコア5

C++

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

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

0クリップ

投稿2021/05/04 14:35

編集2021/05/09 14:18

前提・実現したいこと

Arduino IDEのプログラミングについて

自身でTESTというクラスを作成したのですが、クラスの特定の変数に対して配列のようにアクセスしたいと思っています。
Test_Data型として
・Height;
・Weight;
・Length;
・Position;
の4つがある場合、値を代入するときは個々の変数名を使用したいのですが、シリアルモニタ等データを出力する際は配列を参照する様に
処理できればなと思っています。

プログラミングの経験が浅く、検索するにもやりたい事に対するキーワードが分からず質問させていただきました。
上記の方法を実現する方法がありましたらご教示いただけると幸いです。

【TEST.h】

c

1#ifndef TEST_H 2#define TEST_H 3#include "arduino.h" 4 5class TEST{ 6 public: 7 struct Test_Data{ 8 int32_t Val; 9 uint16_t Offset; 10 String Name; 11 }; 12 13 //この変数に配列の様にアクセスしたい 例)TestAry[2].Valと記述するとLength.Valにアクセスできる等 14 Test_Data Height; //[0] 15 Test_Data Weight; //[1] 16 Test_Data Length; //[2] 17 Test_Data Position; //[3] 18 19 bool Test_A; 20 bool Test_B; 21 bool Test_C; 22 23 void Ini(); 24 void GetData(); 25 void DataConvert(Test_Data & Data); 26 27 private: 28}; 29#endif

【Main.ino】

c

1#include "TEST.h" 2TEST test; 3 4uint32_t LastTime; 5 6void setup(){ 7 Serial.begin(9600); 8 9 pinMode(0, INPUT_PULLUP); 10 pinMode(1, INPUT_PULLUP); 11 pinMode(2, INPUT_PULLUP); 12 pinMode(3, INPUT_PULLUP); 13 14 test.Ini(); 15 16 LastTime = millis(); 17} 18 19void loop(){ 20 if(digitalRead(0)){ 21 test.Height.Val= analogRead(4); 22 } 23 24 if(digitalRead(1)){ 25 test.Weight.Val= analogRead(5); 26 } 27 28 if(digitalRead(2)){ 29 test.Length.Val= analogRead(6); 30 } 31 32 if(digitalRead(3)){ 33 test.Position.Val= analogRead(7); 34 } 35 36 if(millis() - LastTime >= 1000){ 37 Serial.print(test.Height.Val); 38 Serial.print(", "); 39 Serial.print(test.Weight.Val); 40 Serial.print(", "); 41 Serial.print(test.Length.Val); 42 Serial.print(", "); 43 Serial.print(test.Position.Val); 44 45// 上記をできれば配列の様にForで回したい 46// for (uint8_t i = 0; i <= 3; i++){ 47// Serial.print(test.DataAry[i].Val); 48// Serial.print(", "); 49// } 50 51 Serial.println(); 52 53 LastTime = millis(); 54 } 55}

補足情報

・Test_Data型の変数は本来100個ほどあるのですが、見づらくなってしまうので例として4個で記述しました

###回答を頂いた例を参考にして
ポインタ・列挙体・オペレータを使用する方法を試しましたが、全てやりたかったことが実現できました。
が、やりたいことは出来たのですが全ての方法で微妙に差があるためどれが適した方法なのか悩ましいです。
個人的にはオペレータを使用した方法がクラスの使用時にシンプルかと思いましたが、メリット・デメリットありますでしょうか?

【TEST.h】

c

1#ifndef TEST_H 2#define TEST_H 3#include "arduino.h" 4 5class TEST { 6 public: 7 struct Test_Data { 8 int32_t Val; 9 uint16_t Offset; 10 String Name; 11 }; 12 13 //この変数に配列の様にアクセスしたい 例)TestAry[2].Valと記述するとLength.Valにアクセスできる等 14 Test_Data Height; //[0] 15 Test_Data Weight; //[1] 16 Test_Data Length; //[2] 17 Test_Data Position; //[3] 18 19 //ポインタ////////////////////////////////////////////////// 20 Test_Data *DataAry_P[4] = { 21 &Height, 22 &Weight, 23 &Length, 24 &Position 25 }; 26 27 //列挙体////////////////////////////////////////////////// 28 enum TestIndex { 29 H_Index, 30 W_Index, 31 L_Index, 32 P_Index 33 }; 34 35 Test_Data DataAry_E[sizeof(TestIndex)]; 36 37 Test_Data &H(){return DataAry_E[H_Index];} 38 Test_Data &W(){return DataAry_E[W_Index];} 39 Test_Data &L(){return DataAry_E[L_Index];} 40 Test_Data &P(){return DataAry_E[P_Index];} 41 42 //オペレータ////////////////////////////////////////////////// 43 Test_Data& operator[] (uint8_t n) { 44 switch (n) { 45 case 0: return Height; 46 case 1: return Weight; 47 case 2: return Length; 48 case 3: return Position; 49 } 50 }; 51 52 bool Test_A; 53 bool Test_B; 54 bool Test_C; 55 56 57 private: 58}; 59#endif

【Main.ino】

c

1#include "TEST.h" 2TEST test; 3 4uint32_t LastTime; 5uint32_t Cnt; 6 7void setup() { 8 Serial.begin(9600); 9 10 LastTime = millis(); 11} 12 13void loop() { 14 if (millis() - LastTime >= 1000) { 15 LastTime = millis(); 16 Serial.println(Cnt); 17 18 //オペレータ////////////////////////////////////////////////// 19 test.Height.Val = 1; 20 test.Weight.Val = 2; 21 test.Length.Val = 3; 22 test.Position.Val = 4; 23 Serial.print("Operator:"); 24 for (uint8_t i = 0; i <= 3; i++) { 25 Serial.print(test[i].Val); 26 Serial.print(", "); 27 } 28 Serial.println(); 29 30 //列挙体////////////////////////////////////////////////// 31 test.H().Val = 100; 32 test.W().Val = 200; 33 test.L().Val = 300; 34 test.P().Val = 400; 35 Serial.print("Enumerate:"); 36 for (uint8_t i = 0; i <= 3; i++) { 37 Serial.print(test.DataAry_E[i].Val); 38 Serial.print(", "); 39 } 40 Serial.println(); 41 42 //ポインタ////////////////////////////////////////////////// 43 test.DataAry_P[0]->Val = 1000; 44 (*test.DataAry_P[1]).Val = 2000; 45 test.DataAry_P[2]->Val = 3000; 46 test.DataAry_P[3]->Val = 4000; 47 Serial.print("Pointer:"); 48 for (uint8_t i = 0; i <= 3; i++) { 49 Serial.print(test.DataAry_P[i]->Val); 50 Serial.print(", "); 51 } 52 Serial.println(); 53 54 Serial.println(); 55 Cnt++; 56 } 57}

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

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

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

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

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

dodox86

2021/05/04 15:59

Arduinoのコンパイラはまぁ、ほとんどC++であるので、本質問についてはArduinoのタグと共にC++のタグを付けておくと、より有識者の方の目に留まり易くなると思います。
YDTM5

2021/05/05 07:44

ご指南ありがとうございます
guest

回答3

0

逆転の発想として、データを配列に持って
それぞれのメンバ変数をプロパティとして実装する方法があります。

c++

1#include <iostream> 2#include <cstdint> 3#include <string> 4 5using String = std::string; 6 7class TEST{ 8 public: 9 struct Test_Data{ 10 int32_t Val; 11 uint16_t Offset; 12 String Name; 13 }; 14 enum TestIndex { 15 HeightIndex, 16 WeightIndex, 17 LengthIndex, 18 PositionIndex, 19 TestAryMAX 20 }; 21 Test_Data TestAry[TestAryMAX]; 22 Test_Data& Height() { return TestAry[HeightIndex]; } 23 Test_Data& Weight() { return TestAry[WeightIndex]; } 24 Test_Data& Length() { return TestAry[LengthIndex]; } 25 Test_Data& Position() { return TestAry[PositionIndex]; } 26 private: 27}; 28 29int main(){ 30 TEST t; 31 t.Height().Val = 100; // Heightに()が必要になる 32 std::cout << t.TestAry[TEST::HeightIndex].Val << std::endl; 33}

投稿2021/05/05 11:18

編集2021/05/05 13:04
asm

総合スコア15149

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

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

YDTM5

2021/05/09 14:07

ご回答ありがとうございます。 こちらもでもやりたいことができました。 ()をが必要になるのを除けばシンプルかもしれません。
guest

0

ベストアンサー

operator [] を下記のように定義すればできるかと思います。

C++

1Test_Data& operator[]( uint32_t n ){ 2 3 switch ( n ) { 4 case 0: return Height; 5 case 1: return Weight; 6 case 2: return Length; 7 case 3: return Position; 8 default:throw std::out_of_range( "error message" ); 9 } 10}

追記

C++

1class TEST { 2class TESTARY { 3public: 4 TESTARY(TEST& t):test{t}{} 5private: 6 TEST& test; 7public: 8 Test_Data& operater[](uint_8 n){ 9 switch(n){ 10 case 0: return test.Height; 11 } 12 } 13} TestAry{*this}; 14};

投稿2021/05/05 08:25

編集2021/05/10 10:50
Serbonis

総合スコア586

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

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

YDTM5

2021/05/09 14:04

ご回答ありがとうございます。 やりたかった事に一番近いかもしれません。 メンバにアクセスする際にtest.Height.Valと記述するところ、 test[0].Valと記述すればアクセスできてしまうのが不思議な感覚でした。 (test.○○○[0].Valの様に別のメンバが出来るのかと思いました)
Serbonis

2021/05/10 10:54

オペレータのメリットはメンバ変数を追加しない(クラスのサイズが変わらない)で実現できるところです。デメリットは使用時の見た目に違和感があることかと思います。メンバが増えますが、test.TestAry[0].Valでアクセスできる方法を追記しました。
guest

0

自身でTESTというクラスを作成したのですが、クラスの特定の変数に対して配列のようにアクセスしたいと思っています。

一例ですが、こんなのはいかがでしょう。配列のようにアクセスしたいメンバー変数をポインタ配列にしてアクセスします。下記サンプルコードのmain部分のコメントで示したところをご覧ください。

C++

1#include <iostream> 2#include <string> 3typedef int int32_t; 4typedef unsigned short uint16_t; 5typedef std::string String; 6 7class TEST{ 8 public: 9 struct Test_Data{ 10 int32_t Val; 11 uint16_t Offset; 12 String Name; 13 }; 14 15 //この変数に配列の様にアクセスしたい 例)TestAry[2].Valと記述するとLength.Valにアクセスできる等 16 Test_Data Height; //[0] 17 Test_Data Weight; //[1] 18 Test_Data Length; //[2] 19 Test_Data Position; //[3] 20}; 21 22int main() { 23 TEST obj; 24 obj.Height.Val = 1; 25 obj.Weight.Val = 2; 26 obj.Length.Val = 3; 27 obj.Position.Val = 4; 28 29 // Height, Weight, Length, Posion の各メンバー変数をポインタ配列にする。 30 TEST::Test_Data* dataArray[4] = { &obj.Height, &obj.Weight, &obj.Length, &obj.Position }; 31 // ループでアクセス 32 for (int i = 0; i < 4; i++) { 33 std::cout << dataArray[i]->Val << std::endl; 34 } 35 36 return 0; 37}

で、これを実行すると以下のように出力されます。コードは普通のC++ですが、該当部分はArduinoのコンパイラでも使えるはずです。

terminal

1bash-3.2$ g++ -Wall t2.cpp 2bash-3.2$ ./a.out 31 42 53 64

追記しました:
Arduino IDE(1.8.13)でコンパイルできるサンプルコードを以下に示しておきます。

C++

1// arduino 2#include "arduino.h" 3 4class TEST{ 5 public: 6 struct Test_Data{ 7 int32_t Val; 8 uint16_t Offset; 9 String Name; 10 }; 11 12 //この変数に配列の様にアクセスしたい 例)TestAry[2].Valと記述するとLength.Valにアクセスできる等 13 Test_Data Height; //[0] 14 Test_Data Weight; //[1] 15 Test_Data Length; //[2] 16 Test_Data Position; //[3] 17 18 // ポインタ配列でアクセスするメンバー関数 19 void example1(); 20 21private: 22}; 23 24void TEST::example1() { 25 TEST::Test_Data* dataArray[] = {&Height, &Weight, &Length, &Position}; 26 const int NUM = sizeof(dataArray) / sizeof(dataArray[0]); // (NUM==4) 27 for (int i = 0; i < NUM; i++) { 28 // 何かする 29 dataArray[i]->Val; 30 dataArray[i]->Offset; 31 dataArray[i]->Name; 32 } 33} 34 35void setup() { 36 TEST obj; 37 38 obj.Height.Val = 1; 39 obj.Weight.Val = 2; 40 obj.Length.Val = 3; 41 obj.Position.Val = 4; 42 43 // Height, Weight, Length, Posion の各メンバー変数をポインタ配列にする。 44 TEST::Test_Data* dataArray[4] = { &obj.Height, &obj.Weight, &obj.Length, &obj.Position }; 45 // ループでアクセス 46 for (int i = 0; i < 4; i++) { 47 dataArray[i]->Val; // 何かする 48 } 49 50 // メンバー関数でアクセス 51 obj.example1(); 52} 53 54void loop() { 55 // put your main code here, to run repeatedly: 56}

投稿2021/05/04 15:46

編集2021/05/05 08:51
dodox86

総合スコア9256

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

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

dodox86

2021/05/04 16:08

メンバー変数が構造体なのであれば、もともと配列にしておいた方がよりシンプルだとは思います。配列の添字はenumや#defineのマクロで指定した値にするとか。
YDTM5

2021/05/05 07:23

ご回答ありがとうございます。 ポインタというものがあるのですね、勉強になります。 dodox86様に紹介して頂いたコードではArudino IDEでの開発環境ではコンパイルできませんでしたが、頂いたコードを元に調べながら修正してみます。
dodox86

2021/05/05 08:55

> Arudino IDEでの開発環境ではコンパイルできませんでしたが、頂 追記しておきましたのでご覧ください。 ただ、「ポインタ」を知らないとなるとちょっと難しいかもしれません。C言語やC++ならいずれ出てくるでしょうし、ライブラリを使う段になると避けて通れなかったりするのでは。Arduinoで小さいプログラムだったらポインタなど使わなくてもプログラムを組めるでしょうが、参照(&を付けるやつ)との区別もついてないとトラブルのもとかもしれませんし。
YDTM5

2021/05/09 13:57

ご回答ありがとうございます。 確認遅くなってしまい申し訳ありません。 やりたかった動作は行えました。 ポインタについてやんわり調べましたが、メンバにアクセスするためにアロー演算子というものが必要になってくるのですね。 ->の記述に慣れるまで違和感がありそうです。。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問