Home > Software design >  C inheritence and array
C inheritence and array

Time:09-24

I have a java program with which I create a program using logic function blocks. AND gates, OR gates that sorta things. This program can generate functional source code for an arduino board. This essentially allows people to program an Arduino with only logic gates.

The core essentials work but I am only halfway there and I run into a slight problem.

I have this struct and array

typedef struct blox
{
    uint8_t  IN1 : 1 ;  // generic for most blocks
    uint8_t  IN2 : 1 ;  // generic for most blocks
    uint8_t  IN3 : 1 ;  // generic for most blocks
    uint8_t    Q : 1 ;  // generic for most blocks

    uint8_t  pin : 5 ; // only inputs and output types need this
    uint8_t type : 4 ; // 16 combinations
    uint32_t        oldTime ;  // bad idea to use this amount of memory per block if only delays need it?
    const uint32_t  interval ; // perhaps couple a function pointers or obj pointer to it?
} FunctionBlock ;

FunctionBlock block [ nBlocks ] ;

In the main loop() I run all logic and I update the links. The links are hardcoded.

void loop()
{
/***************** UPDATE FUNCTION BLOCKS *****************/
    for( int i = 0 ; i < nBlocks ; i    )
    {
        switch( block[i].type )
        {
        case AND: 
            block[i].Q = block[i].IN1 & block[i].IN2 & block[i].IN3 ; // unused inputs INx are initialized to '1'
            break ;

        case OR: 
            block[i].Q = block[i].IN1 | block[i].IN2 | block[i].IN3 ;
            break ;

        case M:
            if(      block[i].IN3 ) block[i].Q = 0 ; // R
            else if( block[i].IN1 ) block[i].Q = 1 ; // S
            break ; 

        case NOT: 
            block[i].Q = !block[i].IN2 ; 
            break ;

        case INPUT_PIN: 
            block[i].Q = digitalRead( block[i].pin ) ;
            break ;

        case OUTPUT_PIN: 
            digitalWrite( block[i].pin, block[i].IN2 ) ;
            break ;

        case DEL: for( int i = 0 ; i < n_blocks  ; i    )
            {
                if( block[i].Q != block[i].IN )                                   // if new state changes
                {
                    if( millis() - block[i].oldTime >= block[i].interval )         // keep monitor if interval has expired
                    {
                        block[i].Q = block[i].IN ;                                // if so, adopt the new state
                    }
                }
                else
                {
                    block[i].oldTime = millis() ;                                      // if new state does not change, keep setting oldTime
                }
            }
            break ;
        }
    }

/***************** UPDATE LINKS *****************/
    block[3].IN2 = block[1].Q ;  // hardcoded list of all links.
    block[3].IN1 = block[0].Q ;
    block[3].IN3 = block[2].Q ;
    block[4].IN2 = block[3].Q ;
} ;

The problem is that the structure has variables for everything. Now AND and OR gates have a 'pin' variable and every block uses 8 bytes for timing, despite only the delay gate has need for it.

I also want to add analog (all that can be more than '0' or '1') components, like an analog input, servo motor, a map() block (to map one range into an other range), comparator contants etc.

Using the struct like this will consume way too much memory.

My best guess would be to use classes and inheritance. But I haven't used inheritance yet in c and I do not know how I can stuff objectes and derived objects in a single array.

class FunctionBlock   // AND, OR, MEMORY or NOT gates
{
public:
    uint8_t  IN1 : 1 ;
    uint8_t  IN2 : 1 ;
    uint8_t  IN3 : 1 ;
    uint8_t    Q : 1 ;
    uint8_t type ;          // if I create derived classes for OR, AND, MEMORY and not gates, I may discard this variable
} ; 

class IO : public FunctionBlock // INPUT, OUTPUT
{
    uint8_t pin ;
} ;

class DELAY : public FunctionBlock
{
    uint32_t  prevTime ;
    const int delayTime ;
} ;

class MAP : public FunctionBlock
{
    int32_t var ;   // result = map( var, in1, in2, out1, out2 ) ;
    int32_t result
    int32_t in1 ;
    int32_t in2 ;
    int32_t out2 ;
    int32_t out2 ;
} ;

