IF statement based on data type?? Is there such a thing?

Short story is in the title.

Is it possible to write and if statement to check a data type?

example:
rtc is declared as type RTC_PCF8523 or RTC_DS1307

then in the program

#IFDEF rtc as type RTC_PCF8523 then do this
#IFDEF rtc as type RTC_DS1307 then do that

If you have access to stl libraries, you can do this:

RTC_PCF8523 obj;

int result = std::is_same<decltype(obj), RTC_PCF8523>::value;
//result == 0 if not same type
//               1 is same type

#ifdef is a compile-time construct. Do you need this to work at compile time, or run time? Big difference. Either way, it is entirely possible, depending on exactly what you're trying to accomplish.

Regards,
Ray L.

RayLivingston:
#ifdef is a compile-time construct. Do you need this to work at compile time, or run time? Big difference. Either way, it is entirely possible, depending on exactly what you're trying to accomplish.

Regards,
Ray L.

Hmm.

I'm trying to accomplish making an if statement that is based on the data type of declared a variable.

The #IFDEF was just pseudo code, used to exaggerate the fact that I would prefer compile-time over run time as the data type won't change, can't change after download.

#define THING1  // Uncomment to use a Type 1 Thing
// #define THING2  // Uncomment to use a Type 2 Thing

#if defined(THING1)
    #include "Type1Thing.h"
    THING1 myThing = Type1Thing();
#elif defined(THING2)
    #include "Type2Thing.h"
    THING2 myThing = Type2Thing();
#endif

Regards,
Ray L.

RayLivingston:

#define THING1  // Uncomment to use a Type 1 Thing

// #define THING2  // Uncomment to use a Type 2 Thing

#if defined(THING1)
    #include "Type1Thing.h"
    THING1 myThing = Type1Thing();
#elif defined(THING2)
    #include "Type2Thing.h"
    THING2 myThing = Type2Thing();
#endif




Regards,
Ray L.

Good example on #IFDEF, but I don't see any check on the data type. Per the OP, I don't have THING1 and THING2. I have rtc and is either of type RTC_PCF8523 or type RTC_DS1307. rtc is always defined.

If you don't have access to stl, you can do something (via template specialization) like this:

template<typename T> struct CHECK_CLASS
{
    CHECK_CLASS() : value(0) {}
    uint8_t value;
};

template<> struct CHECK_CLASS<RTC_PCF8523>
{
    CHECK_CLASS() : value(1) {}
    uint8_t value;
};

template<> struct CHECK_CLASS<RTC_DS1307>
{
    CHECK_CLASS() : value(2) {}
    uint8_t value;
};


void setup()
{
   RTC_PCF8523 rtc8523;
   if (CHECK_CLASS<decltype(rtc8523)>().value == 1) 
   {
      //object is of type RTC_PCF8523
   } else if (CHECK_CLASS<decltype(rtc8523)>().value == 2)
   {
      //object is of type RTC_DS1307
   } else {
      //unknown type
   }
}

adwsystems:
Good example on #IFDEF, but I don't see any check on the data type. Per the OP, I don't have THING1 and THING2. I have rtc and is either of type RTC_PCF8523 or type RTC_DS1307. rtc is always defined.

From the class I know (are you using adafruit RTClib?) besides the "now()" method and a few other, the classes do not expose the exact same interface. so unless you use ONLY those API they have in common, the code would not be independent of the RTC.

Are you writing a new library or extending Adafruit's ? you could always add a new method that would return the RTC type a code number. that method would exist in all the classes (virtual in a base class, implemented in all subclasses for example, or just have that method in each class) and thus in your code you could do

switch(rtc.type()) {
  case PCF8523_TYPE:
    ...
    break;
  case DS1307_TYPE:
    ...
    break;
  case DS3231_TYPE:
    ...
    break;
}

where the XXXX_TYPE values would be defined in your library.

J-M-L:
From the class I know (are you using adafruit RTClib?) besides the "now()" method and a few other, the classes do not expose the exact same interface. so unless you use ONLY those API they have in common, the code would not be independent of the RTC.

Are you writing a new library or extending Adafruit's ? you could always add a new method that would return the RTC type a code number. that method would exist in all the classes (virtual in a base class, implemented in all subclasses for example, or just have that method in each class) and thus in your code you could do

