[SOLVED] How to write a function that takes various data types

On an RX/gateway node, I currently use four different MQTT .publish functions for four different topics, the data TX'ed as a struct from a remote node.

struct structname
{
[color=green]  bool [color=blue]switch[/color]; // -> cast into long
  byte [color=blue]angle[/color]; // -> cast into long
  int [color=blue]frequency[/color]; // -> cast into long
  float [color=blue]temperature[/color]; -> keep as float[/color]
} structvariable;

How to write a single function that allows passing topic and struct member to it, as well as which type casting is needed?

void publishData(unsigned long now, const int interval, [color=red]? topic[/color], [color=blue]? structmember[/color])
{
  if (millis() - now >= interval)
  {
    now = millis();

    MQTT_connect();

    [color=red]topic[/color].publish(([color=green]long or float[/color])structvariable.[color=blue]structmember[/color]);
  }
}

Is that at all possible/feasible, or should I better keep the four different functions?

template<typename T>
void publishData(unsigned long now, const int interval, MQTT& topic, T structmember)
{
  if (millis() - now >= interval)
  {
    now = millis();

    MQTT_connect();

    topic.publish(structmember);
  }
}

What type of object is 'topic'?

You can overload functions if they take different arguments:

void publishData(unsigned long now, const int interval, ? topic, float floatValue)
{
  if (millis() - now >= interval)
  {
    now = millis();

    MQTT_connect();

    topic.publish(floatValue);
  }
}

code void publishData(unsigned long now, const int interval, ? topic, long longValue)
{
  if (millis() - now >= interval)
  {
    now = millis();

    MQTT_connect();

    topic.publish(longValue);
  }
}

The compiler finds the function that matches the argument you pass. You can pass 'bool', 'byte', and 'int' to the 'long' version and they will get promoted in the passing.

Thanks arduino_new & johnwasser; “topic” is an object from Adafruit’s MQTT library to publish data:

Adafruit_MQTT_Publish [color=red]topic[/color] = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/topicname");

So, with the before

struct structname
{
  [color=blue]bool[/color] switch; // -> cast into long
  [color=blue]byte[/color] angle; // -> cast into long
  [color=blue]int[/color] frequency; // -> cast into long
  [color=blue]float[/color] temperature; -> keep as float
} structvariable;

I have this function

void publishData(unsigned long now, const int interval, Adafruit_MQTT_Publish &topic, [color=blue]bool &structmember[/color])
{
  if (millis() - now >= interval)
  {
    now = millis();

    MQTT_connect();

    topic.publish([color=blue](long) structmember[/color]);
  }
}

publishData(0, 2000, topic, structvariable.switch);

which compiles, but I need an identifier (bool, byte, int or float) in the function argument (there are four different data types in the struct), and I am casting the structmember into a long, which only works for the bool, byte and int struct members.

It seems that I’m close yet far.

Note: ‘switch’ is a reserved word. You can’t use it as an identifier.
Note: You are passing ‘now’ by value so “now = millis();” in your function does not change the value of the passed variable.

This version compiles without error. The compiler (for reasons) can’t decide if ‘bool’, ‘byte’, or ‘int’ should be promoted to ‘long’ or ‘float’ so the integer types have to be cast to ‘long’ when passed.

#include <Adafruit_MQTT.h>
#include <Adafruit_MQTT_Client.h>
#include <Ethernet.h>
#include <EthernetClient.h>


//Set up the ethernet client
EthernetClient client;

#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  1883
#define AIO_USERNAME    "...your AIO username (see https://accounts.adafruit.com)..."
#define AIO_KEY         "...your AIO key..."


Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

struct structname
{
  bool on_off_switch; // -> cast into long
  byte angle; // -> cast into long
  int frequency; // -> cast into long
  float temperature; // -> keep as float
} structvariable;


void MQTT_connect() {}


void publishData(unsigned long &now, const unsigned interval, Adafruit_MQTT_Publish &topic, const long structmember)
{
  if (millis() - now >= interval)
  {
    now = millis();


    MQTT_connect();


    topic.publish(structmember);
  }
}


void publishData(unsigned long &now, const unsigned interval, Adafruit_MQTT_Publish &topic, const float structmember)
{
  if (millis() - now >= interval)
  {
    now = millis();


    MQTT_connect();


    topic.publish(structmember);
  }
}


void setup()
{
  // put your setup code here, to run once:
}


void loop()
{
  Adafruit_MQTT_Publish topic = Adafruit_MQTT_Publish(&mqtt,  AIO_USERNAME "/feeds/photocell");
  unsigned long time = millis();
  
  // put your main code here, to run repeatedly:
  publishData(time, 2000, topic, (long) structvariable.on_off_switch);
  publishData(time, 2000, topic, (long) structvariable.angle);
  publishData(time, 2000, topic, (long) structvariable.frequency);
  publishData(time, 2000, topic, structvariable.temperature);
}

Thanks, I see… so you say to simply cast to long in the function call, not in the function… and reference the memory address of now. I did not think of that at all; much appreciated, solved!

Lagom:
Thanks, I see… so you say to simply cast to long in the function call, not in the function… and reference the memory address of now. I did not think of that at all; much appreciated, solved!

I would suggest to avoid this approach and use the template one as it provides cleaner code and eliminates any type pruning error.

template<typename T> void publishData(unsigned long now, const int interval, Adafruit_MQTT_Publish &topic, T data)
{
  if (millis() - now >= interval)
  {
    now = millis();

    MQTT_connect();

    topic.publish(data);
  }
}

void setup()
{
   publishData(0, 2000, topic, structvariable.switch);
}

Ok, thanks; still need to reference the memory location of now (as johnwasser explained) and cast data to long in the function call - so what would be the advantage of a template function? What do you mean by it being “cleaner”? I have not used templates before.

template<typename T> void publishData(unsigned long &now, const int interval, Adafruit_MQTT_Publish &topic, T data)
{
  if (millis() - now >= interval)
  {
    now = millis();
    MQTT_connect();
    topic.publish(data);
  }
}

publishData(nowFeed3, intervalFeed3, feed3, (long) nodeTX.feed3);