Trying to create a small abstraction library

so i'm new to C++ , coming from java, so i'm trying to create a small library to deal with adafruit RTCLib, so i figured i would create a small class set to abstract away the implementiohn
my problem is that i'm probably missing something basic here, cause that doesnt' seem to be possible , i mean the general rule is that caller must also use new and use delete.
but the caller should not now what implementation class to use but just the interface.
this is true for both get method and the toString method which needs to have a buffer always available to it.

class IDateTimeValue
{
  public:
    virtual char * toString(){};
};

class DateTimeValue : public IDateTimeValue
{
public:
  DateTimeValue(DateTime dt)
  {
    _dt = dt;
  }
  char * toString()
  {
    Serial.println("Hello");


    char b[] ="hh:mm:ss";

    char * bs=_dt.toString(b);    
    return bs;
  }

private:
  DateTime _dt;
};

class Base
{
public:
  virtual void init()=0;
  virtual void get()=0;
};

class Extend : public Base
{
public:
  Extend()
  {
    _rtc = RTC_DS3231();
  }
  void init()
  {
    if (!_rtc.begin())
    {
      while (1)
        delay(10);
    }
    if (_rtc.lostPower())
    {
      _rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }
  }

  void  get()
  {
    DateTime dt = _rtc.now();
    DateTimeValue  dtv=DateTimeValue(dt);
    Serial.println(dtv.toString());
  }

private:
  RTC_DS3231 _rtc;
};

Base * base = new Extend();
void setup()
{
  Serial.begin(9600);

  base->init();  
}
void loop()
{
  delay(1000);
  base->get();
}

What is char * bs=_dt.toString(b); ?
(Don't return addresses of local stack variables.)

Your naming is quite strange to me.

  • toString returns a cstring
  • get returns nothing, but prints something
  • IDateTimeValue has no date, time or value

yea, this is after major hatchet job, of troubleshooting and minimizing
IDateTimeValue is meant to server as an interface for a wrapper called DateTimeValue that will hold DateTime from rtv, it's method would be for decreasing and increasing dates, and returning toString in a fix format which is one of the main issue i've been struggling with

all i want is for toString to return a string representation in fixed format hh:mm:ss without needing the caller to always specify the format by themselves.

Seems overly convoluted and complicated to me.

Why not just extend the class (make a new class that inherits from it) and add the functionality? It sounds like you only need one method.

But, having myself just recently done this, I wonder what is so hard about just including the format specifier in your sketch and using it there.

A more general rule with small embedded systems is to avoid dynamic memory allocation due to the very limited amounts of RAM. If you're going to create objects on the heap, do it at program start and keep them around forever.

As written, b is "local" storage, and after it's been overwritten by RTClib::toString() and returned from your toString function, it goes out of scope and "disappears."
Since dynamic allocation is generally frowned upon in microcontrollers, the easiest way out would be the use of a "static" char array for the return value. But I think then the initialization would only happen once.

Perhaps something like:

static const char format[] = "hh:mm:ss";
static char result[sizeof(format)];
  strlcpy(result, format, sizeof(result));
  return _dt.toString(result);

i'm a java developer it's what we do..:wink:

i don't have a choice cause base is purely virtual , otherwise calling a method on Base base =Extend() would actually lead to method being called on the base.

yea it would, that's why it boggles my mind there's no way to return a string in a simple way.

Agreed. You can't return any other kind of array, either.
I guess because neither of those is actually considered a "basic data type" in C/C++.

Unless I've misunderstood something, you can return a String from a function.
What you can't safely do is define a String variable local to a function and then return (or othewise use) a pointer to that String unless that String variable is declared as static.

Simulator: sketch.ino - Wokwi ESP32, STM32, Arduino Simulator

String addTxt( String inPar){
  String prefix = "text:" ;
  return prefix + inPar ;
}



void setup() {
  Serial.begin(115200);
  Serial.println( addTxt( "myMessage" ) ) ;
}

void loop() {
 
}

i thought Strings usage is highly unrecommended

A big debate about this always rages at every opportunity.
My view is that it depends on the platform and how you use it.
Carelessly used, it is true that it can lead to heap fragmentation, especially on a MCU with a small RAM.
There are methods to mitigate this effect, for example reserve().
Just to note; many of the ESP classes use the String data type.

1 Like

Agreed. Context is everything.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.