How to call a class method by a pointer

piet_de_pad
Posts: 1
Joined: Fri Jun 02, 2023 5:30 pm

How to call a class method by a pointer

Postby piet_de_pad » Tue Mar 19, 2024 10:27 pm

I have a question about how to call a class method with a pointer. Below the code of an RTC.
The important parts of the code are these.

Code: Select all

// declaration of a time souce struct with a pointer to method. 
typedef struct tmSource_t {
        char            id[10];
		CLKstate		state;
		CLK_Quality		quality;
		void			(RTC_Clock::*pt2function)(void*);
	} tmSource_t;

Code: Select all

//The configuration of the time souce struct with a pointer to the xxx_sync methods
tmSource_t  TMSource[4] = { {"NTP1",   AVAILABLE, STM1, &RTC_Clock::NTP_sync   }, {"NTP2",  NOTAVAIL, STM4, &RTC_Clock::NTP_sync}, 
							{"DS3231", AVAILABLE, STM4, &RTC_Clock::DS3231_sync}, {"SOFTW", NOTAVAIL, STM4, &RTC_Clock::SOFT_sync} };
tmSource_t  *TMSSelect=&TMSource[0];

Code: Select all

	
//The call to the xxxsync method pointed by a TMSSelect pointer
		case RTC_SYNC: {
			tm.epoch = NTP.NTP.epoch;
			NTP.new_epoch=false;
			TMSSelect->pt2function(dummyptr);
		} break;
The errors I have are related to the line "TMSSelect->pt2function(dummyptr);" I have the following errors:

expression preceding parentheses of apparent call must have (pointer-to-) function typeC/C++(109)
RTC_Clock::tmSource_t *RTC_Clock::TMSSelect

'pt2function' was not declared in this scope
void (RTC_Clock::*RTC_Clock::tmSource_t::pt2function)(void *)

Can someone tell me the correct syntax how I have to call the line "TMSSelect->pt2function(dummyptr);" that points to a xxx_sync method using a pointer.
Thanks Oscar Goos





Code: Select all

#ifndef		RTC_Clock_H
#define		RTC_Clock_H
#define   	RTC_Debug  1      // RTC debug level
#include	<Wire.h>
#include    	<NTP_client.h>
#include  	<stdio_handler.h>
#include  	<Gas_Meter_AS5600.h>


#define		STOP					true
#define		BCD2DEC(bcdval)			(((bcdval>>4)*10) + (bcdval & 0x0F))
#define		DEC2BCD(decval)			(((decval/10)<<4) + (decval%10))
//extern		stdio_handler 	stdio;
//extern		NTP_Client		NTP;
//extern		DS3231          DS3231_RTC;

//class RTC_Clock: public NTP_Client, public DS3231 {
class RTC_Clock: public NTP_Client {
    public:
	enum		command	 	{ RTC_INIT, RTC_SYNC, TMDT2STR, INC_SEC };
	enum		CLKstate 	{ AVAILABLE, NOTAVAIL, OOS, ERRORED };
	enum		CLK_Quality { STM0, STM1, STM2, STM3, STM4 };
	enum  		TSI 		{ TIME, DATE, IP};

    typedef struct RTC_t {
		uint8_t						id;		// Struct identifier for future use
		uint8_t						sec;	// seconds,			00..59
		uint8_t						min;	// minuts,			00..59	
		uint8_t						hr;		// hours,			00..23
		uint8_t						DinW;	// Day in week,		01..07
		uint8_t						DinM;	// Day in month,	01..31
		uint16_t					DinY;	// Day in year		01..366	
		uint8_t						MinY;	// Month in year,	01..12		
		uint16_t					year;	// Year				2000..2100
		uint32_t					SinD;	// seconds in day,	00..86399
		uint8_t						isdst;	// flag telling you dalight saving time.
		uint32_t					epoch;	// epoch time in sec since 1/1/1970
		int16_t						error;	// Time error in usec/sec
		int32_t						tzone;	// Time zone in signed seconds, MX=-6*3600
		int16_t						dst;	// current Daylight Saving Time offset value				
		char						tmstr[9] ="00:00:00";	// buffer for time string
		char						dtstr[11]="00/00/0000";	// buffer for date string
    } RTC_t;

	typedef struct reg {
		uint8_t						sec;	// seconds,			00..59
		uint8_t						min;	// minuts,			00..59	
		uint8_t						hr;		// hours,			00..23
		uint8_t						DinW;	// Day in week,		01..07
		uint8_t						DinM;	// Day in month,	01..31
		uint8_t						MinY;	// Month in year,	01..12		
		uint8_t						YinC;	// Year in century,	00..99 respresenting 2000..2099)	
	}__attribute__((packed, aligned(1))) reg_t;

	typedef struct tmSource_t {
        char            id[10];
		CLKstate		state;
		CLK_Quality		quality;
		void			(RTC_Clock::*pt2function)(void*);
	} tmSource_t;
	
	typedef struct month_t {
		const char*		id;
		uint8_t		daycnt;
	}month_t; 	
	
	typedef struct week_t {
		const char*		id;
		uint8_t		 index;
	};

month_t		month[13]= {{"",0},{"Jan",31},{"Feb",28},{"Mar",31},{"Apr",30},{"May",31},{"Jun",30},{"Jul",31},{"Aug",31},{"Sep",30},{"Oct",31},{"Nov",30},{"Dec",31} };
week_t 	 	week[8]= {{"",0},{"Mo",1},{"Tu",2},{"We",3},{"Th",4},{"Fr",5},{"Sa",6}, {"Su",7}};