switch(rtc.type()) {

case PCF8523_TYPE:
    ...
    break;
  case DS1307_TYPE:
    ...
    break;
  case DS3231_TYPE:
    ...
    break;
}



where the XXXX_TYPE values would be defined in your library.

Good guess. I'm using Adafruit's library. Not interesting in modifying it and having to keep modifying it as they update it. You are correct they don't expose the same interface and therein lies the point of the question. Depending on the chip they have different functions to do the same thing. That is the point of the post. To be able to write one set of code and switch between the chips by merely changing the initial definition.

adwsystems:
Good example on #IFDEF, but I don't see any check on the data type. Per the OP, I don't have THING1 and THING2. I have rtc and is either of type RTC_PCF8523 or type RTC_DS1307. rtc is always defined.

I would think a type check is unnecessary, since the type is both known and enforced by the #ifdef. You SAID "I would prefer compile-time over run time as the data type won't change, can't change after download". So, make up your mind. If it can only be compiled with one type, why on earth do you need to check the type at run-time? If they are different types, they CANNOT be used inter-changeably - there HAS to be unique code to access each type, which means you HAVE to use #ifdef to include only the necessary code.

What you've given us so far makes no sense, and I'm not a big fan of 20 questions...

Regards,
Ray L.

adwsystems:
Depending on the chip they have different functions to do the same thing.

Can you give an example ?

it looks likee where the function is the same, it seems they have the same method name. (begin, adjust, now). Other stuff is hardware related and thus not common?

RTC_PCF8523
boolean begin(void);
void adjust(const DateTime& dt);
boolean initialized(void);
static DateTime now();

Pcf8523SqwPinMode readSqwPinMode();
void writeSqwPinMode(Pcf8523SqwPinMode mode);
void calibrate(Pcf8523OffsetMode mode, int8_t offset);

RTC_DS3231
boolean begin(void);
static void adjust(const DateTime& dt);
bool lostPower(void);
static DateTime now();
static Ds3231SqwPinMode readSqwPinMode();
static void writeSqwPinMode(Ds3231SqwPinMode mode);
static float getTemperature(); // in Celcius degree

RTC_DS1307
boolean begin(void);
static void adjust(const DateTime& dt);
uint8_t isrunning(void);
static DateTime now();
static Ds1307SqwPinMode readSqwPinMode();
static void writeSqwPinMode(Ds1307SqwPinMode mode);
uint8_t readnvram(uint8_t address);
void readnvram(uint8_t* buf, uint8_t size, uint8_t address);
void writenvram(uint8_t address, uint8_t data);
void writenvram(uint8_t address, uint8_t* buf, uint8_t size);

RTC_PCF8523
boolean begin(void);
void adjust(const DateTime& dt);
boolean initialized(void);
static DateTime now();

Pcf8523SqwPinMode readSqwPinMode();
void writeSqwPinMode(Pcf8523SqwPinMode mode);
void calibrate(Pcf8523OffsetMode mode, int8_t offset);

RTC_DS1307
boolean begin(void);
static void adjust(const DateTime& dt);
uint8_t isrunning(void);
static DateTime now();
static Ds1307SqwPinMode readSqwPinMode();
static void writeSqwPinMode(Ds1307SqwPinMode mode);
uint8_t readnvram(uint8_t address);
void readnvram(uint8_t* buf, uint8_t size, uint8_t address);
void writenvram(uint8_t address, uint8_t data);
void writenvram(uint8_t address, uint8_t* buf, uint8_t size);

J-M-L:
Can you give an example ?

listed above.

J-M-L:
it looks likes where the function is the same, it seems they have the same method name. (begin, adjust, now). Other stuff is hardware related and thus not common?

Not all and that is the source of the post.

I don't get what you say.. what highlight ?

calibration or reading nvram is not common across all hardware

RayLivingston:
I would think a type check is unnecessary, since the type is both known and enforced by the #ifdef. You SAID "I would prefer compile-time over run time as the data type won't change, can't change after download". So, make up your mind. If it can only be compiled with one type, why on earth do you need to check the type at run-time? If they are different types, they CANNOT be used inter-changeably - there HAS to be unique code to access each type, which means you HAVE to use #ifdef to include only the necessary code.

