Search code examples
c++avr-gccatmelstudio

Why defining buffer length of a buffer leads to that class's function member to loose the value of a function pointer member variable?


I am working with GNU AVR GCC version 5.4.0 and Atmelstudio 7.0.2397 and I have the following problem.


Problem Description

In the following Image, ye can see that up to line 13, the program has stored the address of function usart_send into the transmitter variable which is member of SimpleClass object sc.

enter image description here

The prototype of the transmitter function is void (*transmitter)(uint8_t * txbuff, uint8_t len); and usart_send function prototype definition is void usart_send(uint8_t *, uint8_t);.

Now when I step into the foo function, notice that when inside the member function of the class's object, the address of transmitter is now 0.

enter image description here

Similarly inside the function flush, its value is still 0. So I cannot call the desired function. And this is what disassembly is showing and so is my MCU.

enter image description here


Cause of this problem

So I have the following code for SimpleClass.h

#pragma once

typedef unsigned char uint8_t;

#ifndef rxBufferLen
#define rxBufferLen 30
#endif
#ifndef txBufferLen
#define txBufferLen 30
#endif

class SimpleClass{
    uint8_t rxbuffer[rxBufferLen]; ///< receiver buffer
    uint8_t txBuffer[txBufferLen]; ///< transmitter buffer
    uint8_t rxbuff_index, ///< rxbuffer index. Indicates where to put new data
    txbuff_index; ///< txBuffer index. Indicates where to put new data
    public:
    void (*transmitter)(uint8_t * txbuff, uint8_t len);
    void pushtx(uint8_t byte);
    void pushrx(uint8_t byte);
    void flush();
    void foo();
};

Notice that have the length of txBuffer and rxBuffer defined using define. And in incfile1.h I have the following code.

#pragma once

#define rxBufferLen 50
#define txBufferLen 50

#include <avr/io.h>
#include "simpleClass.h"

#define TIMER0_CLOCK_PRESCALAR  (3)
#define TIMER0_CLOCK_COUNT      (0xff - 50)

void usart_init();
void timer0_init();
void serial_send_ln(const char *);
void usart_send(uint8_t *, uint8_t);
void usart_send_ln(uint32_t num);

In here I have redefined rxBufferLen and txBufferLen. This definition of define causes the above problem. If I remove these two lines, this code is working fine.


Question

So you can see that by defining the buffer length for a buffer which is inside a class, causes its member function to loose the value the class's function pointer (which is a member variable). And I want to know why?


Code

Here are many un used functions and this is because I was isolating bug(if it is a bug!) from my project.

main.cpp

#include "IncFile1.h"

SimpleClass sc;

int main(void)
{
    
    usart_init();
    timer0_init();
    
    sc.transmitter = &usart_send;
    sc.foo();
    
    
    while (1) 
    {
    }
}

IncFile.h

#pragma once

#define rxBufferLen 50
#define txBufferLen 50

#include <avr/io.h>
#include "simpleClass.h"

#define TIMER0_CLOCK_PRESCALAR  (3)
#define TIMER0_CLOCK_COUNT      (0xff - 50)

void usart_init();
void timer0_init();
void serial_send_ln(const char *);
void usart_send(uint8_t *, uint8_t);
void usart_send_ln(uint32_t num);

SimpleClass.h

#pragma once

typedef unsigned char uint8_t;

#ifndef rxBufferLen
#define rxBufferLen 30
#endif
#ifndef txBufferLen
#define txBufferLen 30
#endif

class SimpleClass{
    uint8_t rxbuffer[rxBufferLen]; ///< receiver buffer
    uint8_t txBuffer[txBufferLen]; ///< transmitter buffer
    uint8_t rxbuff_index, ///< rxbuffer index. Indicates where to put new data
    txbuff_index; ///< txBuffer index. Indicates where to put new data
    public:
    void (*transmitter)(uint8_t * txbuff, uint8_t len);
    void pushtx(uint8_t byte);
    void pushrx(uint8_t byte);
    void flush();
    void foo();
};

SimpleClass.cpp

#include "simpleClass.h"

void SimpleClass::flush(){
    transmitter(txBuffer, txbuff_index);
}

void SimpleClass::pushtx(uint8_t byte){
    txBuffer[txbuff_index++] = byte;
}

void SimpleClass::pushrx(uint8_t byte){
    rxbuffer[rxbuff_index++] = byte;
}

void SimpleClass::foo(){
    uint8_t dv = 0;
    dv ++ ;
    pushtx(0x01);
    pushtx(0x02);
    pushtx(0x03);
    pushtx(0x04);
    pushtx(0x05);
    flush();
}

CPPFile1.cpp

#include "IncFile1.h"


    void usart_init(){
        unsigned int ubrr = 51;
        /*Set baud rate */
        UBRR0H = (unsigned char)(ubrr>>8);
        UBRR0L = (unsigned char)ubrr;
        /*Enable receiver and transmitter */
        UCSR0B = (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);
        /* Set frame format: 8data, stop bit */
        UCSR0C = (3<<UCSZ00);
    }
    
    void timer0_init(){
        TCCR0B = TIMER0_CLOCK_PRESCALAR;
        TIMSK0 = 1; // enable timer overflow interrupt
        TCNT0 = TIMER0_CLOCK_COUNT;
    }
    
    void serial_send(const char * c){
        for(uint8_t i=0 ; c[i] != '\0';i++){
            while(!(UCSR0A & (1<<UDRE0)));
            UDR0 = c[i];
        }
        while(!(UCSR0A & (1<<UDRE0)));
        UDR0 = 0x0a;
        while(!(UCSR0A & (1<<UDRE0)));
        UDR0 = 0x0d;
    }
    
    void usart_send(uint8_t *buff, uint8_t len){
        for(uint8_t i = 0; i < len; i++){
            while(!(UCSR0A & (1<<UDRE0)));
            UDR0 = buff[i];
        }
    }
    
    void usart_send_ln(uint32_t num){
        for(uint8_t i =0;i < 4; i++){
            while(!(UCSR0A & (1<<UDRE0)));
            UDR0 = num >> (8*(3-i));
        }
    }

Edits


Solution

  • You are violating the One Definition Rule - linking C++ program where the same class is defined twice and these definitions are not identical is undefined behaviour. The compiler/linker is not required to check or report these errors.

    You are doing just that:

    • CPPFile1.cpp includes IncFile1.h which creates SimpleClass definition with buffers[50],
    • SimpleClass.cpp includes SimpleClass.h with buffers[30].