				RTC_Clock();
				RTC_t	tm;
	void    	Driver(RTC_Clock::command) ;
	void		UnixTime_to_StandardTime  (uint32_t);
	uint32_t 	StandardTime_to_UnixTime(char*, char*);
	void		epoch2time (uint32_t);
	void 		parce(TSI, char*, void*);
	void		DST_forward();
	void		DST_backward();
	void		DS3231_init();
	void		DS3231_sync(void*);
	void 		NTP_sync(void*);
	void 		SOFT_sync(void*);
				NTP_Client		NTP;

tmSource_t  TMSource[4] = { {"NTP1",   AVAILABLE, STM1, &RTC_Clock::NTP_sync   }, {"NTP2",  NOTAVAIL, STM4, &RTC_Clock::NTP_sync}, 
							{"DS3231", AVAILABLE, STM4, &RTC_Clock::DS3231_sync}, {"SOFTW", NOTAVAIL, STM4, &RTC_Clock::SOFT_sync} };
tmSource_t  *TMSSelect=&TMSource[0];

private:
//	void		str2tmdt(char*tmstr, char*dtstr);
uint8			I2C_adr=0x068;
reg_t			DSreg;
void			*dummyptr;

protected:
};

RTC_Clock::RTC_Clock() {
//	tm.tmsptr = ptr2tmstr;
//	tm.dtsptr = ptr2dtstr;
	tm.epoch=1688011472;					// testing purpose
	//epoch2time(tm.epoch);					// testing purpose
	UnixTime_to_StandardTime(tm.epoch);	// testing purpose
}

void RTC_Clock::Driver(RTC_Clock::command cmd) {

	uint8_t		n;
	String		TDstr;
		
	switch (cmd) {
		case RTC_INIT: {
		for (n=0; n<4; n++) if ((TMSource[n].quality < TMSSelect->quality) && (TMSource[n].state==AVAILABLE )) { TMSSelect = &TMSource[n]; } 
		stdio.printf("Selected Clock:[%u:4], %s,  Status: %u, Quality:%u\n\n",n+1, TMSSelect->id, TMSSelect->state, TMSSelect->quality ); //to be improved
		tm.tzone=-6*3600; 	// temperaly set o mexico
		tm.dst=0;			// Dailight Saving Time mexio=0;
		}break;

		case RTC_SYNC: {
			tm.epoch = NTP.NTP.epoch;
			NTP.new_epoch=false;
			//epoch2time(tm.epoch);
			//if (TMSource[1].state==AVAILABLE) UnixTime_to_StandardTime (tm.epoch);
			//if (TMSource[3].state==AVAILABLE) DS3231_sync();
			TMSSelect->pt2function(dummyptr);
		} break;
		
		case TMDT2STR: {
			sprintf(tm.tmstr,"%02u:%02u:%02u", tm.hr, tm.min, tm.sec);
			sprintf(tm.dtstr,"%02u/%02u/%04u", tm.DinM, tm.MinY, tm.year);	
			//stdio.printf("Time : %s\t Date: %s\n", tm.tmstr, tm.dtstr);
		} break;

		case INC_SEC: {
			tm.epoch++;
			NTP.state_machine();
			if(NTP.new_epoch) Driver(RTC_Clock::RTC_SYNC);
		} break;
	}
}

MicroController
Posts: 1734
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: How to call a class method by a pointer

Postby MicroController » Thu Mar 21, 2024 12:50 pm

A non-static member function of a class can only be called on an instance of that class; i.e. you need an object/instance of type X on which to invoke a member function X::f(). This also applies to invoking member functions (or accessing any non-static member) via pointers.

When you have an instance, the syntax is like

Code: Select all

void callIt(RTC_Clock* instance, void (RTC_Clock::*pt2function)(void*)) {
  (instance->*pt2function)(...); // Call the RTC_Clock:: member function pointed to by 'pt2function' on the object 'instance'
}
You can also see if the functions you want to call could/should be static, which would make them usable without an instance like normal non-member functions.

You could also use polymorphism, i.e. virtual functions and inheritance/overriding ("the ++ way of C programming" ;-)), where the compiler does the member-function-pointer thing for you in the background, like

Code: Select all

// "Abstract"/"pure-virtual" base class for time sources:
class TimeSource {
  public:
  ...
        char            id[10];
	CLKstate		state;
	CLK_Quality		quality;
	
	// "Abstract" method/"pure virtual" member function to be overridden/implemented by child classes:
	virtual void sync() = 0;
};

class NtpTimeSource : public TimeSource {
  public:
  void sync() override {
    // do NTP sync...
  }
};

class DS3231TimeSource : public TimeSource {
  public:
  void sync() override {
    // do DS3231 sync...
  }
};

NtpTimeSource ntpts {...};
DS3231TimeSource dsts {...};
...

TimeSource* timesources[] {&ntpts, &dsts, ...};

void doSync(int i) {
  timesources[i]->sync();
}
Notice that in this case ::sync() won't need a 'void*'-style "context" argument; any state/"context" the sync() method would need would be held (as member attributes) in the instance it is called on.

Who is online

Users browsing this forum: No registered users and 108 guests