// class analogIn, class constant, class comperator, class decadeCounter etc etc

Were this Java I would simply do:

ArrayList <FunctionBlock> blocks = new ArrayList() ;
...
blocks.add( new AND( arguments ) ;

How do I get these derived classes to work in c ?

CodePudding user response:

I think it might be better to define the separate structures without inheritance, and then use a union in the FunctionBlock structure to keep the data.

Perhaps something like this:

struct IO
{
    uint8_t pin;
};

struct DELAY
{
    uint32_t prevTime;
    const int delayTime;
};

// ... the other structures...

struct FunctionBlock
{
    // The current members of the structure...

    union
    {
        IO io;
        DELAY delay;
        // ... and the other structures...
    };
};

Then you can use e.g. block[i].io.pin to get the IO member pin.

Now you can just use the FunctionBlock structure as any other structure, and create a plain normal array.

CodePudding user response:

I chose to go for inheritence as a union would not really solve the memory problem. And performance is in this case not really important. As in it does not matter if this program run 'efficient' or not.

I declare objects using the subclasses followed by a pointer array.

#include "functionBlocks.h"

static Input  b1 = Input(1) ;
static Input  b2 = Input(2) ;
static Input  b3 = Input(3) ;
//static Or     b4 = Or() ;         // if I swap this Or gate for a Delay gate
static Delay  b4 = Delay(2000) ;    // RAM consumes 4 more bytes as anticipated.
static Delay  b5 = Delay( 3000 ) ;
static Output b6 = Output(13) ;

FunctionBlock *block[] =
{
    &b1,
    &b2,
    &b3,
    &b4,
    &b5,
    &b6,
} ;

const int nBlocks = 6 ;

void setup()
{
}

void loop()
{
    block[3] -> IN1 = block[0] -> Q ;
    block[3] -> IN2 = block[1] -> Q ;
    block[3] -> IN3 = block[2] -> Q ;
    block[4] -> IN2 = block[3] -> Q ;
    block[5] -> IN2 = block[4] -> Q ;

/***************** UPDATE FUNCTION BLOCKS *****************/
    for( int i = 0 ; i < nBlocks ; i    ) block[i] -> run() ;
} ;

I tested the memory consumption by swapping out an OR gate for a DELAY and that increases memory usage by 4 bytes. This is correct because the DELAY uses one whole uint32_t variable on top the base class.

So using subclasses like this does resolve the memory problem.

If I now add a single OR gate, RAM only consumes five more bytes. One byte is used for the variables, IN1, IN2, IN3 and Q but I cannot place the other 4 bytes. I am guessing it is a function pointer to the appropiate run()method.

I also moved all the classes to a seperate header file. It looks as follows:

class FunctionBlock
{
public:
    uint8_t  IN1 : 1 ;
    uint8_t  IN2 : 1 ;
    uint8_t  IN3 : 1 ;
    uint8_t    Q : 1 ;

    virtual void run() ;
} ; 

class And : public FunctionBlock
{
public:
    And()
    {
        IN1 = IN2 = IN3 = 1 ;
    }
    void run()
    {
        Q = IN1 & IN2 & IN3 ;
    }
} ;

class Or : public FunctionBlock
{
public:
    Or()
    {
        IN1 = IN2 = IN3 = 0 ;
    }

    void run()
    {
        Q = IN1 | IN2 | IN3 ;
    }
} ;

class Delay : public FunctionBlock
{
public:
    Delay(int x) : delayTime( x )                       // initialize the constant
    {
    }

    void run()
    {
        if( Q != IN2 )                                   // if new state changes
        {
            if( millis() - prevTime >= delayTime )       // keep monitor if interval has expired
            {
                Q = IN2 ;                                // if so, adopt the new state
            }
        }
        else
        {
            prevTime = millis() ;                         // if new state does not change, keep setting oldTime
        }
    }

private:
    const uint32_t delayTime ;
    uint32_t       prevTime ;
} ;
  • Related