3

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

Sanmveg saini
  • 744
  • 1
  • 7
  • 22
  • You have two different definitions of `SimpleClass`. This violates the One Definition Rule and your program has undefined behaviour. – molbdnilo Sep 21 '20 at 09:01

1 Answers1

3

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].
Quimby
  • 17,735
  • 4
  • 35
  • 55
  • @Sanmvegsaini What about those two translation units I mentioned? Do they have the same lengths? The bottom line is that you must ensure all these definitions are the same. There is no use in defining the length as redefineable macro, use `const static`, `constexpr` if you can. – Quimby Sep 21 '20 at 09:29
  • 1
    That a great advice! I made some changes and I got the mistake. Thank you. – Sanmveg saini Sep 21 '20 at 09:41