Iterate over list of object instances and lookup by instance name?

I'm writing a program which will communicate IO values via JSON-RPC. I've got eight objects I instantiate. These objects are four different Classes, but all share a base Class BrokerData.

The first thing I would like to do is iterate over a list of these objects to access base class methods. For example, all the Objects have a getData() method which returns their current value. My searching seems to point to using a vector or list, which while not built into arduino c++ can be added(?).

The second thing I want to do is lookup objects by "name". I will be receiving JSON-RPC messages like these:

{
    "method" : "status",
    "params" : {
        "data" : ["Voltage", "Load Power","Charge Energy"],
        "style":"verbose"},
    "id" : 1
}

and

{
    "method" : "subscribe",
    "params" : {
        "data" : ["Voltage", "Charge Power", "LoadEnergy"],
        "max_update_ms" : 2000},
    "id" : 2
}

The parsing is handled by the aJSON library and all my objects have a getName() method which returns a matching char[] (e.g. "Voltage"). It looks like I should use a hashtable, which again is part of the std library.

While code examples would be great, I'd be happy with some general guidance on how to handle this. All my c++ experience is on arduino so I'm not really familiar with the std library. If you would like to see more of my code, I'd be happy to provide it.

Energy_monitor.ino (the first bit):

#include "ADS1115.h"
#include <aJSON.h>
#include <JsonRPCServer.h>
#include "broker_data.h"

#define ACS715_mV_per_A 133.0
#define V_DIV_LOW	 10000.0
#define V_DIV_HIGH  21000.0
#define ADC_CHANNEL_LOAD_CURRENT	0
#define ADC_CHANNEL_CHARGE_CURRENT	1
#define ADC_CHANNEL_VOLTAGE	2
#define ADC_CHANNEL_VCC	3
const uint8_t ADS1115_ADDRESS = 0x48;

ADS1115 ads(ADS1115_ADDRESS);
VoltageData v_batt("Voltage", ads, ADC_CHANNEL_VOLTAGE, V_DIV_LOW, V_DIV_HIGH);
VoltageData v_cc("Vcc", ads, ADC_CHANNEL_VCC, 0, 1);	// No voltage divider
CurrentData	current_l("Load_Current", ads, v_cc, ADC_CHANNEL_LOAD_CURRENT, ACS715_mV_per_A);
CurrentData	current_c("Charge_Current", ads, v_cc, ADC_CHANNEL_CHARGE_CURRENT, ACS715_mV_per_A);
PowerData	power_l("Load Power",current_l,v_batt);
PowerData	power_c("Charge Power",current_c,v_batt);
EnergyData	energy_l("Load Energy", power_l);
EnergyData	energy_c("Charge Energy",power_c);
TargetController jsonController(&Serial);
aJsonStream serial_stream(&Serial);

Here's broker_data.h:

#include "ADS1115.h"

#define BROKER_DATA_NAME_LENGTH 15
#define BROKER_DATA_UNIT_LENGTH 5
/*
	class BrokerData is an abstract class for all data objects
*/
class BrokerData {
public:
	BrokerData(char *name, char *unit) {
		strncpy(_data_name, name, BROKER_DATA_NAME_LENGTH);
		strncpy(_data_unit, unit, BROKER_DATA_UNIT_LENGTH);
		_subscription_rate = 0;
		_last_sample_time = NAN;
		_subscription_time = NAN;
		_data_value = NAN;
		_data_max = NAN;
		_data_min = NAN;
	};
	double	getValue() { return _data_value; }
	double	getMax() { return _data_max; }
	double	getMin() { return _data_min; }
	void	resetMin() { _data_min = NAN; }
	void	resetMax() { _data_max = NAN; }
	const char	*getName() { return _data_name; }
	const char	*getUnit() { return _data_unit; }
	void	subscribe(uint8_t sub_rate_s) { _subscription_rate = sub_rate_s;}
	void	unsubscribe() { _subscription_rate = 0; _subscription_time = NAN; }
	bool	isSubscribed() { return (bool)_subscription_rate; }
	bool	subscriptionDue();
	uint8_t		getSubscriptionRate() { return _subscription_rate; }
	void		setSubscriptionTime() { _subscription_time = millis(); } // Called when subscription is generated.
	uint32_t	getSampleTime() { return _last_sample_time; }
	virtual double getData() = 0;

protected:
	uint32_t	_getTimeDelta();
	uint8_t		_checkMinMax();
	double	_data_value;
	double	_data_max;
	double	_data_min;
private:
	char	_data_name[BROKER_DATA_NAME_LENGTH];
	char	_data_unit[BROKER_DATA_UNIT_LENGTH];
	uint8_t	_subscription_rate; // in seconds
	uint32_t	_subscription_time;	// Time of last subscription message
	uint32_t	_last_sample_time;	// Time of last sample
};

/*
class ADCData is an abstract intermediate class for all data objects which get their data from the ADS1115 ADC
*/
class ADCData : public BrokerData {
public:
	ADCData(char *name, char *unit, ADS1115 &ads, uint8_t ADCchannel) : BrokerData(name,unit) {
		_channel = ADCchannel; // should be 0-4
		_ads = &ads;
	}
	uint16_t getADCreading();
	uint8_t	getChannel() { return _channel; }
protected:
	ADS1115	*_ads;
private:
	uint8_t	_channel;
};