What you've given us so far makes no sense, and I'm not a big fan of 20 questions...

Regards,
Ray L.

You are using the pseudo code too literally. I don't have a #define statement. I have a declaration. Based on that declaration, and the data type of that declaration I am looking for a compile time method to select which functions are included in the downloaded program.

adwsystems:
You are using the pseudo code too literally. I don't have a #define statement. I have a declaration. Based on that declaration, and the data type of that declaration I am looking for a compile time method to select which functions are included in the downloaded program.

I don't get it either... if you don't write a library, your code does have the type of the rtc hardcoded... so you know what it is... and if you are writing a library that expect an Adafruit RTC object, then provide a method with different signature based on the possible types

J-M-L:
I don't get it either... if you don't write a library, your code does have the type of the rtc hardcoded... so you know what it is...

Exactly. I need the program to compile based on what the variable is declared as to include the correct functions and omit including the unused ones for the other chip. I don't want to have multiple versions of the program just because some have PCF8523 board and other the DS1307 board when that is the only difference between them. So back to the original question.

rtc will be declared as type RTC_PCF8523 or RTC_DS1307

then in the program

#IF rtc declared as type RTC_PCF8523 then do this
#IF rtc declared as type RTC_DS1307 then do that

(this is pseudo code to show what I am trying to accomplish, this is not the exact or correct way to do it)

To put it another way, how do I only include function calls in a program based on the data type of a variable?

adwsystems:
I don't want to have multiple versions of the program just because some have PCF8523 board and other the DS1307 board when that is the only difference between them.

as you write the code, you can include your own abstractions/construct.. so either you go with the template stuff above or you define something you can use.... I would go like this

#include <RTClib.h>

/* uncomment the line with your RTC type */
#define USING_RTC_PCF8523
//#define USING_RTC_DS1307
//#define USING_RTC_DS3231


#if defined(USING_RTC_PCF8523)
RTC_PCF8523 rtc;
#elif defined(USING_RTC_DS3231)
RTC_DS3231 rtc;
#elif defined(USING_RTC_DS1307)
RTC_DS1307 rtc;
#endif


void setup() {
  Serial.begin(115200);
  
#if defined(USING_RTC_PCF8523)
  Serial.println("USING_RTC_PCF8523");
#elif defined(USING_RTC_DS3231)
  Serial.println("USING_RTC_DS3231");
#elif defined(USING_RTC_DS1307)
  Serial.println("USING_RTC_DS1307");
#endif

}

void loop() {}

Your thinking is backwards. Instead of trying to detect the variable's type, define a flag whose value SETS you its data type and is available for testing later. The code below uses different integer types rather than those RTC libraries that I don't have installed. But, the concept is the same.

#define SHORT 1
#define LONG 2

#define VARIABLE_TYPE SHORT
//#define VARIABLE_TYPE LONG

#if VARIABLE_TYPE == SHORT
uint8_t var;

#elif VARIABLE_TYPE == LONG
uint32_t var;

#else
#error illegal type
#endif

void setup() {
#if VARIABLE_TYPE == SHORT
// Do stuff
// Do more stuff

#elif VARIABLE_TYPE == LONG
// Do different stuff
// Do more different stuff
#endif

}

void loop() {}

EDIT:
I see @J-M-L had the same idea.

gfvalvo:
Your thinking is backwards. Instead of trying to detect the variable's type, define a flag whose value SETS you its data type and is available for testing later. The code below uses different integer types rather than those RTC libraries that I don't have installed. But, the concept is the same.

I wouldn't say backwards. I was aiming for a more direct solution. To use the data type that was already there opposed to adding another item. But to add that item in such a way that is also selects the variable declaration definitely balances the pros with the cons. A 2-for-1 but have to add 1 to get the 2. Interesting.

Bottom line is there is no low hanging fruit there (endless debates around typeof, decltype etc)

You want to get rid of unneeded code at run time, and whilst you could cont on the optimizer to do this, the traditional way of doing this is conditional compiling through #if or #ifdef etc

That’s the approach I would take.

Side note - I would never buy a ds1307 it’s obsolete and not a stable clock nor is it financially more interesting than a ds3132 so why bother coding for this one :slight_smile: