Arm Mbed OS support forum

Assigning a class member function to class variable

Hello all,
the code below synthesizes 6 PWM signals: each can be offset and frequency modulated, The frequency modulation can be sinusoid, triangular or sawtooth ascending or descending. (target mbed LPC1768)
I’m having trouble assigning the mode class variable, the compiler reports:

Error: A value of type “float (Channel::)()" cannot be assigned to an entity of type "float ()()” in “main.cpp”, Line: 63, Col: 15

syntactically it should be correct, what am I missing? The error occurs in both class constructors.
Any help is appreciated, thank-you.
Giancarlo

#include "mbed.h"
#define SCHUMANN 7.83 // Base frequency
#define CHANNELS 6
#define SAMPLING_RATE 10000.0 // Samples per second

DigitalOut out[CHANNELS] = { LED1, LED2, LED3, LED4, p25, p26 };

Ticker counter;
unsigned int ticks = 0;
void tick()
{
    ++ticks;
}

enum MODE { None, Sine, Triangle, SawAsc, SawDesc };

class Channel
{
    float frequency; // Base frequency (Hz)

    inline float modeNone()
    {
        return 0.0;
    }
    inline float modeSine()
    {
        float df = (float) (ticks % sweep_period) - (float) sweep_period / 2.0;
        return sweep_range * df * (ticks % sweep_period < sweep_period / 2) ? 1.0 : -1.0;
    }
    inline float modeTriangle()
    {
        float df = (float) (ticks % sweep_period) - (float) sweep_period / 2.0;
        return sweep_range * df * (ticks % sweep_period < sweep_period / 2) ? 1.0 : -1.0;
    }
    inline float modeSawAsc()
    {
        return sweep_range * (float) (ticks % sweep_period - sweep_period / 2);
    }
    inline float modeSawDesc()
    {
        return -sweep_range * (float) (ticks % sweep_period - sweep_period / 2);
    }

public:
    bool enabled;
    unsigned int period; // ms
    unsigned int offset; // ms
    unsigned int duty; // ms

    float (*mode)();
    float sweep_range; // Hz
    unsigned int sweep_period; // ms

    Channel()
    {
        enabled = false;

        period = SAMPLING_RATE / SCHUMANN;
        offset = 0;
        duty = period * 0.5; // 50%

        mode = &Channel::modeNone;
        sweep_range = 0.0;
        sweep_period = 0;
    }
    Channel(float Frequency, unsigned short Duty, unsigned short Phase = 0, MODE SweepMode = None, float SweepRange = 0.0, float SweepFrequency = 0.0)
    {
        enabled = true;

        frequency = abs(Frequency);
        period = Frequency == 0 ? (unsigned int) ~0 : SAMPLING_RATE / frequency;
        offset = period * Phase / 100.0;
        duty = period * Duty / 100.0;

        if (SweepMode == Sine)
            mode = &Channel::modeSine;
        else if (SweepMode == Triangle)
            mode = &Channel::modeTriangle;
        else if (SweepMode == SawAsc)
            mode = &Channel::modeSawAsc;
        else if (SweepMode == SawDesc)
            mode = &Channel::modeSawDesc;
        else
            mode = &Channel::modeNone;

        sweep_range = abs(SweepRange) > frequency ? frequency : abs(SweepRange);
        sweep_period = SweepFrequency <= 0 ? (unsigned int) ~0 : SAMPLING_RATE / SweepFrequency;
    }
    bool State()
    {
        if (enabled) {
            float df = mode(); // Determine frequency offset from base frequency
            unsigned int dp = -df / frequency * (frequency + df);

            if ((ticks - offset) % (period + dp) < (duty * frequency / (frequency + df)))
                return true;
        }
        return false;
    }
}

int main()
{
    // Initialize channels
    Channel channels[CHANNELS] = {
        Channel(), //SCHUMANN / 2.0, 10),
        Channel(), //SCHUMANN / 2.0, 10, 50),
        Channel(30.0, 10, 0, Sine, 15.0, 0.01),
        Channel(),
        Channel(),
        Channel()
    };
/*
    for (int i = 0; i < CHANNELS; ++i) {
        printf("Channel %d: freq (Hz) %2.2f, offset (ms): %d, duty: %d%%\n", i+1, SAMPLING_RATE / channels[i].period, channels[i].offset, channels[i].duty);
    }
*/
    counter.attach_us(&tick, (unsigned int) 1000000.0 / SAMPLING_RATE);
    int i = 0;
    while (1) {
        out[i] = channels[i].State();
        i = ++i % CHANNELS;
    }
}

Hello Giancarlo,

That seems to be a useful class!
To assign a class method to a pointer to class member function (aka class method) a new type shall be defined. For example as follows:

typedef  float (Channel::*ChanMembFnc)();

Because the Channel class is not known at that point, it shall be announced prior to defining such type:

class Channel;

The pointer to the Channel class method is then declared inside the Channel class definition as such type:

ChanMembFnc     mode;

A pointer to class method shall be used to call the function the function pointer points to :

float           df = (this->*mode)();

The whole code:

#include "mbed.h"
#define SCHUMANN        7.83    // Base frequency
#define CHANNELS        6
#define SAMPLING_RATE   10000.0 // Samples per second
DigitalOut      out[CHANNELS] = { LED1, LED2, LED3, LED4, p25, p26 };

Ticker          counter;
unsigned int    ticks = 0;

void tick()
{
    ++ticks;
}

enum MODE
{
    None,
    Sine,
    Triangle,
    SawAsc,
    SawDesc
};

class Channel;

typedef  float (Channel::*ChanMembFnc)();

class Channel
{
    float           frequency;              // Base frequency (Hz)
    inline float    modeNone()  { return 0.0; }
    inline float    modeSine()
    {
        float   df = (float)(ticks % sweep_period) - (float)sweep_period / 2.0;
        return sweep_range * df * (ticks % sweep_period < sweep_period / 2) ? 1.0 : -1.0;
    }

    inline float    modeTriangle()
    {
        float   df = (float)(ticks % sweep_period) - (float)sweep_period / 2.0;
        return sweep_range * df * (ticks % sweep_period < sweep_period / 2) ? 1.0 : -1.0;
    }

    inline float    modeSawAsc()    { return sweep_range * (float)(ticks % sweep_period - sweep_period / 2); }
    inline float    modeSawDesc()   { return -sweep_range * (float)(ticks % sweep_period - sweep_period / 2); }
public:
    bool            enabled;
    unsigned int    period;                 // ms
    unsigned int    offset;                 // ms
    unsigned int    duty;                   // ms
    ChanMembFnc     mode;
    float           sweep_range;            // Hz
    unsigned int    sweep_period;           // ms
    Channel()
    {
        enabled = false;

        period = SAMPLING_RATE / SCHUMANN;
        offset = 0;
        duty = period * 0.5;                // 50%
        mode = &Channel::modeNone;
        sweep_range = 0.0;
        sweep_period = 0;
    }

    Channel
    (
        float           Frequency,
        unsigned short  Duty,
        unsigned short  Phase = 0,
        MODE            SweepMode = None,
        float           SweepRange = 0.0,
        float           SweepFrequency = 0.0
    )
    {
        enabled = true;

        frequency = abs(Frequency);
        period = Frequency == 0 ? (unsigned int)~0 : SAMPLING_RATE / frequency;
        offset = period * Phase / 100.0;
        duty = period * Duty / 100.0;

        if (SweepMode == Sine)
            mode = &Channel::modeSine;
        else
        if (SweepMode == Triangle)
            mode = &Channel::modeTriangle;
        else
        if (SweepMode == SawAsc)
            mode = &Channel::modeSawAsc;
        else
        if (SweepMode == SawDesc)
            mode = &Channel::modeSawDesc;
        else
            mode = &Channel::modeNone;

        sweep_range = abs(SweepRange) > frequency ? frequency : abs(SweepRange);
        sweep_period = SweepFrequency <= 0 ? (unsigned int)~0 : SAMPLING_RATE / SweepFrequency;
    }

    bool State()
    {
        if (enabled) {
            float           df = (this->*mode)();    // Determine frequency offset from base frequency
            unsigned int    dp = -df / frequency * (frequency + df);

            if ((ticks - offset) % (period + dp) < (duty * frequency / (frequency + df)))
                return true;
        }

        return false;
    }
};

int main()
{
    // Initialize channels
    Channel channels[CHANNELS] =
    {
        Channel(),  //SCHUMANN / 2.0, 10),
        Channel(),  //SCHUMANN / 2.0, 10, 50),
        Channel(30.0, 10, 0, Sine, 15.0, 0.01),
        Channel(),
        Channel(),
        Channel()
    };
    /*
    for (int i = 0; i < CHANNELS; ++i) {
        printf("Channel %d: freq (Hz) %2.2f, offset (ms): %d, duty: %d%%\n", i+1, SAMPLING_RATE / channels[i].period, channels[i].offset, channels[i].duty);
    }
*/

    counter.attach_us(&tick, (unsigned int)1000000.0 / SAMPLING_RATE);

    int i = 0;
    while (1) {
        out[i] = channels[i].State();
        i = ++i % CHANNELS;
    }
}

Best regards, Zoltan

Impeccable and logical, thank-you Zoltan.
Giancarlo