/*
class VoltageData is a class for all data objects which represent a voltage object
*/
class VoltageData : public ADCData {
public:
	VoltageData(char *name, ADS1115 &ads, uint8_t ADCchannel, uint32_t high_div, uint32_t low_div) : ADCData(name,"V",ads,ADCchannel) {
		_v_div = (high_div + low_div) / low_div;
	}
	double getData();
private:
	double	_v_div;
};
/*
class CurrentData is a class for all data objects which represent a current object
*/
class CurrentData: public ADCData {
public:
	CurrentData(char *name, ADS1115 &ads,VoltageData &vcc,uint8_t ADCchannel,double mV_per_A) : ADCData(name,"A",ads,ADCchannel) {
		_vcc = &vcc;
		_mV_per_A = mV_per_A;
	}
	double	getData();
private:
	double	_mV_per_A;
	VoltageData	*_vcc;
};

/*
class PowerData is a class for all data objects which represent a power object
*/
class PowerData : public BrokerData {
public:
	PowerData(char *name, CurrentData &current, VoltageData &voltage) : BrokerData(name,"W") {
		_voltage = &voltage;
		_current = &current;
	}
	double	getData();	// Calculates new Power based on most recent current and voltage
	void	resetData();
private:
	CurrentData	*_current;
	VoltageData	*_voltage;
};

/*
class EnergyData is a class for all data objects which represent an energy object
*/
class EnergyData : public BrokerData {
public:
	EnergyData(char *name, PowerData &power) : BrokerData(name,"Wh") {
		_power = &power;
}
	double getData(); // Calculates new Energy based on most recent power
	void	setData(double energy_value);	// Used for setting value, usually after reboot.
	void	resetData() { setData(0);}
private:
	PowerData	*_power;
};

My searching seems to point to using a vector or list, which while not built into arduino c++ can be added(?).

Or an array, if you know how many objects you want to store in the array.

The second thing I want to do is lookup objects by "name".

At run time, objects have addresses, not names. You can NOT look up the address based on the name that the compiler round-filed.

You can make each object store a name, and ask each object what it's name is, and determine if that is the name you are looking for.

It looks like I should use a hashtable, which again is part of the std library.

If you are planning to have thousands of objects, sure. For a very small number, which is all that the Arduino can manage, simply iterating over an array is far simpler.

The std library is NOT part of the Arduino's repertoire.

All my c++ experience is on arduino so I'm not really familiar with the std library.

Then why do you keep think that it will solve all your problems?

Thanks for your reply.

I only have the eight objects, so iterating for name lookup should be fine.

I got some of this working with:

#define BROKERDATA_OBJECTS 8

VoltageData v_batt("Voltage", ads, ADC_CHANNEL_VOLTAGE, V_DIV_LOW, V_DIV_HIGH);
VoltageData v_cc("Vcc", ads, ADC_CHANNEL_VCC, 0, 1); // No voltage divider
CurrentData current_l("Load_Current", ads, v_cc, ADC_CHANNEL_LOAD_CURRENT, ACS715_mV_per_A);
CurrentData current_c("Charge_Current", ads, v_cc, ADC_CHANNEL_CHARGE_CURRENT, ACS715_mV_per_A);
PowerData power_l("Load_Power",current_l,v_batt);
PowerData power_c("Charge_Power",current_c,v_batt);
EnergyData energy_l("Load_Energy", power_l);
EnergyData energy_c("Charge_Energy",power_c);

BrokerData *brokerobjs[BROKERDATA_OBJECTS];

void setup() {
	brokerobjs[0] = &v_batt;
	brokerobjs[1] = &v_cc;
	brokerobjs[2] = &current_l;
	brokerobjs[3] = &current_c;
	brokerobjs[4] = &power_l;
	brokerobjs[5] = &power_c;
	brokerobjs[6] = &energy_l;
	brokerobjs[7] = &energy_c;

}
void loop() {
	for (uint8_t obj_no = 0; obj_no < BROKERDATA_OBJECTS; obj_no++) {
		Serial.print(brokerobjs[obj_no]->getName());
		Serial.print(" = ");
		Serial.print(brokerobjs[obj_no]->getValue());
		Serial.print(" ");
		Serial.print(brokerobjs[obj_no]->getUnit());
		Serial.println();
	}
}

results in

Vcc = 4.56 V
Load_Current = 3.44 A
Charge_Current = 3.44 A
Load_Power = 0.00 W
Charge_Power = 0.00 W
Load_Energy = 0.00 Wh
Charge_Energy = nan Wh
S-Voltage = 0.00 V

I'm still not exactly sure how an array of the base class works when the pointers point to Classes which only inherit from the base class. Perhaps it's just compiler magic.

PaulS:
Or an array, if you know how many objects you want to store in the array.
At run time, objects have addresses, not names. You can NOT look up the address based on the name that the compiler round-filed.

You can make each object store a name, and ask each object what it's name is, and determine if that is the name you are looking for.
If you are planning to have thousands of objects, sure. For a very small number, which is all that the Arduino can manage, simply iterating over an array is far simpler.

The std library is NOT part of the Arduino's repertoire.
Then why do you keep think that it will solve all your problems?

What pointed me to the std library is that anytime I searched for C++ and my issue, the answer invariably was something like "You should use std::vector". Obviously, we are a bit more constrained on an arduino.